From ad6cd090bf976d4253094ec7fa9f3e782a22bec5 Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Mon, 25 Nov 2024 20:49:28 +0100 Subject: [PATCH 01/15] added support for tt02 and scheduled tests for Playwright --- .github/workflows/scheduled-playwright.yml | 19 +++++++++++++++++++ frontend/playwright/package.json | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/scheduled-playwright.yml diff --git a/.github/workflows/scheduled-playwright.yml b/.github/workflows/scheduled-playwright.yml new file mode 100644 index 00000000..9a8b4a82 --- /dev/null +++ b/.github/workflows/scheduled-playwright.yml @@ -0,0 +1,19 @@ +name: Scheduled Playwright Tests + +on: + schedule: + # Runs at 7:00 AM UTC every day + - cron: "0 7 * * *" + # Runs at 1:00 PM UTC every day + - cron: "0 13 * * *" + +jobs: + playwright-scheduled: + name: "Scheduled Playwright tests" + strategy: + fail-fast: false + matrix: + environment: [TT02] + uses: "./.github/workflows/template-test-playwright.yml" + with: + environment: ${{ matrix.environment }} diff --git a/frontend/playwright/package.json b/frontend/playwright/package.json index ad1b428d..59e7d0ba 100644 --- a/frontend/playwright/package.json +++ b/frontend/playwright/package.json @@ -8,7 +8,8 @@ "env:AT21": "cross-env environment=at21 npx playwright test", "env:AT22": "cross-env environment=at22 npx playwright test", "env:AT23": "cross-env environment=at23 npx playwright test", - "env:AT24": "cross-env environment=at24 npx playwright test" + "env:AT24": "cross-env environment=at24 npx playwright test", + "env:TT02": "cross-env environment=tt02 npx playwright test" }, "keywords": [], "author": "", From e45e6ded93ffd52b731d08afffdd8a2cb66127a6 Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Wed, 27 Nov 2024 10:15:16 +0100 Subject: [PATCH 02/15] first testscenario done for rejecting system user request --- .gitignore | 2 + .../playwright/api-requests/ApiRequests.tsx | 103 ++++++++++++++++++ frontend/playwright/api-requests/Testuser.tsx | 24 ++++ frontend/playwright/api-requests/Token.tsx | 82 ++++++++++++++ frontend/playwright/config/.env.at22 | 9 +- frontend/playwright/config/.env.at23 | 9 +- frontend/playwright/config/.env.at24 | 9 +- frontend/playwright/config/.env.tt02 | 9 +- .../e2eTests/approveSystemUserRequest.spec.ts | 38 +++++++ frontend/playwright/playwright.config.ts | 5 + 10 files changed, 286 insertions(+), 4 deletions(-) create mode 100644 frontend/playwright/api-requests/ApiRequests.tsx create mode 100644 frontend/playwright/api-requests/Testuser.tsx create mode 100644 frontend/playwright/api-requests/Token.tsx create mode 100644 frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts diff --git a/.gitignore b/.gitignore index c86f156b..b5a45b13 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ /frontend/playwright/blob-report/ /frontend/playwright/.cache/ /frontend/playwright/.playwright/ +/frontend/playwright/config/.env + # From access management repo: # Yarn stuff; we're not using PnP/Zero installs diff --git a/frontend/playwright/api-requests/ApiRequests.tsx b/frontend/playwright/api-requests/ApiRequests.tsx new file mode 100644 index 00000000..dc9777a3 --- /dev/null +++ b/frontend/playwright/api-requests/ApiRequests.tsx @@ -0,0 +1,103 @@ +interface PostSystemUserRequestPayload { + systemId: string; + partyOrgNo: string; + externalRef: string; + rights: Array<{ + resource: Array<{ + id: string; + value: string; + }>; + }>; + redirectUrl: string; +} + + +export class ApiRequests { + + public async sendPostRequest(payload: PostSystemUserRequestPayload, endpoint: string, token: string): Promise { + var url = `${process.env.API_BASE_URL}${endpoint}` + + try { + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}`, // Add the Authorization header + }, + body: JSON.stringify(payload), + }); + + if (!response.ok) { + const errorBody = await response.text(); // Read the response body + console.error(`HTTP Error! Status: ${response.status}, Response Body: ${errorBody}`); + throw new Error(`HTTP error! Status: ${response.status}, Response Body: ${errorBody}`); + } + const data = await response.json(); + return data; // Return the response data + } catch (error) { + console.error('Error:', error); + throw error; // Rethrow the error to handle it in the test + } + } + + /** + * Sends a GET request to fetch the last system request. + * @param endpoint The API endpoint (relative path). + * @param token The authorization token. + * @returns The response data as JSON. + */ +public async fetchLastSystemRequest(endpoint: string, token: string): Promise { + const url = `${process.env.API_BASE_URL}${endpoint}`; + + try { + const response = await fetch(url, { + method: 'GET', + headers: { + 'Authorization': `Bearer ${token}`, // Add the Authorization header + }, + }); + + if (!response.ok) { + const errorBody = await response.text(); // Read the response body + console.error(`HTTP Error! Status: ${response.status}, Response Body: ${errorBody}`); + throw new Error(`HTTP error! Status: ${response.status}, Response Body: ${errorBody}`); + } + + const data = await response.json(); + return data; // Return the response data + } catch (error) { + console.error('Error:', error); + throw error; // Rethrow the error to handle it in the test + } +} + + generatePayloadSystemUserRequest(): PostSystemUserRequestPayload { + var randomString = Date.now(); // Current timestamp in milliseconds + const randomNum = Math.random().toString(36); + return { + systemId: `${process.env.SYSTEM_ID}`, + partyOrgNo: `${process.env.ORG}`, + externalRef: `${randomNum}${randomString}`, + rights: [ + { + resource: [ + { + value: "authentication-e2e-test", + id: "urn:altinn:resource" + } + ] + }, + { + resource: [ + { + value: "vegardtestressurs", + id: "urn:altinn:resource" + } + ] + } + ], + redirectUrl: "https://altinn.no" + }; + } + +} \ No newline at end of file diff --git a/frontend/playwright/api-requests/Testuser.tsx b/frontend/playwright/api-requests/Testuser.tsx new file mode 100644 index 00000000..f010ff6b --- /dev/null +++ b/frontend/playwright/api-requests/Testuser.tsx @@ -0,0 +1,24 @@ +import path from "path"; + +export class Testuser { + scopes: string; + pid: string; + userId: string; + altinnPartyId: string; + + constructor(scopes: string, pid: string, userId: string, altinnPartyId: string) { + this.scopes = scopes; + this.pid = pid; + this.userId = userId; + this.altinnPartyId = altinnPartyId; + } + + loadTestUsers(environment: string): Testuser[] { + const filePath = path.resolve(__dirname, `testusers.${environment}.json`); + const data = require('fs').readFileSync(filePath, 'utf-8'); + const users = JSON.parse(data) as Testuser[]; // Assuming the JSON structure matches Testuser + return users; + } + + +} \ No newline at end of file diff --git a/frontend/playwright/api-requests/Token.tsx b/frontend/playwright/api-requests/Token.tsx new file mode 100644 index 00000000..e8931921 --- /dev/null +++ b/frontend/playwright/api-requests/Token.tsx @@ -0,0 +1,82 @@ +import * as dotenv from 'dotenv'; + +export class Token { + + + /** + * Fetches an enterprise Altinn token for a specific organization and environment. + * @param scopes Scopes required for the token. + * @returns The enterprise Altinn token as a string. + */ +public async getEnterpriseAltinnToken(scopes: string): Promise { + const username = process.env.token_api_username; + const password = process.env.token_api_password; + + if (!username || !password) { + throw new Error('API username or password is not defined in the environment variables.'); + } + + // Construct the URL for fetching the enterprise Altinn test token + const url = `https://altinn-testtools-token-generator.azurewebsites.net/api/GetEnterpriseToken` + + `?orgNo=${process.env.ORG}&env=${process.env.environment}&scopes=${scopes}`; + + // Basic authentication header + const auth = Buffer.from(`${username}:${password}`).toString('base64'); + const headers = { + 'Authorization': `Basic ${auth}`, + }; + + // Retrieve the token + const token = await this.getAltinnToken(url, headers); + if (!token) { + throw new Error("Token retrieval failed for Enterprise Altinn token"); + } + + return token; +} + + + + /** + * Used for fetching an Altinn test token for a specific role + * @param user User read from test config (testusers.at.json) + * @returns The Altinn test token as a string + */ + public async getPersonalAltinnToken(scopes: string): Promise { + const username = process.env.token_api_username; + const password = process.env.token_api_password; + if (!username || !password) { + throw new Error('API username or password is not defined in the environment variables.'); + } + // Construct the URL for fetching the Altinn test token + const url = `https://altinn-testtools-token-generator.azurewebsites.net/api/GetPersonalToken?env=${process.env.environment}` + + `&scopes=${scopes}` + + `&pid=${process.env.PID}` + + `&userid=${process.env.ALTINN_USER_ID}` + + `&partyid=${process.env.ALTINN_PARTY_ID}` + + `&authLvl=3&ttl=3000`; + + console.log(url) + + // Retrieve the token + const auth = Buffer.from(`${username}:${password}`).toString('base64'); + const headers = { + 'Authorization': `Basic ${auth}`, + }; + + const token = await this.getAltinnToken(url, headers); + if (!token) { + throw new Error("Token retrieval failed for Altinn token"); + } + return token; + } + +private async getAltinnToken(url: string, headers: Record): Promise { + const response = await fetch(url, { headers }); + if (!response.ok) { + const errorMessage = await response.text(); // Fetch the full error message from the response + throw new Error(`Failed to fetch token: ${response.statusText} - ${errorMessage}`); + } + return response.text(); + } +} \ No newline at end of file diff --git a/frontend/playwright/config/.env.at22 b/frontend/playwright/config/.env.at22 index 07bf4a63..992976ed 100644 --- a/frontend/playwright/config/.env.at22 +++ b/frontend/playwright/config/.env.at22 @@ -1,3 +1,10 @@ ENV_NAME="at22" BASE_URL="https://at22.altinn.cloud" -SYSYEMUSER_URL="https://authn.ui.at22.altinn.cloud/authfront/ui/auth/creation" \ No newline at end of file +SYSYEMUSER_URL="https://authn.ui.at22.altinn.cloud/authfront/ui/auth/creation" +API_BASE_URL="https://platform.at22.altinn.cloud/authentication/api/" + +ALTINN_PARTY_ID="51359757" +ALTINN_USER_ID="20010849" +PID="14824497789" +ORG="310547891" +SYSTEM_ID="310547891_E2E - Playwright - Authentication" \ No newline at end of file diff --git a/frontend/playwright/config/.env.at23 b/frontend/playwright/config/.env.at23 index 1c031a00..1e6cc762 100644 --- a/frontend/playwright/config/.env.at23 +++ b/frontend/playwright/config/.env.at23 @@ -1,3 +1,10 @@ ENV_NAME="at23" BASE_URL="https://at23.altinn.cloud" -SYSYEMUSER_URL="https://authn.ui.at23.altinn.cloud/authfront/ui/auth/creation" \ No newline at end of file +SYSYEMUSER_URL="https://authn.ui.at23.altinn.cloud/authfront/ui/auth/creation" +API_BASE_URL="https://platform.at23.altinn.cloud/authentication/api/" + +ALTINN_PARTY_ID="51359757" +ALTINN_USER_ID="20010849" +PID="14824497789" +ORG="310547891" +SYSTEM_ID="310547891_E2E - Playwright - Authentication" \ No newline at end of file diff --git a/frontend/playwright/config/.env.at24 b/frontend/playwright/config/.env.at24 index 1243fb99..11ea32a6 100644 --- a/frontend/playwright/config/.env.at24 +++ b/frontend/playwright/config/.env.at24 @@ -1,3 +1,10 @@ ENV_NAME="at24" BASE_URL="https://at24.altinn.cloud" -SYSYEMUSER_URL="https://authn.ui.at24.altinn.cloud/authfront/ui/auth/creation" \ No newline at end of file +SYSYEMUSER_URL="https://authn.ui.at24.altinn.cloud/authfront/ui/auth/creation" +API_BASE_URL="https://platform.at24.altinn.cloud/authentication/api/" + +ALTINN_PARTY_ID="51359757" +ALTINN_USER_ID="20010849" +PID="14824497789" +ORG="310547891" +SYSTEM_ID="310547891_E2E - Playwright - Authentication" \ No newline at end of file diff --git a/frontend/playwright/config/.env.tt02 b/frontend/playwright/config/.env.tt02 index df30e2f6..ae3ba4e9 100644 --- a/frontend/playwright/config/.env.tt02 +++ b/frontend/playwright/config/.env.tt02 @@ -1,3 +1,10 @@ ENV_NAME="tt02" BASE_URL="https://tt02.altinn.no" -SYSYEMUSER_URL="https://authn.ui.tt02.altinn.no/authfront/ui/auth/creation" \ No newline at end of file +SYSYEMUSER_URL="https://authn.ui.tt02.altinn.no/authfront/ui/auth/creation" +API_BASE_URL="https://platform.tt02.altinn.no/authentication/api/" + +ALTINN_PARTY_ID="51359757" +ALTINN_USER_ID="20010849" +PID="14824497789" +ORG="310547891" +SYSTEM_ID="310547891_E2E - Playwright - Authentication" \ No newline at end of file diff --git a/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts new file mode 100644 index 00000000..326dc71f --- /dev/null +++ b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts @@ -0,0 +1,38 @@ +//Dummy URL to test with: https://authn.ui.at22.altinn.cloud/authfront/ui/auth/vendorrequest?id=fa82738b-d948-48d7-b58f-b2709dccec55 +import test, { expect } from '@playwright/test'; +import { ApiRequests } from '../api-requests/ApiRequests'; +import { Token } from 'playwright/api-requests/Token'; + +async function prepareSystemUserRequest(api: ApiRequests, tokenclass: Token) { + const payload = api.generatePayloadSystemUserRequest(); + const scopes = + 'altinn:authentication/systemuser.request.read altinn:authentication/systemuser.request.write'; + const token = await tokenclass.getEnterpriseAltinnToken(scopes); + const endpoint = 'v1/systemuser/request/vendor'; + const apiResponse = await api.sendPostRequest(payload, endpoint, token); + return apiResponse.confirmUrl; // Return the URL to use in the test +} + +// Removed the import statement for ApiRequests due to the error indicating it's not a module +test('Avvis Systembrukerforespørsel', async ({ page }): Promise => { + const api = new ApiRequests(); // Create an instance + const tokenclass = new Token(); + + const payload = api.generatePayloadSystemUserRequest(); + var scopes = + 'altinn:authentication/systemuser.request.read altinn:authentication/systemuser.request.write'; + + var token = await tokenclass.getEnterpriseAltinnToken(scopes); + + const endpoint = 'v1/systemuser/request/vendor'; + const apiResponse = await api.sendPostRequest(payload, endpoint, token); + + await page.goto(apiResponse.confirmUrl); + await page.getByRole('button', { name: 'Avvis' }).click(); + const logoutButton = page.locator('span.sr-only', { hasText: 'Logg inn/Min profil' }); + await expect(logoutButton).toBeVisible(); +}); + +//Lag ny test: https://authn.ui.at22.altinn.cloud/authfront/ui/auth/creation - Lag ny systemtilgang + +//https://platform.at22.altinn.cloud/authentication/api/v1/systemregister/vendor diff --git a/frontend/playwright/playwright.config.ts b/frontend/playwright/playwright.config.ts index 3932218a..d939306e 100644 --- a/frontend/playwright/playwright.config.ts +++ b/frontend/playwright/playwright.config.ts @@ -11,6 +11,11 @@ dotenv.config({ path: path.resolve(__dirname, `config/.env.${process.env.environment ?? 'AT22'}`), }); +// Load sensitive credentials from shared `.env` file +dotenv.config({ + path: path.resolve(__dirname, 'config/.env'), +}); + /** * See https://playwright.dev/docs/test-configuration. */ From d869c36f4be1c6d2c0571a9ed7a0ad718331c98a Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Wed, 27 Nov 2024 15:28:25 +0100 Subject: [PATCH 03/15] added e2e test for creating system user - todo: delete and cancel tests --- .../playwright/api-requests/ApiRequests.tsx | 63 +++++++++++++++++++ frontend/playwright/api-requests/Testuser.tsx | 24 ------- frontend/playwright/api-requests/Token.tsx | 11 +--- .../e2eTests/approveSystemUserRequest.spec.ts | 50 ++++++++------- .../e2eTests/selectSystemVendor.spec.ts | 20 +++++- frontend/playwright/pages/systemUserPage.ts | 32 +++++++--- frontend/playwright/util/TestdataApi.tsx | 25 ++++++++ 7 files changed, 160 insertions(+), 65 deletions(-) delete mode 100644 frontend/playwright/api-requests/Testuser.tsx create mode 100644 frontend/playwright/util/TestdataApi.tsx diff --git a/frontend/playwright/api-requests/ApiRequests.tsx b/frontend/playwright/api-requests/ApiRequests.tsx index dc9777a3..2c48d845 100644 --- a/frontend/playwright/api-requests/ApiRequests.tsx +++ b/frontend/playwright/api-requests/ApiRequests.tsx @@ -14,6 +14,69 @@ interface PostSystemUserRequestPayload { export class ApiRequests { + + public async cleanUpSystemUsers(systemUsers: Array<{ id: string }>, token: string): Promise { + for (const systemuser of systemUsers) { + await this.deleteSystemUser(token, systemuser.id) + } +} + + //Todo - consider moving to class TestdataApi + public async getSystemUsers(token: string): Promise { + var endpoint = `v1/systemuser/${process.env.ALTINN_PARTY_ID}`; + var url = `${process.env.API_BASE_URL}${endpoint}`; + + try { + const response = await fetch(url, { + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, // Use the token for authentication + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + const errorText = await response.text(); // Optional: Read the error body + console.error('Failed to fetch system users:', response.status, errorText); + throw new Error(`Failed to fetch system users: ${response.statusText}`); + } + + const data = await response.json(); // Assuming the API returns JSON data + return JSON.stringify(data, null, 2); // Format the JSON for better readability (optional) + } catch (error) { + console.error('Error fetching system users:', error); + throw new Error('Error fetching system users. Check logs for details.'); + } + + } + + public async deleteSystemUser(token: string, systemUserId: string): Promise{ + var endpoint = `v1/systemuser/${process.env.ALTINN_PARTY_ID}/${systemUserId}` + var url = `${process.env.API_BASE_URL}${endpoint}` + + try { + const response = await fetch(url, { + method: 'DELETE', + headers: { + Authorization: `Bearer ${token}`, // Replace with your token logic + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + const errorBody = await response.text(); // Read the error body if needed + console.error('Failed to delete system user:', response.status, errorBody); + throw new Error(`Failed to delete system user: ${response.statusText}`); + } + + console.log(`System user deleted successfully: ${response.status}`); + } catch (error) { + console.error('Error during system user deletion:', error); + throw new Error('System user deletion failed. Check logs for details.'); + } + } + + public async sendPostRequest(payload: PostSystemUserRequestPayload, endpoint: string, token: string): Promise { var url = `${process.env.API_BASE_URL}${endpoint}` diff --git a/frontend/playwright/api-requests/Testuser.tsx b/frontend/playwright/api-requests/Testuser.tsx deleted file mode 100644 index f010ff6b..00000000 --- a/frontend/playwright/api-requests/Testuser.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import path from "path"; - -export class Testuser { - scopes: string; - pid: string; - userId: string; - altinnPartyId: string; - - constructor(scopes: string, pid: string, userId: string, altinnPartyId: string) { - this.scopes = scopes; - this.pid = pid; - this.userId = userId; - this.altinnPartyId = altinnPartyId; - } - - loadTestUsers(environment: string): Testuser[] { - const filePath = path.resolve(__dirname, `testusers.${environment}.json`); - const data = require('fs').readFileSync(filePath, 'utf-8'); - const users = JSON.parse(data) as Testuser[]; // Assuming the JSON structure matches Testuser - return users; - } - - -} \ No newline at end of file diff --git a/frontend/playwright/api-requests/Token.tsx b/frontend/playwright/api-requests/Token.tsx index e8931921..ed65923a 100644 --- a/frontend/playwright/api-requests/Token.tsx +++ b/frontend/playwright/api-requests/Token.tsx @@ -35,14 +35,11 @@ public async getEnterpriseAltinnToken(scopes: string): Promise { return token; } - - /** * Used for fetching an Altinn test token for a specific role - * @param user User read from test config (testusers.at.json) * @returns The Altinn test token as a string */ - public async getPersonalAltinnToken(scopes: string): Promise { + public async getPersonalAltinnToken(scopes: string = ''): Promise { const username = process.env.token_api_username; const password = process.env.token_api_password; if (!username || !password) { @@ -50,13 +47,11 @@ public async getEnterpriseAltinnToken(scopes: string): Promise { } // Construct the URL for fetching the Altinn test token const url = `https://altinn-testtools-token-generator.azurewebsites.net/api/GetPersonalToken?env=${process.env.environment}` + - `&scopes=${scopes}` + `&pid=${process.env.PID}` + `&userid=${process.env.ALTINN_USER_ID}` + `&partyid=${process.env.ALTINN_PARTY_ID}` + - `&authLvl=3&ttl=3000`; - - console.log(url) + `&authLvl=3&ttl=3000` + + (scopes ? `&scopes=${scopes}` : ''); // Retrieve the token const auth = Buffer.from(`${username}:${password}`).toString('base64'); diff --git a/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts index 326dc71f..96517a63 100644 --- a/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts +++ b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts @@ -2,37 +2,43 @@ import test, { expect } from '@playwright/test'; import { ApiRequests } from '../api-requests/ApiRequests'; import { Token } from 'playwright/api-requests/Token'; +import { TestdataApi } from 'playwright/util/TestdataApi'; -async function prepareSystemUserRequest(api: ApiRequests, tokenclass: Token) { - const payload = api.generatePayloadSystemUserRequest(); - const scopes = - 'altinn:authentication/systemuser.request.read altinn:authentication/systemuser.request.write'; - const token = await tokenclass.getEnterpriseAltinnToken(scopes); - const endpoint = 'v1/systemuser/request/vendor'; - const apiResponse = await api.sendPostRequest(payload, endpoint, token); - return apiResponse.confirmUrl; // Return the URL to use in the test -} - -// Removed the import statement for ApiRequests due to the error indicating it's not a module test('Avvis Systembrukerforespørsel', async ({ page }): Promise => { const api = new ApiRequests(); // Create an instance const tokenclass = new Token(); + var confirmUrl = await prepareSystemUserRequest(api, tokenclass); - const payload = api.generatePayloadSystemUserRequest(); - var scopes = - 'altinn:authentication/systemuser.request.read altinn:authentication/systemuser.request.write'; - - var token = await tokenclass.getEnterpriseAltinnToken(scopes); + await page.goto(confirmUrl); + await page.getByRole('button', { name: 'Avvis' }).click(); + const logoutButton = page.locator('span.sr-only', { hasText: 'Logg inn/Min profil' }); + await expect(logoutButton).toBeVisible(); +}); - const endpoint = 'v1/systemuser/request/vendor'; - const apiResponse = await api.sendPostRequest(payload, endpoint, token); +test('Godkjenn Systembrukerforespørsel', async ({ page }): Promise => { + const api = new ApiRequests(); // Create an instance + const tokenclass = new Token(); + var confirmUrl = await prepareSystemUserRequest(api, tokenclass); - await page.goto(apiResponse.confirmUrl); - await page.getByRole('button', { name: 'Avvis' }).click(); + await page.goto(confirmUrl); + await page.getByRole('button', { name: 'Godkjenn' }).click(); const logoutButton = page.locator('span.sr-only', { hasText: 'Logg inn/Min profil' }); await expect(logoutButton).toBeVisible(); }); -//Lag ny test: https://authn.ui.at22.altinn.cloud/authfront/ui/auth/creation - Lag ny systemtilgang +//Clean up test users +test.afterAll(async () => { + await TestdataApi.cleanUpTestUsers(); +}); + +async function prepareSystemUserRequest(api: ApiRequests, tokenclass: Token) { + const payload = api.generatePayloadSystemUserRequest(); + const scopes = + 'altinn:authentication/systemuser.request.read altinn:authentication/systemuser.request.write'; + const token = await tokenclass.getEnterpriseAltinnToken(scopes); + const endpoint = 'v1/systemuser/request/vendor'; + const apiResponse = await api.sendPostRequest(payload, endpoint, token); + return apiResponse.confirmUrl; // Return the Confirmation URL to use in the test +} -//https://platform.at22.altinn.cloud/authentication/api/v1/systemregister/vendor +//Lag ny test: https://authn.ui.at22.altinn.cloud/authfront/ui/auth/creation - Lag ny systemtilgang diff --git a/frontend/playwright/e2eTests/selectSystemVendor.spec.ts b/frontend/playwright/e2eTests/selectSystemVendor.spec.ts index ae56d616..e115eee8 100644 --- a/frontend/playwright/e2eTests/selectSystemVendor.spec.ts +++ b/frontend/playwright/e2eTests/selectSystemVendor.spec.ts @@ -1,7 +1,23 @@ -import { test } from '@playwright/test'; +import { expect, test } from '@playwright/test'; import { SystemUserPage } from '../pages/systemUserPage'; +import { TestdataApi } from 'playwright/util/TestdataApi'; test('should be able to select a system vendor', async ({ page }): Promise => { const systemUserPage = new SystemUserPage(page); - await systemUserPage.selectSystemVendor('Tripletex'); + await systemUserPage.selectSystemVendor('TripletexN/A (914286018)'); +}); + +test('Create system user and verify landing page', async ({ page }): Promise => { + const systemUserPage = new SystemUserPage(page); + await systemUserPage.selectSystemVendor('TripletexN/A (914286018)'); + await systemUserPage.CREATE_ACCESS_BUTTON.click(); + await expect(systemUserPage.SYSTEMUSER_CREATED_HEADING).toBeVisible(); + await expect(page.getByText('Tripletex').first()).toBeVisible(); +}); + +//Tdo - lag test for å slette og avbryte + +//Clean up test users +test.afterAll(async () => { + await TestdataApi.cleanUpTestUsers(); }); diff --git a/frontend/playwright/pages/systemUserPage.ts b/frontend/playwright/pages/systemUserPage.ts index 9dd681cb..48d14a3b 100644 --- a/frontend/playwright/pages/systemUserPage.ts +++ b/frontend/playwright/pages/systemUserPage.ts @@ -1,17 +1,31 @@ -import { type Page, expect } from '@playwright/test'; +import { Locator, type Page, expect } from '@playwright/test'; +import noNb from '../../src/localizations/no_nb.json'; export class SystemUserPage { - constructor(public page: Page) {} + public readonly SELECT_VENDOR_LABEL: Locator; + public readonly CONTINUE_BUTTON: Locator; + public readonly CREATE_ACCESS_BUTTON: Locator; + public readonly SYSTEMUSER_CREATED_HEADING: Locator; + + constructor(public page: Page) { + this.SELECT_VENDOR_LABEL = this.page.getByLabel(noNb.authent_creationpage.pull_down_menu_label); + this.CONTINUE_BUTTON = this.page.getByRole('button', { + name: noNb.authent_creationpage.confirm_button, + }); + this.CREATE_ACCESS_BUTTON = this.page.getByRole('button', { + name: noNb.authent_includedrightspage.confirm_button, + }); + this.SYSTEMUSER_CREATED_HEADING = this.page.getByRole('heading', { + name: noNb.authent_overviewpage.created_system_user_title, + }); + } async selectSystemVendor(vendorSystem: string) { await this.page.goto(`${process.env.SYSYEMUSER_URL}`); - await this.page.getByLabel('Velg fagsystem').fill(vendorSystem); - await this.page.getByRole('option', { name: vendorSystem }).click(); - await this.page.getByRole('button', { name: 'Gå videre' }).click(); + await this.SELECT_VENDOR_LABEL.fill(vendorSystem.substring(0, 9)); + await this.page.getByLabel(vendorSystem, { exact: true }).click(); - const createButton = this.page.getByRole('button', { - name: 'Opprett systemtilgang', - }); - await expect(createButton).toBeVisible(); + await this.CONTINUE_BUTTON.click(); + await expect(this.CREATE_ACCESS_BUTTON).toBeVisible(); } } diff --git a/frontend/playwright/util/TestdataApi.tsx b/frontend/playwright/util/TestdataApi.tsx new file mode 100644 index 00000000..77b9b324 --- /dev/null +++ b/frontend/playwright/util/TestdataApi.tsx @@ -0,0 +1,25 @@ +import { ApiRequests } from '../api-requests/ApiRequests'; // Adjust the path based on your project structure +import { Token } from '../api-requests/Token'; // Adjust the path based on your project structure + +export class TestdataApi { + + static async cleanUpTestUsers() { + const api = new ApiRequests(); + const tokenclass = new Token(); + + try { + const token = await tokenclass.getPersonalAltinnToken(); + const resp = await api.getSystemUsers(token); + const users = JSON.parse(resp); // Convert the JSON string into an array of objects + + if (users.length > 0) { + const respCleanUp = await api.cleanUpSystemUsers(users, token); + console.log('Cleanup response:', respCleanUp); + } else { + console.log('No system users to clean up.'); + } + } catch (error) { + console.error('Error during cleanup:', error); + } + } +} \ No newline at end of file From dfc4b7b05301562430abb246fab6950fdb9b6871 Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Wed, 27 Nov 2024 20:00:36 +0100 Subject: [PATCH 04/15] wait longer for explicit assertions --- frontend/playwright/playwright.config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/playwright/playwright.config.ts b/frontend/playwright/playwright.config.ts index d939306e..fe88cb23 100644 --- a/frontend/playwright/playwright.config.ts +++ b/frontend/playwright/playwright.config.ts @@ -21,6 +21,9 @@ dotenv.config({ */ export default defineConfig({ testDir: './e2eTests', + expect: { + timeout: 15_000, + }, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: [ ['dot'], From fb98ef2d9241c3c39fa2367917780c166824d07e Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Wed, 27 Nov 2024 21:26:38 +0100 Subject: [PATCH 05/15] updated pipeline to support api user/pw --- .github/workflows/scheduled-playwright.yml | 11 +++--- .../workflows/template-test-playwright.yml | 8 +++++ frontend/playwright/api-requests/Token.tsx | 36 +++++++++++-------- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/.github/workflows/scheduled-playwright.yml b/.github/workflows/scheduled-playwright.yml index 9a8b4a82..2dfc2f4f 100644 --- a/.github/workflows/scheduled-playwright.yml +++ b/.github/workflows/scheduled-playwright.yml @@ -10,10 +10,13 @@ on: jobs: playwright-scheduled: name: "Scheduled Playwright tests" + uses: ./.github/workflows/template-test-playwright.yml + with: + environment: ${{ matrix.environment }} strategy: fail-fast: false matrix: - environment: [TT02] - uses: "./.github/workflows/template-test-playwright.yml" - with: - environment: ${{ matrix.environment }} + environment: [AT22] + secrets: + USERNAME_TEST_API: ${{ secrets.USERNAME_TEST_API }} + PASSWORD_TEST_API: ${{ secrets.PASSWORD_TEST_API }} diff --git a/.github/workflows/template-test-playwright.yml b/.github/workflows/template-test-playwright.yml index 73e3ab89..4ccd62db 100644 --- a/.github/workflows/template-test-playwright.yml +++ b/.github/workflows/template-test-playwright.yml @@ -7,6 +7,11 @@ on: type: string required: true description: Environment + secrets: + USERNAME_TEST_API: + required: true + PASSWORD_TEST_API: + required: true jobs: playwright: @@ -27,6 +32,9 @@ jobs: - name: Run Playwright tests working-directory: frontend/playwright run: yarn run env:${{ inputs.environment }} + env: + USERNAME_TEST_API: ${{ secrets.USERNAME_TEST_API }} + PASSWORD_TEST_API: ${{ secrets.PASSWORD_TEST_API }} - uses: actions/upload-artifact@v4 if: failure() with: diff --git a/frontend/playwright/api-requests/Token.tsx b/frontend/playwright/api-requests/Token.tsx index ed65923a..36242d2b 100644 --- a/frontend/playwright/api-requests/Token.tsx +++ b/frontend/playwright/api-requests/Token.tsx @@ -2,6 +2,25 @@ import * as dotenv from 'dotenv'; export class Token { + private readonly username: string; + private readonly password: string; + private readonly org: string; + private readonly environment: string; + + constructor() { + this.username = process.env.token_api_username || process.env.USERNAME_TEST_API || ''; + this.password = process.env.token_api_password || process.env.PASSWORD_TEST_API || ''; + this.org = process.env.ORG || ''; + this.environment = process.env.environment || ''; + + if (!this.username || !this.password) { + throw new Error('API username or password is not defined in the environment variables.'); + } + + if (!this.org || !this.environment) { + throw new Error('ORG or environment is not defined in the environment variables.'); + } + } /** * Fetches an enterprise Altinn token for a specific organization and environment. @@ -9,19 +28,11 @@ export class Token { * @returns The enterprise Altinn token as a string. */ public async getEnterpriseAltinnToken(scopes: string): Promise { - const username = process.env.token_api_username; - const password = process.env.token_api_password; - - if (!username || !password) { - throw new Error('API username or password is not defined in the environment variables.'); - } - // Construct the URL for fetching the enterprise Altinn test token const url = `https://altinn-testtools-token-generator.azurewebsites.net/api/GetEnterpriseToken` + `?orgNo=${process.env.ORG}&env=${process.env.environment}&scopes=${scopes}`; - // Basic authentication header - const auth = Buffer.from(`${username}:${password}`).toString('base64'); + const auth = Buffer.from(`${this.username}:${this.password}`).toString('base64'); const headers = { 'Authorization': `Basic ${auth}`, }; @@ -40,11 +51,6 @@ public async getEnterpriseAltinnToken(scopes: string): Promise { * @returns The Altinn test token as a string */ public async getPersonalAltinnToken(scopes: string = ''): Promise { - const username = process.env.token_api_username; - const password = process.env.token_api_password; - if (!username || !password) { - throw new Error('API username or password is not defined in the environment variables.'); - } // Construct the URL for fetching the Altinn test token const url = `https://altinn-testtools-token-generator.azurewebsites.net/api/GetPersonalToken?env=${process.env.environment}` + `&pid=${process.env.PID}` + @@ -54,7 +60,7 @@ public async getEnterpriseAltinnToken(scopes: string): Promise { (scopes ? `&scopes=${scopes}` : ''); // Retrieve the token - const auth = Buffer.from(`${username}:${password}`).toString('base64'); + const auth = Buffer.from(`${this.username}:${this.password}`).toString('base64'); const headers = { 'Authorization': `Basic ${auth}`, }; From 9a1adab02b50fb9560bbc45f8eecc57ea7323b21 Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Thu, 28 Nov 2024 09:56:24 +0100 Subject: [PATCH 06/15] fetch user/pw properly for test api --- frontend/playwright/api-requests/Token.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/playwright/api-requests/Token.tsx b/frontend/playwright/api-requests/Token.tsx index 36242d2b..f57bdfb8 100644 --- a/frontend/playwright/api-requests/Token.tsx +++ b/frontend/playwright/api-requests/Token.tsx @@ -8,8 +8,8 @@ export class Token { private readonly environment: string; constructor() { - this.username = process.env.token_api_username || process.env.USERNAME_TEST_API || ''; - this.password = process.env.token_api_password || process.env.PASSWORD_TEST_API || ''; + this.username = process.env.USERNAME_TEST_API || ''; + this.password = process.env.PASSWORD_TEST_API || ''; this.org = process.env.ORG || ''; this.environment = process.env.environment || ''; From 28ce93a855f903fee37ce8ddd23748455e8bd9e1 Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Thu, 28 Nov 2024 13:57:09 +0100 Subject: [PATCH 07/15] global teardown + delete test --- .../playwright/api-requests/ApiRequests.tsx | 11 ++++--- frontend/playwright/api-requests/Token.tsx | 1 + .../api-requests/global-teardown.tsx | 9 ++++++ frontend/playwright/config/.env.tt02 | 4 +-- .../e2eTests/approveSystemUserRequest.spec.ts | 6 ---- .../e2eTests/deleteSystemUser.spec.ts | 24 ++++++++++++++ .../e2eTests/selectSystemVendor.spec.ts | 9 +----- frontend/playwright/pages/systemUserPage.ts | 31 +++++++++++++++++-- frontend/playwright/playwright.config.ts | 1 + frontend/playwright/util/TestdataApi.tsx | 5 ++- 10 files changed, 75 insertions(+), 26 deletions(-) create mode 100644 frontend/playwright/api-requests/global-teardown.tsx create mode 100644 frontend/playwright/e2eTests/deleteSystemUser.spec.ts diff --git a/frontend/playwright/api-requests/ApiRequests.tsx b/frontend/playwright/api-requests/ApiRequests.tsx index 2c48d845..2ecbcbc7 100644 --- a/frontend/playwright/api-requests/ApiRequests.tsx +++ b/frontend/playwright/api-requests/ApiRequests.tsx @@ -36,8 +36,13 @@ export class ApiRequests { }); if (!response.ok) { - const errorText = await response.text(); // Optional: Read the error body + if (response.status === 404) { + console.warn('System users not found (404).'); + return '[]'; //Return empty list if no users to be deleted + } + const errorText = await response.text(); console.error('Failed to fetch system users:', response.status, errorText); + console.log(response.status) throw new Error(`Failed to fetch system users: ${response.statusText}`); } @@ -58,7 +63,7 @@ export class ApiRequests { const response = await fetch(url, { method: 'DELETE', headers: { - Authorization: `Bearer ${token}`, // Replace with your token logic + Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, }); @@ -68,8 +73,6 @@ export class ApiRequests { console.error('Failed to delete system user:', response.status, errorBody); throw new Error(`Failed to delete system user: ${response.statusText}`); } - - console.log(`System user deleted successfully: ${response.status}`); } catch (error) { console.error('Error during system user deletion:', error); throw new Error('System user deletion failed. Check logs for details.'); diff --git a/frontend/playwright/api-requests/Token.tsx b/frontend/playwright/api-requests/Token.tsx index f57bdfb8..9cfead5c 100644 --- a/frontend/playwright/api-requests/Token.tsx +++ b/frontend/playwright/api-requests/Token.tsx @@ -13,6 +13,7 @@ export class Token { this.org = process.env.ORG || ''; this.environment = process.env.environment || ''; + if (!this.username || !this.password) { throw new Error('API username or password is not defined in the environment variables.'); } diff --git a/frontend/playwright/api-requests/global-teardown.tsx b/frontend/playwright/api-requests/global-teardown.tsx new file mode 100644 index 00000000..8106dad3 --- /dev/null +++ b/frontend/playwright/api-requests/global-teardown.tsx @@ -0,0 +1,9 @@ +import { FullConfig } from "@playwright/test"; +import { TestdataApi } from "playwright/util/TestdataApi"; + +async function globalTeardown(config: FullConfig) { + console.log('Kjører global opprydding...'); + await TestdataApi.cleanUpTestUsers(); +} + +export default globalTeardown; \ No newline at end of file diff --git a/frontend/playwright/config/.env.tt02 b/frontend/playwright/config/.env.tt02 index ae3ba4e9..96a4d171 100644 --- a/frontend/playwright/config/.env.tt02 +++ b/frontend/playwright/config/.env.tt02 @@ -3,8 +3,8 @@ BASE_URL="https://tt02.altinn.no" SYSYEMUSER_URL="https://authn.ui.tt02.altinn.no/authfront/ui/auth/creation" API_BASE_URL="https://platform.tt02.altinn.no/authentication/api/" -ALTINN_PARTY_ID="51359757" -ALTINN_USER_ID="20010849" +ALTINN_PARTY_ID="51573288" +ALTINN_USER_ID="1260880" PID="14824497789" ORG="310547891" SYSTEM_ID="310547891_E2E - Playwright - Authentication" \ No newline at end of file diff --git a/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts index 96517a63..2788d71a 100644 --- a/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts +++ b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts @@ -1,4 +1,3 @@ -//Dummy URL to test with: https://authn.ui.at22.altinn.cloud/authfront/ui/auth/vendorrequest?id=fa82738b-d948-48d7-b58f-b2709dccec55 import test, { expect } from '@playwright/test'; import { ApiRequests } from '../api-requests/ApiRequests'; import { Token } from 'playwright/api-requests/Token'; @@ -26,11 +25,6 @@ test('Godkjenn Systembrukerforespørsel', async ({ page }): Promise => { await expect(logoutButton).toBeVisible(); }); -//Clean up test users -test.afterAll(async () => { - await TestdataApi.cleanUpTestUsers(); -}); - async function prepareSystemUserRequest(api: ApiRequests, tokenclass: Token) { const payload = api.generatePayloadSystemUserRequest(); const scopes = diff --git a/frontend/playwright/e2eTests/deleteSystemUser.spec.ts b/frontend/playwright/e2eTests/deleteSystemUser.spec.ts new file mode 100644 index 00000000..568eb76a --- /dev/null +++ b/frontend/playwright/e2eTests/deleteSystemUser.spec.ts @@ -0,0 +1,24 @@ +import { expect, test } from '@playwright/test'; +import { SystemUserPage } from '../pages/systemUserPage'; + +test('Delete created system user', async ({ page }): Promise => { + const systemUserPage = new SystemUserPage(page); + + // Create new system user + await systemUserPage.selectSystemVendor('TripletexN/A (914286018)'); + await systemUserPage.CREATE_SYSTEM_USER_BUTTON.click(); + await expect(systemUserPage.SYSTEMUSER_CREATED_HEADING).toBeVisible(); + await expect(page.getByText('Tripletex').first()).toBeVisible(); + + // Delete system user + await systemUserPage.EDIT_SYSTEMUSER_LINK.click(); + await systemUserPage.DELETE_SYSTEMUSER_BUTTON.click(); + await systemUserPage.FINAL_DELETE_SYSTEMUSER_BUTTON.click(); + + //Back to overview page + await expect(systemUserPage.MAIN_HEADER).toBeVisible(); +}); + +test('Cancel', async ({ page }): Promise => { + const systemUserPage = new SystemUserPage(page); +}); diff --git a/frontend/playwright/e2eTests/selectSystemVendor.spec.ts b/frontend/playwright/e2eTests/selectSystemVendor.spec.ts index e115eee8..2cc43f18 100644 --- a/frontend/playwright/e2eTests/selectSystemVendor.spec.ts +++ b/frontend/playwright/e2eTests/selectSystemVendor.spec.ts @@ -10,14 +10,7 @@ test('should be able to select a system vendor', async ({ page }): Promise test('Create system user and verify landing page', async ({ page }): Promise => { const systemUserPage = new SystemUserPage(page); await systemUserPage.selectSystemVendor('TripletexN/A (914286018)'); - await systemUserPage.CREATE_ACCESS_BUTTON.click(); + await systemUserPage.CREATE_SYSTEM_USER_BUTTON.click(); await expect(systemUserPage.SYSTEMUSER_CREATED_HEADING).toBeVisible(); await expect(page.getByText('Tripletex').first()).toBeVisible(); }); - -//Tdo - lag test for å slette og avbryte - -//Clean up test users -test.afterAll(async () => { - await TestdataApi.cleanUpTestUsers(); -}); diff --git a/frontend/playwright/pages/systemUserPage.ts b/frontend/playwright/pages/systemUserPage.ts index 48d14a3b..4c2d9644 100644 --- a/frontend/playwright/pages/systemUserPage.ts +++ b/frontend/playwright/pages/systemUserPage.ts @@ -4,20 +4,45 @@ import noNb from '../../src/localizations/no_nb.json'; export class SystemUserPage { public readonly SELECT_VENDOR_LABEL: Locator; public readonly CONTINUE_BUTTON: Locator; - public readonly CREATE_ACCESS_BUTTON: Locator; + public readonly CREATE_SYSTEM_USER_BUTTON: Locator; public readonly SYSTEMUSER_CREATED_HEADING: Locator; + public readonly EDIT_SYSTEMUSER_LINK: Locator; + public readonly DELETE_SYSTEMUSER_BUTTON: Locator; + public readonly FINAL_DELETE_SYSTEMUSER_BUTTON: Locator; + public readonly CREATE_NEW_SYSTEMUSER_HEADER: Locator; + public readonly MAIN_HEADER: Locator; constructor(public page: Page) { this.SELECT_VENDOR_LABEL = this.page.getByLabel(noNb.authent_creationpage.pull_down_menu_label); this.CONTINUE_BUTTON = this.page.getByRole('button', { name: noNb.authent_creationpage.confirm_button, }); - this.CREATE_ACCESS_BUTTON = this.page.getByRole('button', { + this.CREATE_SYSTEM_USER_BUTTON = this.page.getByRole('button', { name: noNb.authent_includedrightspage.confirm_button, }); this.SYSTEMUSER_CREATED_HEADING = this.page.getByRole('heading', { name: noNb.authent_overviewpage.created_system_user_title, }); + + this.EDIT_SYSTEMUSER_LINK = this.page.getByRole('link', { + name: noNb.authent_overviewpage.edit_system_user, + }); + + this.DELETE_SYSTEMUSER_BUTTON = this.page.getByRole('button', { + name: noNb.authent_detailpage.delete_systemuser, + }); + + this.FINAL_DELETE_SYSTEMUSER_BUTTON = this.page.getByRole('contentinfo').getByRole('button', { + name: noNb.authent_detailpage.delete_systemuser, + }); + + this.CREATE_NEW_SYSTEMUSER_HEADER = this.page.getByRole('heading', { + name: noNb.authent_overviewpage.sub_title, + }); + + this.MAIN_HEADER = this.page.getByRole('heading', { + name: noNb.authent_overviewpage.banner_title, + }); } async selectSystemVendor(vendorSystem: string) { @@ -26,6 +51,6 @@ export class SystemUserPage { await this.page.getByLabel(vendorSystem, { exact: true }).click(); await this.CONTINUE_BUTTON.click(); - await expect(this.CREATE_ACCESS_BUTTON).toBeVisible(); + await expect(this.CREATE_SYSTEM_USER_BUTTON).toBeVisible(); } } diff --git a/frontend/playwright/playwright.config.ts b/frontend/playwright/playwright.config.ts index fe88cb23..53e687dd 100644 --- a/frontend/playwright/playwright.config.ts +++ b/frontend/playwright/playwright.config.ts @@ -21,6 +21,7 @@ dotenv.config({ */ export default defineConfig({ testDir: './e2eTests', + globalTeardown: './api-requests/global-teardown.tsx', expect: { timeout: 15_000, }, diff --git a/frontend/playwright/util/TestdataApi.tsx b/frontend/playwright/util/TestdataApi.tsx index 77b9b324..e51dc173 100644 --- a/frontend/playwright/util/TestdataApi.tsx +++ b/frontend/playwright/util/TestdataApi.tsx @@ -8,15 +8,14 @@ export class TestdataApi { const tokenclass = new Token(); try { + //cleanup method, dont fail test if this fails but log it const token = await tokenclass.getPersonalAltinnToken(); const resp = await api.getSystemUsers(token); - const users = JSON.parse(resp); // Convert the JSON string into an array of objects + const users = JSON.parse(resp); if (users.length > 0) { const respCleanUp = await api.cleanUpSystemUsers(users, token); - console.log('Cleanup response:', respCleanUp); } else { - console.log('No system users to clean up.'); } } catch (error) { console.error('Error during cleanup:', error); From a1a31a2794ac291445d0d8558153b48c7a62d0c9 Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Fri, 29 Nov 2024 17:21:36 +0100 Subject: [PATCH 08/15] updated selector for final deletion button --- .github/workflows/scheduled-playwright.yml | 2 +- frontend/playwright/api-requests/ApiRequests.tsx | 1 - frontend/playwright/pages/systemUserPage.ts | 2 +- frontend/playwright/util/TestdataApi.tsx | 5 ++--- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/scheduled-playwright.yml b/.github/workflows/scheduled-playwright.yml index 2dfc2f4f..ce19435e 100644 --- a/.github/workflows/scheduled-playwright.yml +++ b/.github/workflows/scheduled-playwright.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - environment: [AT22] + environment: [AT22, TT02] secrets: USERNAME_TEST_API: ${{ secrets.USERNAME_TEST_API }} PASSWORD_TEST_API: ${{ secrets.PASSWORD_TEST_API }} diff --git a/frontend/playwright/api-requests/ApiRequests.tsx b/frontend/playwright/api-requests/ApiRequests.tsx index 2ecbcbc7..d2d32598 100644 --- a/frontend/playwright/api-requests/ApiRequests.tsx +++ b/frontend/playwright/api-requests/ApiRequests.tsx @@ -21,7 +21,6 @@ export class ApiRequests { } } - //Todo - consider moving to class TestdataApi public async getSystemUsers(token: string): Promise { var endpoint = `v1/systemuser/${process.env.ALTINN_PARTY_ID}`; var url = `${process.env.API_BASE_URL}${endpoint}`; diff --git a/frontend/playwright/pages/systemUserPage.ts b/frontend/playwright/pages/systemUserPage.ts index 4c2d9644..52e06e02 100644 --- a/frontend/playwright/pages/systemUserPage.ts +++ b/frontend/playwright/pages/systemUserPage.ts @@ -32,7 +32,7 @@ export class SystemUserPage { name: noNb.authent_detailpage.delete_systemuser, }); - this.FINAL_DELETE_SYSTEMUSER_BUTTON = this.page.getByRole('contentinfo').getByRole('button', { + this.FINAL_DELETE_SYSTEMUSER_BUTTON = this.page.getByRole('dialog').getByRole('button', { name: noNb.authent_detailpage.delete_systemuser, }); diff --git a/frontend/playwright/util/TestdataApi.tsx b/frontend/playwright/util/TestdataApi.tsx index e51dc173..17c4e905 100644 --- a/frontend/playwright/util/TestdataApi.tsx +++ b/frontend/playwright/util/TestdataApi.tsx @@ -14,9 +14,8 @@ export class TestdataApi { const users = JSON.parse(resp); if (users.length > 0) { - const respCleanUp = await api.cleanUpSystemUsers(users, token); - } else { - } + await api.cleanUpSystemUsers(users, token); + } } catch (error) { console.error('Error during cleanup:', error); } From 1e0daddfe175c50a7d5bdef5b7bb91745bf75d9d Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Fri, 29 Nov 2024 18:41:00 +0100 Subject: [PATCH 09/15] fixed workflow --- .github/workflows/scheduled-playwright.yml | 5 +++-- .../workflows/template-test-playwright.yml | 22 ++++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/.github/workflows/scheduled-playwright.yml b/.github/workflows/scheduled-playwright.yml index ce19435e..d35e4441 100644 --- a/.github/workflows/scheduled-playwright.yml +++ b/.github/workflows/scheduled-playwright.yml @@ -1,4 +1,4 @@ -name: Scheduled Playwright Tests +name: Playwright tests (Authentication) on: schedule: @@ -16,7 +16,8 @@ jobs: strategy: fail-fast: false matrix: - environment: [AT22, TT02] + environment: [TT02] secrets: USERNAME_TEST_API: ${{ secrets.USERNAME_TEST_API }} PASSWORD_TEST_API: ${{ secrets.PASSWORD_TEST_API }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.github/workflows/template-test-playwright.yml b/.github/workflows/template-test-playwright.yml index 4ccd62db..ffdd611b 100644 --- a/.github/workflows/template-test-playwright.yml +++ b/.github/workflows/template-test-playwright.yml @@ -12,6 +12,8 @@ on: required: true PASSWORD_TEST_API: required: true + SLACK_WEBHOOK_URL: + required: true jobs: playwright: @@ -35,9 +37,17 @@ jobs: env: USERNAME_TEST_API: ${{ secrets.USERNAME_TEST_API }} PASSWORD_TEST_API: ${{ secrets.PASSWORD_TEST_API }} - - uses: actions/upload-artifact@v4 - if: failure() - with: - name: playwright-report - path: frontend/playwright/test-results/${{ inputs.environment }} - retention-days: 30 + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + + - name: Notify Slack on Failure + if: failure() # This step runs only if the previous steps fail + run: | + curl -X POST -H 'Content-type: application/json' --data '{ + "text": ":playwright: Frontend-tester feilet i testmiljø:`${{ inputs.environment }}` på repo: `${{ github.repository }}`. Mer detaljer her: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + }' ${{ secrets.SLACK_WEBHOOK_URL }} + # - uses: actions/upload-artifact@v4 + # if: failure() + # with: + # name: playwright-report + # path: frontend/playwright/test-results/${{ inputs.environment }} + # retention-days: 30 From 05b0af9c2e1b230740d437610441bf05b6711f0f Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Fri, 29 Nov 2024 18:46:35 +0100 Subject: [PATCH 10/15] working workflow posting to Slack --- .github/workflows/template-test-playwright.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/template-test-playwright.yml b/.github/workflows/template-test-playwright.yml index ffdd611b..e4dba71a 100644 --- a/.github/workflows/template-test-playwright.yml +++ b/.github/workflows/template-test-playwright.yml @@ -45,9 +45,9 @@ jobs: curl -X POST -H 'Content-type: application/json' --data '{ "text": ":playwright: Frontend-tester feilet i testmiljø:`${{ inputs.environment }}` på repo: `${{ github.repository }}`. Mer detaljer her: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" }' ${{ secrets.SLACK_WEBHOOK_URL }} - # - uses: actions/upload-artifact@v4 - # if: failure() - # with: - # name: playwright-report - # path: frontend/playwright/test-results/${{ inputs.environment }} - # retention-days: 30 + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: playwright-report + path: frontend/playwright/test-results/${{ inputs.environment }} + retention-days: 30 From c03e1af2c4d79172953cffa5187af2e4fd014526 Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Mon, 2 Dec 2024 11:41:03 +0100 Subject: [PATCH 11/15] fixed lint errors --- .../playwright/api-requests/ApiRequests.tsx | 30 +++++++++---------- frontend/playwright/api-requests/Token.tsx | 3 +- .../api-requests/global-teardown.tsx | 3 +- .../e2eTests/approveSystemUserRequest.spec.ts | 5 ++-- .../e2eTests/deleteSystemUser.spec.ts | 4 --- .../e2eTests/selectSystemVendor.spec.ts | 1 - 6 files changed, 19 insertions(+), 27 deletions(-) diff --git a/frontend/playwright/api-requests/ApiRequests.tsx b/frontend/playwright/api-requests/ApiRequests.tsx index d2d32598..69f84119 100644 --- a/frontend/playwright/api-requests/ApiRequests.tsx +++ b/frontend/playwright/api-requests/ApiRequests.tsx @@ -2,12 +2,12 @@ interface PostSystemUserRequestPayload { systemId: string; partyOrgNo: string; externalRef: string; - rights: Array<{ - resource: Array<{ - id: string; + rights: { + resource: { + id: string; value: string; - }>; - }>; + }[]; + }[]; redirectUrl: string; } @@ -15,15 +15,15 @@ interface PostSystemUserRequestPayload { export class ApiRequests { - public async cleanUpSystemUsers(systemUsers: Array<{ id: string }>, token: string): Promise { + public async cleanUpSystemUsers(systemUsers: { id: string }[], token: string): Promise { for (const systemuser of systemUsers) { await this.deleteSystemUser(token, systemuser.id) } } public async getSystemUsers(token: string): Promise { - var endpoint = `v1/systemuser/${process.env.ALTINN_PARTY_ID}`; - var url = `${process.env.API_BASE_URL}${endpoint}`; + const endpoint = `v1/systemuser/${process.env.ALTINN_PARTY_ID}`; + const url = `${process.env.API_BASE_URL}${endpoint}`; try { const response = await fetch(url, { @@ -55,8 +55,8 @@ export class ApiRequests { } public async deleteSystemUser(token: string, systemUserId: string): Promise{ - var endpoint = `v1/systemuser/${process.env.ALTINN_PARTY_ID}/${systemUserId}` - var url = `${process.env.API_BASE_URL}${endpoint}` + const endpoint = `v1/systemuser/${process.env.ALTINN_PARTY_ID}/${systemUserId}` + const url = `${process.env.API_BASE_URL}${endpoint}` try { const response = await fetch(url, { @@ -79,8 +79,8 @@ export class ApiRequests { } - public async sendPostRequest(payload: PostSystemUserRequestPayload, endpoint: string, token: string): Promise { - var url = `${process.env.API_BASE_URL}${endpoint}` + public async sendPostRequest(payload: PostSystemUserRequestPayload, endpoint: string, token: string): Promise { + const url = `${process.env.API_BASE_URL}${endpoint}` try { const response = await fetch(url, { @@ -98,7 +98,7 @@ export class ApiRequests { throw new Error(`HTTP error! Status: ${response.status}, Response Body: ${errorBody}`); } const data = await response.json(); - return data; // Return the response data + return data as Promise; // Return the response data } catch (error) { console.error('Error:', error); throw error; // Rethrow the error to handle it in the test @@ -111,7 +111,7 @@ export class ApiRequests { * @param token The authorization token. * @returns The response data as JSON. */ -public async fetchLastSystemRequest(endpoint: string, token: string): Promise { +public async fetchLastSystemRequest(endpoint: string, token: string): Promise { const url = `${process.env.API_BASE_URL}${endpoint}`; try { @@ -137,7 +137,7 @@ public async fetchLastSystemRequest(endpoint: string, token: string): Promise { * Used for fetching an Altinn test token for a specific role * @returns The Altinn test token as a string */ - public async getPersonalAltinnToken(scopes: string = ''): Promise { + public async getPersonalAltinnToken(scopes = ''): Promise { // Construct the URL for fetching the Altinn test token const url = `https://altinn-testtools-token-generator.azurewebsites.net/api/GetPersonalToken?env=${process.env.environment}` + `&pid=${process.env.PID}` + diff --git a/frontend/playwright/api-requests/global-teardown.tsx b/frontend/playwright/api-requests/global-teardown.tsx index 8106dad3..ddd6371e 100644 --- a/frontend/playwright/api-requests/global-teardown.tsx +++ b/frontend/playwright/api-requests/global-teardown.tsx @@ -1,7 +1,6 @@ -import { FullConfig } from "@playwright/test"; import { TestdataApi } from "playwright/util/TestdataApi"; -async function globalTeardown(config: FullConfig) { +async function globalTeardown() { console.log('Kjører global opprydding...'); await TestdataApi.cleanUpTestUsers(); } diff --git a/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts index 2788d71a..21df039b 100644 --- a/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts +++ b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts @@ -1,12 +1,11 @@ import test, { expect } from '@playwright/test'; import { ApiRequests } from '../api-requests/ApiRequests'; import { Token } from 'playwright/api-requests/Token'; -import { TestdataApi } from 'playwright/util/TestdataApi'; test('Avvis Systembrukerforespørsel', async ({ page }): Promise => { const api = new ApiRequests(); // Create an instance const tokenclass = new Token(); - var confirmUrl = await prepareSystemUserRequest(api, tokenclass); + const confirmUrl = await prepareSystemUserRequest(api, tokenclass); await page.goto(confirmUrl); await page.getByRole('button', { name: 'Avvis' }).click(); @@ -17,7 +16,7 @@ test('Avvis Systembrukerforespørsel', async ({ page }): Promise => { test('Godkjenn Systembrukerforespørsel', async ({ page }): Promise => { const api = new ApiRequests(); // Create an instance const tokenclass = new Token(); - var confirmUrl = await prepareSystemUserRequest(api, tokenclass); + const confirmUrl = await prepareSystemUserRequest(api, tokenclass); await page.goto(confirmUrl); await page.getByRole('button', { name: 'Godkjenn' }).click(); diff --git a/frontend/playwright/e2eTests/deleteSystemUser.spec.ts b/frontend/playwright/e2eTests/deleteSystemUser.spec.ts index 568eb76a..12a7fa79 100644 --- a/frontend/playwright/e2eTests/deleteSystemUser.spec.ts +++ b/frontend/playwright/e2eTests/deleteSystemUser.spec.ts @@ -18,7 +18,3 @@ test('Delete created system user', async ({ page }): Promise => { //Back to overview page await expect(systemUserPage.MAIN_HEADER).toBeVisible(); }); - -test('Cancel', async ({ page }): Promise => { - const systemUserPage = new SystemUserPage(page); -}); diff --git a/frontend/playwright/e2eTests/selectSystemVendor.spec.ts b/frontend/playwright/e2eTests/selectSystemVendor.spec.ts index 2cc43f18..48c2f277 100644 --- a/frontend/playwright/e2eTests/selectSystemVendor.spec.ts +++ b/frontend/playwright/e2eTests/selectSystemVendor.spec.ts @@ -1,6 +1,5 @@ import { expect, test } from '@playwright/test'; import { SystemUserPage } from '../pages/systemUserPage'; -import { TestdataApi } from 'playwright/util/TestdataApi'; test('should be able to select a system vendor', async ({ page }): Promise => { const systemUserPage = new SystemUserPage(page); From e17006aebfb34e388aa3c5695e01913f9bf1a051 Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Mon, 2 Dec 2024 12:42:16 +0100 Subject: [PATCH 12/15] formatted typescript classes properly - i hope? --- .../playwright/api-requests/ApiRequests.tsx | 165 +++++++++--------- frontend/playwright/api-requests/Token.tsx | 69 ++++---- 2 files changed, 115 insertions(+), 119 deletions(-) diff --git a/frontend/playwright/api-requests/ApiRequests.tsx b/frontend/playwright/api-requests/ApiRequests.tsx index 69f84119..c63b973c 100644 --- a/frontend/playwright/api-requests/ApiRequests.tsx +++ b/frontend/playwright/api-requests/ApiRequests.tsx @@ -4,61 +4,57 @@ interface PostSystemUserRequestPayload { externalRef: string; rights: { resource: { - id: string; + id: string; value: string; }[]; }[]; redirectUrl: string; } - export class ApiRequests { - - public async cleanUpSystemUsers(systemUsers: { id: string }[], token: string): Promise { - for (const systemuser of systemUsers) { - await this.deleteSystemUser(token, systemuser.id) + for (const systemuser of systemUsers) { + await this.deleteSystemUser(token, systemuser.id); + } } -} public async getSystemUsers(token: string): Promise { const endpoint = `v1/systemuser/${process.env.ALTINN_PARTY_ID}`; const url = `${process.env.API_BASE_URL}${endpoint}`; try { - const response = await fetch(url, { - method: 'GET', - headers: { - Authorization: `Bearer ${token}`, // Use the token for authentication - 'Content-Type': 'application/json', - }, - }); + const response = await fetch(url, { + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, // Use the token for authentication + 'Content-Type': 'application/json', + }, + }); - if (!response.ok) { + if (!response.ok) { if (response.status === 404) { console.warn('System users not found (404).'); return '[]'; //Return empty list if no users to be deleted } - const errorText = await response.text(); - console.error('Failed to fetch system users:', response.status, errorText); - console.log(response.status) - throw new Error(`Failed to fetch system users: ${response.statusText}`); - } - - const data = await response.json(); // Assuming the API returns JSON data - return JSON.stringify(data, null, 2); // Format the JSON for better readability (optional) - } catch (error) { - console.error('Error fetching system users:', error); - throw new Error('Error fetching system users. Check logs for details.'); - } + const errorText = await response.text(); + console.error('Failed to fetch system users:', response.status, errorText); + console.log(response.status); + throw new Error(`Failed to fetch system users: ${response.statusText}`); + } + const data = await response.json(); // Assuming the API returns JSON data + return JSON.stringify(data, null, 2); // Format the JSON for better readability (optional) + } catch (error) { + console.error('Error fetching system users:', error); + throw new Error('Error fetching system users. Check logs for details.'); + } } - public async deleteSystemUser(token: string, systemUserId: string): Promise{ - const endpoint = `v1/systemuser/${process.env.ALTINN_PARTY_ID}/${systemUserId}` - const url = `${process.env.API_BASE_URL}${endpoint}` + public async deleteSystemUser(token: string, systemUserId: string): Promise { + const endpoint = `v1/systemuser/${process.env.ALTINN_PARTY_ID}/${systemUserId}`; + const url = `${process.env.API_BASE_URL}${endpoint}`; - try { + try { const response = await fetch(url, { method: 'DELETE', headers: { @@ -78,25 +74,28 @@ export class ApiRequests { } } - - public async sendPostRequest(payload: PostSystemUserRequestPayload, endpoint: string, token: string): Promise { - const url = `${process.env.API_BASE_URL}${endpoint}` + public async sendPostRequest( + payload: PostSystemUserRequestPayload, + endpoint: string, + token: string, + ): Promise { + const url = `${process.env.API_BASE_URL}${endpoint}`; try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}`, // Add the Authorization header + Authorization: `Bearer ${token}`, // Add the Authorization header }, body: JSON.stringify(payload), }); if (!response.ok) { - const errorBody = await response.text(); // Read the response body - console.error(`HTTP Error! Status: ${response.status}, Response Body: ${errorBody}`); - throw new Error(`HTTP error! Status: ${response.status}, Response Body: ${errorBody}`); - } + const errorBody = await response.text(); // Read the response body + console.error(`HTTP Error! Status: ${response.status}, Response Body: ${errorBody}`); + throw new Error(`HTTP error! Status: ${response.status}, Response Body: ${errorBody}`); + } const data = await response.json(); return data as Promise; // Return the response data } catch (error) { @@ -106,63 +105,61 @@ export class ApiRequests { } /** - * Sends a GET request to fetch the last system request. - * @param endpoint The API endpoint (relative path). - * @param token The authorization token. - * @returns The response data as JSON. - */ -public async fetchLastSystemRequest(endpoint: string, token: string): Promise { - const url = `${process.env.API_BASE_URL}${endpoint}`; + * Sends a GET request to fetch the last system request. + * @param endpoint The API endpoint (relative path). + * @param token The authorization token. + * @returns The response data as JSON. + */ + public async fetchLastSystemRequest(endpoint: string, token: string): Promise { + const url = `${process.env.API_BASE_URL}${endpoint}`; - try { - const response = await fetch(url, { - method: 'GET', - headers: { - 'Authorization': `Bearer ${token}`, // Add the Authorization header - }, - }); + try { + const response = await fetch(url, { + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, // Add the Authorization header + }, + }); - if (!response.ok) { - const errorBody = await response.text(); // Read the response body - console.error(`HTTP Error! Status: ${response.status}, Response Body: ${errorBody}`); - throw new Error(`HTTP error! Status: ${response.status}, Response Body: ${errorBody}`); + if (!response.ok) { + const errorBody = await response.text(); // Read the response body + console.error(`HTTP Error! Status: ${response.status}, Response Body: ${errorBody}`); + throw new Error(`HTTP error! Status: ${response.status}, Response Body: ${errorBody}`); + } + const data = await response.json(); + return data; // Return the response data + } catch (error) { + console.error('Error:', error); + throw error; // Rethrow the error to handle it in the test } - - const data = await response.json(); - return data; // Return the response data - } catch (error) { - console.error('Error:', error); - throw error; // Rethrow the error to handle it in the test } -} generatePayloadSystemUserRequest(): PostSystemUserRequestPayload { - const randomString = Date.now(); // Current timestamp in milliseconds + const randomString = Date.now(); // Current timestamp in milliseconds const randomNum = Math.random().toString(36); return { systemId: `${process.env.SYSTEM_ID}`, partyOrgNo: `${process.env.ORG}`, externalRef: `${randomNum}${randomString}`, rights: [ - { - resource: [ - { - value: "authentication-e2e-test", - id: "urn:altinn:resource" - } - ] - }, - { - resource: [ - { - value: "vegardtestressurs", - id: "urn:altinn:resource" - } - ] - } - ], - redirectUrl: "https://altinn.no" + { + resource: [ + { + value: 'authentication-e2e-test', + id: 'urn:altinn:resource', + }, + ], + }, + { + resource: [ + { + value: 'vegardtestressurs', + id: 'urn:altinn:resource', + }, + ], + }, + ], + redirectUrl: 'https://altinn.no', }; } - } \ No newline at end of file diff --git a/frontend/playwright/api-requests/Token.tsx b/frontend/playwright/api-requests/Token.tsx index c5cb3b45..a901f525 100644 --- a/frontend/playwright/api-requests/Token.tsx +++ b/frontend/playwright/api-requests/Token.tsx @@ -1,6 +1,4 @@ - export class Token { - private readonly username: string; private readonly password: string; private readonly org: string; @@ -12,7 +10,6 @@ export class Token { this.org = process.env.ORG || ''; this.environment = process.env.environment || ''; - if (!this.username || !this.password) { throw new Error('API username or password is not defined in the environment variables.'); } @@ -23,56 +20,58 @@ export class Token { } /** - * Fetches an enterprise Altinn token for a specific organization and environment. - * @param scopes Scopes required for the token. - * @returns The enterprise Altinn token as a string. - */ -public async getEnterpriseAltinnToken(scopes: string): Promise { - // Construct the URL for fetching the enterprise Altinn test token - const url = `https://altinn-testtools-token-generator.azurewebsites.net/api/GetEnterpriseToken` + - `?orgNo=${process.env.ORG}&env=${process.env.environment}&scopes=${scopes}`; + * Fetches an enterprise Altinn token for a specific organization and environment. + * @param scopes Scopes required for the token. + * @returns The enterprise Altinn token as a string. + */ + public async getEnterpriseAltinnToken(scopes: string): Promise { + // Construct the URL for fetching the enterprise Altinn test token + const url = + `https://altinn-testtools-token-generator.azurewebsites.net/api/GetEnterpriseToken` + + `?orgNo=${process.env.ORG}&env=${process.env.environment}&scopes=${scopes}`; - const auth = Buffer.from(`${this.username}:${this.password}`).toString('base64'); - const headers = { - 'Authorization': `Basic ${auth}`, - }; + const auth = Buffer.from(`${this.username}:${this.password}`).toString('base64'); + const headers = { + Authorization: `Basic ${auth}`, + }; - // Retrieve the token - const token = await this.getAltinnToken(url, headers); - if (!token) { - throw new Error("Token retrieval failed for Enterprise Altinn token"); - } + // Retrieve the token + const token = await this.getAltinnToken(url, headers); + if (!token) { + throw new Error('Token retrieval failed for Enterprise Altinn token'); + } - return token; -} + return token; + } - /** + /** * Used for fetching an Altinn test token for a specific role * @returns The Altinn test token as a string */ public async getPersonalAltinnToken(scopes = ''): Promise { // Construct the URL for fetching the Altinn test token - const url = `https://altinn-testtools-token-generator.azurewebsites.net/api/GetPersonalToken?env=${process.env.environment}` + - `&pid=${process.env.PID}` + - `&userid=${process.env.ALTINN_USER_ID}` + - `&partyid=${process.env.ALTINN_PARTY_ID}` + - `&authLvl=3&ttl=3000` + - (scopes ? `&scopes=${scopes}` : ''); + const url = + `https://altinn-testtools-token-generator.azurewebsites.net/api/GetPersonalToken?env=${process.env.environment}` + + `&pid=${process.env.PID}` + + `&userid=${process.env.ALTINN_USER_ID}` + + `&partyid=${process.env.ALTINN_PARTY_ID}` + + `&authLvl=3&ttl=3000` + + (scopes ? `&scopes=${scopes}` : ''); // Retrieve the token const auth = Buffer.from(`${this.username}:${this.password}`).toString('base64'); - const headers = { - 'Authorization': `Basic ${auth}`, - }; + const headers = { + Authorization: `Basic ${auth}`, + }; const token = await this.getAltinnToken(url, headers); if (!token) { - throw new Error("Token retrieval failed for Altinn token"); + throw new Error('Token retrieval failed for Altinn token'); } return token; } -private async getAltinnToken(url: string, headers: Record): Promise { + private async getAltinnToken(url: string, headers: Record): Promise { const response = await fetch(url, { headers }); if (!response.ok) { const errorMessage = await response.text(); // Fetch the full error message from the response @@ -80,4 +79,4 @@ private async getAltinnToken(url: string, headers: Record): Prom } return response.text(); } -} \ No newline at end of file +} From ab294fef6d39612690f3d3bbed1778c16d5738f6 Mon Sep 17 00:00:00 2001 From: Vegard Wright Date: Mon, 2 Dec 2024 13:48:12 +0100 Subject: [PATCH 13/15] Update frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts Co-authored-by: Martin Gunnerud --- frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts index 21df039b..828e1b9c 100644 --- a/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts +++ b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts @@ -30,7 +30,7 @@ async function prepareSystemUserRequest(api: ApiRequests, tokenclass: Token) { 'altinn:authentication/systemuser.request.read altinn:authentication/systemuser.request.write'; const token = await tokenclass.getEnterpriseAltinnToken(scopes); const endpoint = 'v1/systemuser/request/vendor'; - const apiResponse = await api.sendPostRequest(payload, endpoint, token); + const apiResponse = await api.sendPostRequest<{ confirmUrl: string }>(payload, endpoint, token); return apiResponse.confirmUrl; // Return the Confirmation URL to use in the test } From 61f31a1111e98c1f5fec0c93d4552e4beea00c8d Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Mon, 2 Dec 2024 15:09:03 +0100 Subject: [PATCH 14/15] pr feedback, use specific testdata made for e2e playwright tests --- .../e2eTests/approveSystemUserRequest.spec.ts | 3 ++- frontend/playwright/e2eTests/deleteSystemUser.spec.ts | 6 ++++-- .../playwright/e2eTests/selectSystemVendor.spec.ts | 10 +++------- frontend/playwright/pages/systemUserPage.ts | 6 +++--- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts index 828e1b9c..0f383c59 100644 --- a/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts +++ b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts @@ -20,8 +20,9 @@ test('Godkjenn Systembrukerforespørsel', async ({ page }): Promise => { await page.goto(confirmUrl); await page.getByRole('button', { name: 'Godkjenn' }).click(); - const logoutButton = page.locator('span.sr-only', { hasText: 'Logg inn/Min profil' }); + const logoutButton = page.locator('span', { hasText: 'Logg inn/Min profil' }); await expect(logoutButton).toBeVisible(); + await expect(page).toHaveURL('https://info.altinn.no'); }); async function prepareSystemUserRequest(api: ApiRequests, tokenclass: Token) { diff --git a/frontend/playwright/e2eTests/deleteSystemUser.spec.ts b/frontend/playwright/e2eTests/deleteSystemUser.spec.ts index 12a7fa79..bf3dbe34 100644 --- a/frontend/playwright/e2eTests/deleteSystemUser.spec.ts +++ b/frontend/playwright/e2eTests/deleteSystemUser.spec.ts @@ -4,11 +4,13 @@ import { SystemUserPage } from '../pages/systemUserPage'; test('Delete created system user', async ({ page }): Promise => { const systemUserPage = new SystemUserPage(page); + const system = 'E2E - Playwright - Authentication'; + // Create new system user - await systemUserPage.selectSystemVendor('TripletexN/A (914286018)'); + await systemUserPage.selectSystem(system); await systemUserPage.CREATE_SYSTEM_USER_BUTTON.click(); await expect(systemUserPage.SYSTEMUSER_CREATED_HEADING).toBeVisible(); - await expect(page.getByText('Tripletex').first()).toBeVisible(); + await expect(page.getByText(system).first()).toBeVisible(); // Delete system user await systemUserPage.EDIT_SYSTEMUSER_LINK.click(); diff --git a/frontend/playwright/e2eTests/selectSystemVendor.spec.ts b/frontend/playwright/e2eTests/selectSystemVendor.spec.ts index 48c2f277..b63d3ae7 100644 --- a/frontend/playwright/e2eTests/selectSystemVendor.spec.ts +++ b/frontend/playwright/e2eTests/selectSystemVendor.spec.ts @@ -1,15 +1,11 @@ import { expect, test } from '@playwright/test'; import { SystemUserPage } from '../pages/systemUserPage'; -test('should be able to select a system vendor', async ({ page }): Promise => { - const systemUserPage = new SystemUserPage(page); - await systemUserPage.selectSystemVendor('TripletexN/A (914286018)'); -}); - test('Create system user and verify landing page', async ({ page }): Promise => { + const system = 'E2E - Playwright - Authentication'; const systemUserPage = new SystemUserPage(page); - await systemUserPage.selectSystemVendor('TripletexN/A (914286018)'); + await systemUserPage.selectSystem(system); await systemUserPage.CREATE_SYSTEM_USER_BUTTON.click(); await expect(systemUserPage.SYSTEMUSER_CREATED_HEADING).toBeVisible(); - await expect(page.getByText('Tripletex').first()).toBeVisible(); + await expect(page.getByText(system).first()).toBeVisible(); }); diff --git a/frontend/playwright/pages/systemUserPage.ts b/frontend/playwright/pages/systemUserPage.ts index 52e06e02..d4f3934e 100644 --- a/frontend/playwright/pages/systemUserPage.ts +++ b/frontend/playwright/pages/systemUserPage.ts @@ -45,10 +45,10 @@ export class SystemUserPage { }); } - async selectSystemVendor(vendorSystem: string) { + async selectSystem(system: string) { await this.page.goto(`${process.env.SYSYEMUSER_URL}`); - await this.SELECT_VENDOR_LABEL.fill(vendorSystem.substring(0, 9)); - await this.page.getByLabel(vendorSystem, { exact: true }).click(); + await this.SELECT_VENDOR_LABEL.fill(system); + await this.page.getByLabel(system).click(); await this.CONTINUE_BUTTON.click(); await expect(this.CREATE_SYSTEM_USER_BUTTON).toBeVisible(); From a9616bdfeade4af27b82b1de3b4b52a9f8ad34ad Mon Sep 17 00:00:00 2001 From: Vegard Nyeng Date: Wed, 4 Dec 2024 15:49:21 +0100 Subject: [PATCH 15/15] pr feedback + added at22 to test environments that run daily --- .github/workflows/scheduled-playwright.yml | 2 +- .../e2eTests/approveSystemUserRequest.spec.ts | 79 +++++++++++-------- .../e2eTests/deleteSystemUser.spec.ts | 1 - frontend/playwright/pages/loginPage.ts | 8 +- 4 files changed, 52 insertions(+), 38 deletions(-) diff --git a/.github/workflows/scheduled-playwright.yml b/.github/workflows/scheduled-playwright.yml index d35e4441..21c1feb2 100644 --- a/.github/workflows/scheduled-playwright.yml +++ b/.github/workflows/scheduled-playwright.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - environment: [TT02] + environment: [AT22, TT02] secrets: USERNAME_TEST_API: ${{ secrets.USERNAME_TEST_API }} PASSWORD_TEST_API: ${{ secrets.PASSWORD_TEST_API }} diff --git a/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts index 0f383c59..c4f602ac 100644 --- a/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts +++ b/frontend/playwright/e2eTests/approveSystemUserRequest.spec.ts @@ -1,38 +1,49 @@ import test, { expect } from '@playwright/test'; import { ApiRequests } from '../api-requests/ApiRequests'; import { Token } from 'playwright/api-requests/Token'; +import { LoginWithUserPage } from 'playwright/pages/loginPage'; -test('Avvis Systembrukerforespørsel', async ({ page }): Promise => { - const api = new ApiRequests(); // Create an instance - const tokenclass = new Token(); - const confirmUrl = await prepareSystemUserRequest(api, tokenclass); - - await page.goto(confirmUrl); - await page.getByRole('button', { name: 'Avvis' }).click(); - const logoutButton = page.locator('span.sr-only', { hasText: 'Logg inn/Min profil' }); - await expect(logoutButton).toBeVisible(); -}); - -test('Godkjenn Systembrukerforespørsel', async ({ page }): Promise => { - const api = new ApiRequests(); // Create an instance - const tokenclass = new Token(); - const confirmUrl = await prepareSystemUserRequest(api, tokenclass); - - await page.goto(confirmUrl); - await page.getByRole('button', { name: 'Godkjenn' }).click(); - const logoutButton = page.locator('span', { hasText: 'Logg inn/Min profil' }); - await expect(logoutButton).toBeVisible(); - await expect(page).toHaveURL('https://info.altinn.no'); -}); - -async function prepareSystemUserRequest(api: ApiRequests, tokenclass: Token) { - const payload = api.generatePayloadSystemUserRequest(); - const scopes = - 'altinn:authentication/systemuser.request.read altinn:authentication/systemuser.request.write'; - const token = await tokenclass.getEnterpriseAltinnToken(scopes); - const endpoint = 'v1/systemuser/request/vendor'; - const apiResponse = await api.sendPostRequest<{ confirmUrl: string }>(payload, endpoint, token); - return apiResponse.confirmUrl; // Return the Confirmation URL to use in the test -} - -//Lag ny test: https://authn.ui.at22.altinn.cloud/authfront/ui/auth/creation - Lag ny systemtilgang +test.describe('Godkjenn og avvis Systembrukerforespørsel', () => { + let token: Token; + let loginPage: LoginWithUserPage; + let api: ApiRequests; + + test.beforeEach(async ({ page }) => { + api = new ApiRequests(); + token = new Token(); + loginPage = new LoginWithUserPage(page); + }); + + test('Avvis Systembrukerforespørsel', async ({ page }): Promise => { + //Generate confirmUrl from API + const confirmUrl = await prepareSystemUserRequest(api, token); + + await page.goto(confirmUrl); + await page.getByRole('button', { name: 'Avvis' }).click(); + + //Expect user to be logged out + await expect(loginPage.LOGIN_BUTTON).toBeVisible(); + await expect(page).toHaveURL('https://info.altinn.no'); + }); + + test('Godkjenn Systembrukerforespørsel', async ({ page }): Promise => { + const confirmUrl = await prepareSystemUserRequest(api, token); + + await page.goto(confirmUrl); + await page.getByRole('button', { name: 'Godkjenn' }).click(); + + //Expect user to be logged out + await expect(loginPage.LOGIN_BUTTON).toBeVisible(); + await expect(page).toHaveURL('https://info.altinn.no'); + }); + + async function prepareSystemUserRequest(api: ApiRequests, tokenclass: Token) { + const payload = api.generatePayloadSystemUserRequest(); + const scopes = + 'altinn:authentication/systemuser.request.read altinn:authentication/systemuser.request.write'; + const token = await tokenclass.getEnterpriseAltinnToken(scopes); + const endpoint = 'v1/systemuser/request/vendor'; + const apiResponse = await api.sendPostRequest<{ confirmUrl: string }>(payload, endpoint, token); + return apiResponse.confirmUrl; // Return the Confirmation URL to use in the test + } +}); \ No newline at end of file diff --git a/frontend/playwright/e2eTests/deleteSystemUser.spec.ts b/frontend/playwright/e2eTests/deleteSystemUser.spec.ts index bf3dbe34..4f0e3f88 100644 --- a/frontend/playwright/e2eTests/deleteSystemUser.spec.ts +++ b/frontend/playwright/e2eTests/deleteSystemUser.spec.ts @@ -3,7 +3,6 @@ import { SystemUserPage } from '../pages/systemUserPage'; test('Delete created system user', async ({ page }): Promise => { const systemUserPage = new SystemUserPage(page); - const system = 'E2E - Playwright - Authentication'; // Create new system user diff --git a/frontend/playwright/pages/loginPage.ts b/frontend/playwright/pages/loginPage.ts index c9933cd7..ba7e978e 100644 --- a/frontend/playwright/pages/loginPage.ts +++ b/frontend/playwright/pages/loginPage.ts @@ -1,9 +1,13 @@ -import { type Page, expect } from '@playwright/test'; +import { Locator, type Page, expect } from '@playwright/test'; const authFile = '.playwright/.auth/user.json'; export class LoginWithUserPage { - constructor(public page: Page) {} + public readonly LOGIN_BUTTON: Locator; + + constructor(public page: Page) { + this.LOGIN_BUTTON = page.locator('span', { hasText: 'Logg inn/Min profil' }); + } async loginAndChooseReportee(testUser: string, reportee: string) { await this.page.goto(`${process.env.BASE_URL}`);