Skip to content

Commit

Permalink
fix: issues with HDS login component HP-1867 (#398)
Browse files Browse the repository at this point in the history
* chore: update hds to 3.11

* fix: add language to login / logout parameters

* fix: e2e tests and unit test
  • Loading branch information
Riippi authored Dec 5, 2024
1 parent beda29b commit aa83876
Show file tree
Hide file tree
Showing 13 changed files with 125 additions and 144 deletions.
21 changes: 13 additions & 8 deletions e2e/tests/connected-profiles.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
createProfile,
loginToProfileWithEmail,
checkDownloadedJson,
clickLoginButton,
clickProfileLoginButton,
clickKeycloakLoginButton,
clickServiceConnectionsLink,
} from '../utils/utils';
import {
Expand Down Expand Up @@ -38,10 +39,10 @@ test('1 - No connected accounts', async ({ page }) => {

test.skip('2 - Connect profile to Linked Events', async ({ page }) => {
await page.goto(LINKED_EVENTS_URL);
await clickLoginButton(page);
await clickProfileLoginButton(page);
await page.getByLabel('Sähköposti').fill(mailbox.emailAddress);
await page.getByLabel('Salasana').fill(USER_PASSWORD);
await clickLoginButton(page);
await page.getByLabel('Salasana', { exact: true }).fill(USER_PASSWORD);
await clickProfileLoginButton(page);
await expect(
page.getByText(
'Palvelu Linked Events DEV pyytää lupaa käyttää seuraavia tietoja profiilistasi'
Expand All @@ -56,7 +57,7 @@ test.skip('2 - Connect profile to Linked Events', async ({ page }) => {

// Check that the connected profile is visible
await page.goto(PROFILE_URL);
await clickLoginButton(page);
await clickProfileLoginButton(page);
await page.getByRole('link', { name: 'Helsinki-tunnus' }).click();
const valuesThatShouldBeInJson = ['linkedevents-test', mailbox.emailAddress];
await checkDownloadedJson(page, valuesThatShouldBeInJson);
Expand Down Expand Up @@ -97,11 +98,15 @@ test('3 - Delete profile', async ({ page }) => {
)
).toBeVisible();

// TODO: Remove this when the profile deletion is improved
await page.waitForTimeout(11000);

// Try to login after deleting the profile
await page.goto(PROFILE_URL + '/login');
await clickLoginButton(page);
await clickProfileLoginButton(page);
await page.getByRole('link', { name: 'Helsinki-tunnus' }).click();
await page.getByLabel('Sähköposti').fill(mailbox.emailAddress);
await page.getByLabel('Salasana').fill(USER_PASSWORD);
await clickLoginButton(page);
await page.getByLabel('Salasana', { exact: true }).fill(USER_PASSWORD);
await clickKeycloakLoginButton(page);
await expect(page.getByText('Väärä sähköposti tai salasana')).toBeVisible();
});
4 changes: 2 additions & 2 deletions e2e/tests/create-profile.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { test, expect } from '@playwright/test';

import { clickLoginButton, createProfile } from '../utils/utils';
import { clickProfileLoginButton, createProfile } from '../utils/utils';
import { Mailbox } from '../utils/mailbox';
import { PROFILE_URL } from '../utils/constants';

Expand All @@ -24,7 +24,7 @@ test('Same email address cannot be used for multiple accounts', async ({
const newContext = await browser.newContext();
const newPage = await newContext.newPage();
await newPage.goto(PROFILE_URL);
await clickLoginButton(newPage);
await clickProfileLoginButton(newPage);
await newPage.locator('.login-method-helsinki_tunnus a').click();
await newPage
.locator('a.hds-button:has-text("Luo Helsinki-profiili")')
Expand Down
44 changes: 7 additions & 37 deletions e2e/tests/login-methods.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { test, expect } from '@playwright/test';

import { clickLoginButton, fillSSNAndContinue } from '../utils/utils';
import { fillSSNAndContinue } from '../utils/utils';
import { PROFILE_URL } from '../utils/constants';

const TEST_SSN = '081172-998T';
Expand All @@ -13,58 +13,28 @@ test.beforeEach(async ({ page }) => {

test('Login and logout - Swedish', async ({ page }) => {
await page.getByRole('button', { name: 'Svenska' }).click();
await page.getByRole('button', { name: 'Logga in' }).click();
await page.getByLabel('Logga in').click();
await page.getByRole('link', { name: 'Suomi.fi-identifikation' }).click();
await page.getByRole('link', { name: 'Test IdP' }).click();
await fillSSNAndContinue(page, TEST_SSN);
await page.getByRole('button', { name: 'Fortsätt till tjänsten' }).click();
await page.getByLabel('Profilmeny').click();
await page.getByRole('link', { name: 'Logga ut' }).click();
await page.getByTestId('user-menu-button').click();
await page.getByRole('button', { name: 'Logga ut' }).click();
await expect(
page.getByText('Du har loggats ut från Helsingfors stads e-tjänst')
).toBeVisible();
});

test('Login and logout - English', async ({ page }) => {
await page.getByRole('button', { name: 'English' }).click();
await page.getByRole('button', { name: 'Log in' }).click();
await page.getByLabel('Log in').click();
await page.getByRole('link', { name: 'Suomi.fi e-Identification' }).click();
await page.getByRole('link', { name: 'Test IdP' }).click();
await fillSSNAndContinue(page, TEST_SSN);
await page.getByRole('button', { name: 'Continue to service' }).click();
await page.getByLabel('Profile menu').click();
await page.getByRole('link', { name: 'Sign out' }).click();
await page.getByTestId('user-menu-button').click();
await page.getByRole('button', { name: 'Log out' }).click();
await expect(
page.getByText('You have been logged out of City of Helsinki services')
).toBeVisible();
});

test('Login with YLE account', async ({ page }) => {
const YLE_ACCOUNT_NAME = 'Emeritus Tarmo';
const YLE_ACCOUNT_EMAIL = process.env.YLE_TEST_USER_EMAIL || null;
const YLE_ACCOUNT_PASSWORD = process.env.YLE_TEST_USER_PASSWORD || null;

if (!YLE_ACCOUNT_EMAIL || !YLE_ACCOUNT_PASSWORD) {
test.skip(true, 'YLE account credentials not provided');
return;
}

await clickLoginButton(page);
await page.getByRole('link', { name: 'Yle Tunnus' }).click();
await page.getByLabel('Vain välttämättömät').click();
await page.getByLabel('Sähköposti').fill(YLE_ACCOUNT_EMAIL);
await page.getByLabel('Salasana', { exact: true }).fill(YLE_ACCOUNT_PASSWORD);
await Promise.all([
page.waitForURL(PROFILE_URL),
page.getByLabel('Kirjaudu sisään').click(),
]);
await expect(
page.getByRole('heading', { name: 'Omat tiedot' })
).toBeVisible();
await expect(page.getByTestId('basic-data-firstName-value')).toContainText(
YLE_ACCOUNT_NAME
);
await expect(
page.locator('section').filter({ hasText: 'Tunnistautumistapa' })
).toContainText('Yle');
});
2 changes: 1 addition & 1 deletion e2e/tests/modify-profile-info.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,6 @@ test('Change password', async ({ page }) => {
page.getByRole('button', { name: 'Vaihda salasana' })
).toBeVisible();
await page.getByRole('button', { name: 'Vaihda salasana' }).click();
await expect(page.getByLabel('Profiilivalikko')).toBeVisible();
await expect(page.getByTestId('user-menu-button')).toBeVisible();
await expect(page.getByText(SAVE_SUCCESS)).toBeVisible();
});
1 change: 1 addition & 0 deletions e2e/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const USER_FIRSTNAME = 'Testietunimi';
export const USER_LASTNAME = 'Testisukunimi';
export const USER_PASSWORD = 'Testisalasana123!';
export const PROFILE_NAME = `${USER_FIRSTNAME} ${USER_LASTNAME}`;

export const LINKED_EVENTS_URL =
process.env.LINKED_EVENTS_URL ?? 'https://linkedevents.dev.hel.ninja';
Expand Down
29 changes: 19 additions & 10 deletions e2e/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,25 @@ import {
EXAMPLE_APP_URL,
USER_FIRSTNAME,
USER_LASTNAME,
PROFILE_NAME,
} from './constants';

const LOG_IN_BUTTON = 'Kirjaudu sisään';

export const fillSSNAndContinue = async (page: Page, SSN: string) => {
await page.getByPlaceholder('-9988').fill(SSN);
await page.locator('.box').click();
await page.getByRole('button', { name: 'Tunnistaudu' }).click();
};

export const clickLoginButton = async (page: Page) => {
await page.getByRole('button', { name: 'Kirjaudu sisään' }).click();
// Click login button in profile app page
export const clickProfileLoginButton = async (page: Page) => {
await page.getByLabel(LOG_IN_BUTTON, { exact: true }).click();
};

// Click login button in keycloak
export const clickKeycloakLoginButton = async (page: Page) => {
await page.getByRole('button', { name: LOG_IN_BUTTON }).click();
};

export const clickServiceConnectionsLink = async (page: Page) => {
Expand All @@ -30,20 +39,20 @@ export const clickServiceConnectionsLink = async (page: Page) => {

export const loginToProfileWithEmail = async (page: Page, email: string) => {
await page.goto(PROFILE_URL);
await clickLoginButton(page);
await clickProfileLoginButton(page);
await page.getByRole('link', { name: 'Helsinki-tunnus' }).click();
await page.getByLabel('Sähköposti').fill(email);
await page.getByLabel('Salasana').fill(USER_PASSWORD);
await clickLoginButton(page);
await expect(page.getByLabel('Profiilivalikko')).toBeVisible();
await page.getByLabel('Salasana', { exact: true }).fill(USER_PASSWORD);
await clickKeycloakLoginButton(page);
await expect(page.getByLabel(PROFILE_NAME)).toBeVisible();
await expect(
page.getByTestId('profile-information-explanation')
).toBeVisible();
};

export const loginToProfileWithSuomiFi = async (page: Page, ssn: string) => {
await page.goto(PROFILE_URL);
await clickLoginButton(page);
await clickProfileLoginButton(page);
await page.getByRole('link', { name: 'Suomi.fi-tunnistus' }).click();
await page.getByRole('link', { name: 'Testitunnistaja' }).click();
await fillSSNAndContinue(page, ssn);
Expand All @@ -63,7 +72,7 @@ export const loginToExampleApp = async (page: Page, ssn: string) => {
.click();
await page
.getByRole('banner')
.getByRole('button', { name: 'Kirjaudu sisään' })
.getByRole('button', { name: LOG_IN_BUTTON })
.click();
await page.getByRole('link', { name: 'Suomi.fi identification' }).click();
await page.getByRole('link', { name: 'Test IdP' }).click();
Expand Down Expand Up @@ -91,7 +100,7 @@ export const checkDownloadedJson = async (page: Page, values: string[]) => {

export const createProfile = async (page: Page, mailbox: Mailbox) => {
await page.goto(PROFILE_URL);
await clickLoginButton(page);
await clickProfileLoginButton(page);
await page.locator('.login-method-helsinki_tunnus a').click();
await page.locator('a.hds-button:has-text("Luo Helsinki-profiili")').click();
await page
Expand All @@ -116,5 +125,5 @@ export const createProfile = async (page: Page, mailbox: Mailbox) => {
await page.locator('#hs-register-acknowledgements').click();
await page.locator('#hs-register-age-check').click();
await page.locator('input[value="Luo profiili"]').click();
await expect(page.getByLabel('Profiilivalikko')).toBeVisible();
await expect(page.getByLabel(PROFILE_NAME)).toBeVisible();
};
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@
"formik": "^2.0.4",
"graphql": "^15.8.0",
"graphql-tag": "2.12.6",
"hds-core": "^3.10.1",
"hds-design-tokens": "^3.10.1",
"hds-react": "^3.10.1",
"hds-core": "^3.11.0",
"hds-design-tokens": "^3.11.0",
"hds-react": "^3.11.0",
"history": "^4.9.0",
"http-status-typed": "^1.0.0",
"i18n-iso-countries": "^7.0.0",
Expand Down
49 changes: 28 additions & 21 deletions src/auth/__tests__/useProfile.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';
import { render, waitFor } from '@testing-library/react';
import { User } from 'oidc-client-ts';
import * as hdsReact from 'hds-react';

import {
mockUserCreator,
MockedUserOverrides,
} from '../../common/test/userMocking';
import * as useAuthMock from '../useAuth';
import useProfile, { Profile } from '../useProfile';
import TestLoginProvider from '../../common/test/TestLoginProvider';

Expand All @@ -31,19 +31,20 @@ describe('useProfile', () => {
}: {
callCounter: () => number;
}) => {
const hasLoadStarted = callCounter() > 0;
const { profile, loading, error } = useProfile();
const hasLoadStarted = callCounter() > 0;
const isFinished = hasLoadStarted && loading === false;
let status: Status = loadedStatus;

if (error) {
return <span id={statusIndicatorElementId}>{errorStatus}</span>;
}
if (!isFinished) {
return <span id={statusIndicatorElementId}>{loadingStatus}</span>;
status = errorStatus;
} else if (!isFinished) {
status = loadingStatus;
}

return (
<div>
<span id={statusIndicatorElementId}>{loadedStatus}</span>
<span id={statusIndicatorElementId}>{status}</span>
<span id={profileElementId}>
{JSON.stringify(profile ? profile : noProfile)}
</span>
Expand All @@ -56,18 +57,25 @@ describe('useProfile', () => {
error = false
): DataGetters => {
const userData = mockUserCreator(overrides);
const mockedGetUser = vi.fn();

vi.spyOn(useAuthMock, 'default').mockImplementation(() => ({
const mockedGetUser = vi
.fn()
.mockImplementation(() => (error ? null : userData));

vi.spyOn(hdsReact, 'useOidcClient').mockReturnValue({
getUser: mockedGetUser,
getAmr: vi.fn(),
getState: vi.fn(),
getToken: vi.fn(),
getUserManager: vi.fn(),
handleCallback: vi.fn(),
isAuthenticated: vi.fn(),
getUser: error
? mockedGetUser.mockRejectedValue(null)
: mockedGetUser.mockResolvedValue(userData),
endLogin: vi.fn(),
isRenewing: vi.fn(),
login: vi.fn(),
logout: vi.fn(),
changePassword: vi.fn(),
}));
renewUser: vi.fn(),
connect: vi.fn(),
namespace: '',
});

const result = render(
<TestLoginProvider>
Expand Down Expand Up @@ -119,13 +127,12 @@ describe('useProfile', () => {
});

it('should provide no profile if it has expired', async () => {
const { getInfo, getProfile } = renderTestComponent({
const { getProfile } = renderTestComponent({
userOverrides: ({
expired: true,
} as unknown) as Partial<User>,
});
await waitFor(() => expect(getInfo()).toEqual(loadedStatus));
expect(getProfile()).toEqual(noProfile);
await waitFor(() => expect(getProfile()).toEqual(noProfile));
});

it('should provide no profile if user.expired is undefined', async () => {
Expand All @@ -134,8 +141,8 @@ describe('useProfile', () => {
expired: undefined,
} as unknown) as Partial<User>,
});
await waitFor(() => expect(getInfo()).toEqual(loadedStatus));
expect(getProfile()).toEqual(noProfile);
await waitFor(() => getInfo());
await waitFor(() => expect(getProfile()).toEqual(noProfile));
});

it('should return an empty array if arm is undefined', async () => {
Expand Down
4 changes: 3 additions & 1 deletion src/auth/components/login/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import commonContentStyles from '../../../common/cssHelpers/content.module.css';
import FocusableH1 from '../../../common/focusableH1/FocusableH1';

function Login(): React.ReactElement {
const { t } = useTranslation();
const { t, i18n } = useTranslation();
const lang = i18n.resolvedLanguage;

return (
<PageLayout title={'login.login'}>
Expand All @@ -27,6 +28,7 @@ function Login(): React.ReactElement {
<LoginButton
errorText={t('authentication.genericError.message')}
loggingInText={t('nav.loggingIn')}
redirectionProps={{ language: lang }}
>
{t('login.login')}
</LoginButton>
Expand Down
Loading

0 comments on commit aa83876

Please sign in to comment.