From 3498c6c30ef6eeac0ffaa837cd228962641d1d80 Mon Sep 17 00:00:00 2001 From: WilliamThorenfeldt <133344438+WilliamThorenfeldt@users.noreply.github.com> Date: Mon, 5 Feb 2024 19:18:09 +0100 Subject: [PATCH 1/7] 12138 playwright for settings modal (#12196) * Moving all e2e tests from Cypress to Playwright * initial commit * initial commit * creating overview page * fixing typo * fixing feedback from PR * almost implementing all tests * Finalinsing navigation Playwright test * fix cypress * fix * fixing cypress * initial commit * implementing header class * adding more * adding more gitea and header logic * adding functionality for downloading repo * Finalising Git Cypress * initial commit * Completing SettingsModal Playwright tests * fixing unit tests * adding more delay to playwright tests * fixing feedback on PR * fixing broken tests * fixing update org * merge master * deleting old cypress * fixing feedback from PR --- .../SettingsModalButton.test.tsx | 4 +- .../SettingsModalButton.tsx | 2 +- .../src/integration/studio/settingsModal.js | 75 ---------- .../cypress/src/selectors/accessControlTab.js | 17 --- .../src/selectors/administrationTab.js | 7 - .../cypress/src/selectors/policyEditorTab.js | 8 -- .../cypress/src/selectors/settingsTab.js | 16 --- .../testing/playwright/components/Header.ts | 4 + .../playwright/components/PolicyEditor.ts | 33 +++++ .../playwright/components/SettingsModal.ts | 58 ++++++++ frontend/testing/playwright/enum/AppNames.ts | 1 + frontend/testing/playwright/enum/TestNames.ts | 1 + .../testing/playwright/playwright.config.ts | 14 +- .../settings-modal/settings-modal.spec.ts | 129 ++++++++++++++++++ 14 files changed, 242 insertions(+), 127 deletions(-) delete mode 100644 frontend/testing/cypress/src/integration/studio/settingsModal.js delete mode 100644 frontend/testing/cypress/src/selectors/accessControlTab.js delete mode 100644 frontend/testing/cypress/src/selectors/administrationTab.js delete mode 100644 frontend/testing/cypress/src/selectors/policyEditorTab.js delete mode 100644 frontend/testing/cypress/src/selectors/settingsTab.js create mode 100644 frontend/testing/playwright/components/PolicyEditor.ts create mode 100644 frontend/testing/playwright/components/SettingsModal.ts create mode 100644 frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts diff --git a/frontend/app-development/layout/SettingsModalButton/SettingsModalButton.test.tsx b/frontend/app-development/layout/SettingsModalButton/SettingsModalButton.test.tsx index 3db79347158..1146acb3a24 100644 --- a/frontend/app-development/layout/SettingsModalButton/SettingsModalButton.test.tsx +++ b/frontend/app-development/layout/SettingsModalButton/SettingsModalButton.test.tsx @@ -43,7 +43,7 @@ describe('SettingsModal', () => { }); expect(modalHeading).not.toBeInTheDocument(); - const button = screen.getByRole('button', { name: textMock('settings_modal.heading') }); + const button = screen.getByRole('button', { name: textMock('sync_header.settings') }); await act(() => user.click(button)); const modalHeadingAfter = screen.getByRole('heading', { @@ -55,7 +55,7 @@ describe('SettingsModal', () => { it('closes the SettingsModal when the modal is closed', async () => { render(); - const button = screen.getByRole('button', { name: textMock('settings_modal.heading') }); + const button = screen.getByRole('button', { name: textMock('sync_header.settings') }); await act(() => user.click(button)); const modalHeading = screen.getByRole('heading', { diff --git a/frontend/app-development/layout/SettingsModalButton/SettingsModalButton.tsx b/frontend/app-development/layout/SettingsModalButton/SettingsModalButton.tsx index 3e691df6ecd..617f1e489de 100644 --- a/frontend/app-development/layout/SettingsModalButton/SettingsModalButton.tsx +++ b/frontend/app-development/layout/SettingsModalButton/SettingsModalButton.tsx @@ -27,7 +27,7 @@ export const SettingsModalButton = ({ org, app }: SettingsModalButtonProps): Rea color='inverted' icon={} > - {t('settings_modal.heading')} + {t('sync_header.settings')} { // Done to prevent API calls to be executed before the modal is open diff --git a/frontend/testing/cypress/src/integration/studio/settingsModal.js b/frontend/testing/cypress/src/integration/studio/settingsModal.js deleted file mode 100644 index 1c1e556a818..00000000000 --- a/frontend/testing/cypress/src/integration/studio/settingsModal.js +++ /dev/null @@ -1,75 +0,0 @@ -/// -/// - -import * as texts from '../../../../../language/src/nb.json'; -import { accessControlTab } from '../../selectors/accessControlTab'; -import { administrationTab } from '../../selectors/administrationTab'; -import { policyEditorTab } from '../../selectors/policyEditorTab'; -import { settingsTab } from '../../selectors/settingsTab'; - -const designerAppId = `${Cypress.env('autoTestUser')}/${Cypress.env('designerAppName')}`; - -context('SettingsModal', () => { - before(() => { - cy.deleteAllApps(Cypress.env('autoTestUser'), Cypress.env('accessToken')); - cy.studioLogin(Cypress.env('autoTestUser'), Cypress.env('autoTestUserPwd')); - cy.createApp(Cypress.env('autoTestUser'), Cypress.env('designerAppName')); - }); - beforeEach(() => { - cy.visit('/dashboard'); - // Navigate to designerApp - cy.visit('/editor/' + designerAppId); - cy.openSettingsModal(); - }); - after(() => { - cy.deleteAllApps(Cypress.env('autoTestUser'), Cypress.env('accessToken')); - }); - - it('is possible to open the settings modal', () => { - cy.findByRole('heading', { name: texts['settings_modal.heading'] }).should('be.visible'); - }); - - it('is possible to close the settings modal', () => { - cy.findByRole('button', { name: texts['modal.close_icon'] }).click(); - cy.findByRole('heading', { name: texts['settings_modal.heading'] }).should('not.exist'); - }); - - it('is possible to see and edit information on About App tab', () => { - administrationTab.getHeader().should('be.visible'); - administrationTab.getAppNameField().clear().type('New app name'); - administrationTab.getAppNameField().invoke('val').should('contain', 'New app name'); - }); - - it('is possible to toggle settings on app settings tab', () => { - settingsTab.getTab().click(); - settingsTab.getHeader().should('be.visible'); - settingsTab.getAutoDelete().should('be.visible'); - settingsTab.getEnableCopyInstance().should('be.visible'); - settingsTab.getHideInInbox().should('be.visible'); - settingsTab.getShowStartedInstances().should('be.visible'); - }); - - it('is possible to load the policy editor tab', () => { - // This test only loads the tab and tests that it loads as expected. - // We should implement a separate test for the poloicy editor. - policyEditorTab.getTab().click(); - policyEditorTab.getHeader().should('be.visible'); - policyEditorTab.getSecurityLevelSelect().should('be.visible'); - }); - - it('is possible to update settings on the access control tab', () => { - accessControlTab.getTab().click(); - accessControlTab.getHeader().should('be.visible'); - accessControlTab.getOrganisationParty().should('be.visible'); - accessControlTab.getPersonParty().should('be.visible'); - accessControlTab.getSubUnitParty().should('be.visible'); - accessControlTab.getBankruptcyParty().should('be.visible').click(); - accessControlTab.getBankruptcyPartyCheckbox().should('be.checked'); - - // Close modal and re-open to confirm data is set as expected - cy.findByRole('button', { name: texts['modal.close_icon'] }).click(); - cy.openSettingsModal(); - accessControlTab.getTab().click(); - accessControlTab.getBankruptcyPartyCheckbox().should('be.checked'); - }); -}); diff --git a/frontend/testing/cypress/src/selectors/accessControlTab.js b/frontend/testing/cypress/src/selectors/accessControlTab.js deleted file mode 100644 index c147c1934d1..00000000000 --- a/frontend/testing/cypress/src/selectors/accessControlTab.js +++ /dev/null @@ -1,17 +0,0 @@ -import * as texts from '@altinn-studio/language/src/nb.json'; - -export const accessControlTab = { - getHeader: () => - cy.findByRole('heading', { name: texts['settings_modal.access_control_tab_heading'] }), - getTab: () => cy.findByText(texts['settings_modal.access_control_tab_heading']), - getBankruptcyParty: () => - cy.findByText(texts['settings_modal.access_control_tab_option_bankruptcy_estate']), - getBankruptcyPartyCheckbox: () => - cy.findByRole('checkbox', { - name: texts['settings_modal.access_control_tab_option_bankruptcy_estate'], - }), - getOrganisationParty: () => - cy.findByText(texts['settings_modal.access_control_tab_option_organisation']), - getPersonParty: () => cy.findByText(texts['settings_modal.access_control_tab_option_person']), - getSubUnitParty: () => cy.findByText(texts['settings_modal.access_control_tab_option_sub_unit']), -}; diff --git a/frontend/testing/cypress/src/selectors/administrationTab.js b/frontend/testing/cypress/src/selectors/administrationTab.js deleted file mode 100644 index 3a68cb833c2..00000000000 --- a/frontend/testing/cypress/src/selectors/administrationTab.js +++ /dev/null @@ -1,7 +0,0 @@ -import * as texts from '@altinn-studio/language/src/nb.json'; - -export const administrationTab = { - getAppNameField: () => - cy.findByRole('textbox', { name: texts['settings_modal.about_tab_name_label'] }), - getHeader: () => cy.findByRole('heading', { name: texts['settings_modal.about_tab_heading'] }), -}; diff --git a/frontend/testing/cypress/src/selectors/policyEditorTab.js b/frontend/testing/cypress/src/selectors/policyEditorTab.js deleted file mode 100644 index 379ac85010f..00000000000 --- a/frontend/testing/cypress/src/selectors/policyEditorTab.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as texts from '@altinn-studio/language/src/nb.json'; - -export const policyEditorTab = { - getHeader: () => cy.findByRole('heading', { name: texts['settings_modal.policy_tab_heading'] }), - getTab: () => cy.findByText(texts['settings_modal.policy_tab_heading']), - getSecurityLevelSelect: () => - cy.findByRole('combobox', { name: texts['policy_editor.select_auth_level_label'] }), -}; diff --git a/frontend/testing/cypress/src/selectors/settingsTab.js b/frontend/testing/cypress/src/selectors/settingsTab.js deleted file mode 100644 index af07a6b2aed..00000000000 --- a/frontend/testing/cypress/src/selectors/settingsTab.js +++ /dev/null @@ -1,16 +0,0 @@ -import * as texts from '@altinn-studio/language/src/nb.json'; - -export const settingsTab = { - getHeader: () => cy.findByRole('heading', { name: texts['settings_modal.setup_tab_heading'] }), - getTab: () => cy.findByText(texts['settings_modal.setup_tab_heading']), - getAutoDelete: () => - cy.findByText(texts['settings_modal.setup_tab_switch_autoDeleteOnProcessEnd']), - getHideInInbox: () => - cy.findByText( - texts['settings_modal.setup_tab_switch_messageBoxConfig_hideSettings_hideAlways'], - ), - getEnableCopyInstance: () => - cy.findByText(texts['settings_modal.setup_tab_switch_copyInstanceSettings_enabled']), - getShowStartedInstances: () => - cy.findByText(texts['settings_modal.setup_tab_switch_onEntry_show']), -}; diff --git a/frontend/testing/playwright/components/Header.ts b/frontend/testing/playwright/components/Header.ts index 8623ded5e16..ed42474b70d 100644 --- a/frontend/testing/playwright/components/Header.ts +++ b/frontend/testing/playwright/components/Header.ts @@ -61,4 +61,8 @@ export class Header extends BasePage { .getByRole('button', { name: this.textMock('sync_header.local_changes') }) .click(); } + + public async clickOnOpenSettingsModalButton(): Promise { + await this.page.getByRole('button', { name: this.textMock('sync_header.settings') }).click(); + } } diff --git a/frontend/testing/playwright/components/PolicyEditor.ts b/frontend/testing/playwright/components/PolicyEditor.ts new file mode 100644 index 00000000000..97a9c2ff69d --- /dev/null +++ b/frontend/testing/playwright/components/PolicyEditor.ts @@ -0,0 +1,33 @@ +import { BasePage } from '../helpers/BasePage'; +import type { Environment } from '../helpers/StudioEnvironment'; +import type { Page } from '@playwright/test'; + +type SecurityLevel = 0 | 1 | 2 | 3 | 4; + +export class PolicyEditor extends BasePage { + constructor(page: Page, environment?: Environment) { + super(page, environment); + } + + public async getSelectedSecurityLevel(): Promise { + return await this.page + .getByRole('combobox', { + name: this.textMock('policy_editor.select_auth_level_label'), + }) + .inputValue(); + } + + public getSecurityLevelByTextByLevel(level: SecurityLevel): string { + return this.textMock(`policy_editor.auth_level_option_${level}`); + } + + public async clickOnSecurityLevelSelect(): Promise { + await this.page.getByLabel(this.textMock('policy_editor.select_auth_level_label')).click(); + } + + public async clickOnSecurityLevelSelectOption(level: SecurityLevel): Promise { + await this.page + .getByRole('option', { name: this.textMock(`policy_editor.auth_level_option_${level}`) }) + .click(); + } +} diff --git a/frontend/testing/playwright/components/SettingsModal.ts b/frontend/testing/playwright/components/SettingsModal.ts new file mode 100644 index 00000000000..2860c875098 --- /dev/null +++ b/frontend/testing/playwright/components/SettingsModal.ts @@ -0,0 +1,58 @@ +import { BasePage } from '../helpers/BasePage'; +import type { Environment } from '../helpers/StudioEnvironment'; +import type { Page } from '@playwright/test'; + +export type SettingsModalTab = 'about' | 'accessControl' | 'policy' | 'setup'; +type SettingsModalTabHeading = 'about' | 'access_control' | 'policy' | 'setup'; + +export class SettingsModal extends BasePage { + constructor(page: Page, environment?: Environment) { + super(page, environment); + } + + public async verifyThatSettingsModalIsOpen(): Promise { + await this.page + .getByRole('heading', { + name: this.textMock('settings_modal.heading'), + level: 1, + }) + .isVisible(); + } + + public async clickOnCloseSettingsModalButton(): Promise { + await this.page.getByRole('button', { name: this.textMock('modal.close_icon') }).click(); + } + + public async verifyThatSettingsModalIsNotOpen(): Promise { + await this.page + .getByRole('heading', { + name: this.textMock('settings_modal.heading'), + level: 1, + }) + .isHidden(); + } + + public async navigateToTab(tab: SettingsModalTab): Promise { + await this.page + .getByRole('tab', { name: this.textMock(`settings_modal.left_nav_tab_${tab}`) }) + .click(); + } + + public async verifyThatTabIsVisible(tabHeading: SettingsModalTabHeading): Promise { + await this.page + .getByRole('heading', { + name: this.textMock(`settings_modal.${tabHeading}_tab_heading`), + level: 2, + }) + .isVisible(); + } + + public async verifyThatTabIsHidden(tabHeading: SettingsModalTabHeading): Promise { + await this.page + .getByRole('heading', { + name: this.textMock(`settings_modal.${tabHeading}_tab_heading`), + level: 2, + }) + .isHidden(); + } +} diff --git a/frontend/testing/playwright/enum/AppNames.ts b/frontend/testing/playwright/enum/AppNames.ts index 1721b14cfbb..2c11b95a1a3 100644 --- a/frontend/testing/playwright/enum/AppNames.ts +++ b/frontend/testing/playwright/enum/AppNames.ts @@ -4,5 +4,6 @@ export enum AppNames { DASHBOARD_APP = 'dashboard-app-test', GIT_SYNC_APP = 'git-sync-app-test', MAIN_NAVIGATION_APP = 'navigation-app-test', + SETTINGS_MODAL_APP = 'settings-modal-app-test', UI_EDITOR_APP = 'ui-editor-app-test', } diff --git a/frontend/testing/playwright/enum/TestNames.ts b/frontend/testing/playwright/enum/TestNames.ts index 2cfd654fce8..266747e9f9e 100644 --- a/frontend/testing/playwright/enum/TestNames.ts +++ b/frontend/testing/playwright/enum/TestNames.ts @@ -7,4 +7,5 @@ export enum TestNames { MAIN_NAVIGATION_BETWEEN_SUB_APPS = 'main-navigation-between-sub-apps', UI_EDITOR = 'ui-editor', GIT_SYNC = 'git-sync', + SETTINGS_MODAL = 'settings-modal', } diff --git a/frontend/testing/playwright/playwright.config.ts b/frontend/testing/playwright/playwright.config.ts index 8f0d8ef3b2b..2bdaa95d35a 100644 --- a/frontend/testing/playwright/playwright.config.ts +++ b/frontend/testing/playwright/playwright.config.ts @@ -89,12 +89,23 @@ export default defineConfig({ testMatch: '*.spec.ts', use: { ...devices['Desktop Chrome'], - baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL, storageState: '.playwright/auth/user.json', testAppName: AppNames.UI_EDITOR_APP, headless: true, }, }, + { + name: TestNames.SETTINGS_MODAL, + dependencies: ['setup'], + testDir: './tests/settings-modal/', + testMatch: '*.spec.ts', + use: { + ...devices['Desktop Chrome'], + storageState: '.playwright/auth/user.json', + testAppName: AppNames.SETTINGS_MODAL_APP, + headless: true, + }, + }, { name: TestNames.LOGOUT_AND_INVALID_LOGIN_ONLY, // Add ALL other test names here to make sure that the log out test is the last test to be executed @@ -106,6 +117,7 @@ export default defineConfig({ TestNames.MAIN_NAVIGATION_BETWEEN_SUB_APPS, TestNames.GIT_SYNC, TestNames.UI_EDITOR, + TestNames.SETTINGS_MODAL, ], testDir: './tests/logout-and-invalid-login-only/', testMatch: '*.spec.ts', diff --git a/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts b/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts new file mode 100644 index 00000000000..935e11842c3 --- /dev/null +++ b/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts @@ -0,0 +1,129 @@ +import { expect } from '@playwright/test'; +import type { Page } from '@playwright/test'; +import { test } from '../../extenders/testExtend'; +import { DesignerApi } from '../../helpers/DesignerApi'; +import type { StorageState } from '../../types/StorageState'; +import { Header } from '../../components/Header'; +import { UiEditorPage } from '../../pages/UiEditorPage'; +import { SettingsModal } from '../../components/SettingsModal'; +import type { SettingsModalTab } from '../../components/SettingsModal'; +import { PolicyEditor } from '../../components/PolicyEditor'; +import { Gitea } from 'testing/playwright/helpers/Gitea'; + +// This line must be there to ensure that the tests do not run in parallell, and +// that the before all call is being executed before we start the tests +test.describe.configure({ mode: 'serial' }); + +// Before the tests starts, we need to create the data model app +test.beforeAll(async ({ testAppName, request, storageState }) => { + // Create a new app + const designerApi = new DesignerApi({ app: testAppName }); + const response = await designerApi.createApp(request, storageState as StorageState); + expect(response.ok()).toBeTruthy(); +}); + +test.afterAll(async ({ request, testAppName }) => { + const gitea = new Gitea(); + const response = await request.delete(gitea.getDeleteAppEndpoint({ app: testAppName })); + expect(response.ok()).toBeTruthy(); +}); + +const setupAndVerifyUiEditorPage = async (page: Page, testAppName: string): Promise => { + const uiEditorPage = new UiEditorPage(page, { app: testAppName }); + await uiEditorPage.loadUiEditorPage(); + await uiEditorPage.verifyUiEditorPage(); +}; + +const setUpAndOpenSettingsModal = async ( + page: Page, + testAppName: string, + tabToStartAt: SettingsModalTab = 'about', +): Promise => { + const settingsModal = new SettingsModal(page, { app: testAppName }); + const header = new Header(page, { app: testAppName }); + + await setupAndVerifyUiEditorPage(page, testAppName); + + await header.clickOnOpenSettingsModalButton(); + await settingsModal.verifyThatSettingsModalIsOpen(); + + if (tabToStartAt !== 'about') { + settingsModal.navigateToTab(tabToStartAt); + } + + return settingsModal; +}; + +test('That it is possible to change tab from "About app" tab to "Setup" tab', async ({ + page, + testAppName, +}) => { + const settingsModal = await setUpAndOpenSettingsModal(page, testAppName); + + await settingsModal.verifyThatTabIsVisible('about'); + await settingsModal.verifyThatTabIsHidden('setup'); + + await settingsModal.navigateToTab('setup'); + + await settingsModal.verifyThatTabIsHidden('about'); + await settingsModal.verifyThatTabIsVisible('setup'); +}); + +test('That it is possible to change tab to "Policy editor" tab', async ({ page, testAppName }) => { + const settingsModal = await setUpAndOpenSettingsModal(page, testAppName, 'setup'); + + await settingsModal.verifyThatTabIsVisible('setup'); + await settingsModal.verifyThatTabIsHidden('policy'); + + await settingsModal.navigateToTab('policy'); + + await settingsModal.verifyThatTabIsHidden('setup'); + await settingsModal.verifyThatTabIsVisible('policy'); +}); + +test('That it is possible to edit security level on "Policy editor" tab, and that changes are saved', async ({ + page, + testAppName, +}) => { + const settingsModal = await setUpAndOpenSettingsModal(page, testAppName, 'policy'); + await settingsModal.verifyThatTabIsVisible('policy'); + + const policyEditor = new PolicyEditor(page, { app: testAppName }); + + const securityLevel2 = 2; + const securityLevel2Text = policyEditor.getSecurityLevelByTextByLevel(securityLevel2); + expect(await policyEditor.getSelectedSecurityLevel()).toEqual(securityLevel2Text); + + await policyEditor.clickOnSecurityLevelSelect(); + await policyEditor.clickOnSecurityLevelSelectOption(3); + + const securityLevel3 = 3; + const securityLevel3Text = policyEditor.getSecurityLevelByTextByLevel(securityLevel3); + expect(await policyEditor.getSelectedSecurityLevel()).toEqual(securityLevel3Text); + + await settingsModal.navigateToTab('about'); + await settingsModal.verifyThatTabIsVisible('about'); + await settingsModal.verifyThatTabIsHidden('policy'); + + await settingsModal.navigateToTab('policy'); + expect(await policyEditor.getSelectedSecurityLevel()).toEqual(securityLevel3Text); +}); + +test('That it is possible to change tab to "Access control" tab', async ({ page, testAppName }) => { + const settingsModal = await setUpAndOpenSettingsModal(page, testAppName, 'policy'); + + await settingsModal.verifyThatTabIsVisible('policy'); + await settingsModal.verifyThatTabIsHidden('access_control'); + + await settingsModal.navigateToTab('accessControl'); + + await settingsModal.verifyThatTabIsHidden('policy'); + await settingsModal.verifyThatTabIsVisible('access_control'); +}); + +test('That it is possible to close the settings modal', async ({ page, testAppName }) => { + const settingsModal = await setUpAndOpenSettingsModal(page, testAppName); + + await settingsModal.clickOnCloseSettingsModalButton(); + await settingsModal.verifyThatSettingsModalIsNotOpen(); +}); From b5fb51e5277a11804cefc2168dfe4cd6d011d431 Mon Sep 17 00:00:00 2001 From: Mirko Sekulic Date: Mon, 5 Feb 2024 21:10:37 +0100 Subject: [PATCH 2/7] Proccess file sync (#12246) * Add MediatR package * event and event handlers structure added * fix endpoint and add tests * delete taskName change endpoint --- backend/packagegroups/NuGet.props | 1 + .../Controllers/AppDevelopmentController.cs | 1 + .../Controllers/ProcessModelingController.cs | 88 +++++++++---- backend/src/Designer/Designer.csproj | 1 + ...TaskIdChangedApplicationMetadataHandler.cs | 16 +++ .../Events/ProcessTaskIdChangedEvent.cs | 9 ++ .../Models/Dto/ProcessDefinitionMetadata.cs | 8 ++ .../src/Designer/Models/Dto/TaskIdChange.cs | 7 + .../Dto}/VersionResponse.cs | 2 +- backend/src/Designer/Program.cs | 4 + .../ProcessModeling/ProcessModelingService.cs | 31 ----- .../Interfaces/IProcessModelingService.cs | 10 -- .../GetAppVersionTests.cs | 1 + .../UpdateProcessTaskNameTests.cs | 54 -------- .../UpsertProcessDefinitionAndNotifyTests.cs | 65 +++++++++ .../Services/ProcessModelingServiceTests.cs | 28 ---- testdata/App/config/process/process.bpmn | 124 +++++++++++------- 17 files changed, 251 insertions(+), 199 deletions(-) create mode 100644 backend/src/Designer/EventHandlers/ProcessTaskIdChanged/ProcessTaskIdChangedApplicationMetadataHandler.cs create mode 100644 backend/src/Designer/Events/ProcessTaskIdChangedEvent.cs create mode 100644 backend/src/Designer/Models/Dto/ProcessDefinitionMetadata.cs create mode 100644 backend/src/Designer/Models/Dto/TaskIdChange.cs rename backend/src/Designer/{ViewModels/Response => Models/Dto}/VersionResponse.cs (87%) delete mode 100644 backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpdateProcessTaskNameTests.cs create mode 100644 backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpsertProcessDefinitionAndNotifyTests.cs diff --git a/backend/packagegroups/NuGet.props b/backend/packagegroups/NuGet.props index 80caac4ea61..d4964c9ce35 100644 --- a/backend/packagegroups/NuGet.props +++ b/backend/packagegroups/NuGet.props @@ -37,6 +37,7 @@ + diff --git a/backend/src/Designer/Controllers/AppDevelopmentController.cs b/backend/src/Designer/Controllers/AppDevelopmentController.cs index 5fbc871c510..ddff9e403d3 100644 --- a/backend/src/Designer/Controllers/AppDevelopmentController.cs +++ b/backend/src/Designer/Controllers/AppDevelopmentController.cs @@ -10,6 +10,7 @@ using Altinn.Studio.Designer.Helpers; using Altinn.Studio.Designer.Infrastructure.GitRepository; using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Models.Dto; using Altinn.Studio.Designer.Services.Interfaces; using Altinn.Studio.Designer.ViewModels.Response; using Microsoft.AspNetCore.Authorization; diff --git a/backend/src/Designer/Controllers/ProcessModelingController.cs b/backend/src/Designer/Controllers/ProcessModelingController.cs index 94361b99ff2..8a3e237ed13 100644 --- a/backend/src/Designer/Controllers/ProcessModelingController.cs +++ b/backend/src/Designer/Controllers/ProcessModelingController.cs @@ -2,11 +2,15 @@ using System.Collections.Generic; using System.IO; using System.Net.Mime; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Altinn.Studio.Designer.Events; using Altinn.Studio.Designer.Helpers; using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Models.Dto; using Altinn.Studio.Designer.Services.Interfaces; +using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -23,9 +27,12 @@ namespace Altinn.Studio.Designer.Controllers public class ProcessModelingController : ControllerBase { private readonly IProcessModelingService _processModelingService; - public ProcessModelingController(IProcessModelingService processModelingService) + private readonly IMediator _mediator; + + public ProcessModelingController(IProcessModelingService processModelingService, IMediator mediator) { _processModelingService = processModelingService; + _mediator = mediator; } [HttpGet("process-definition")] @@ -33,13 +40,17 @@ public FileStreamResult GetProcessDefinition(string org, string repo) { string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); - Stream processDefinitionStream = _processModelingService.GetProcessDefinitionStream(AltinnRepoEditingContext.FromOrgRepoDeveloper(org, repo, developer)); + Stream processDefinitionStream = + _processModelingService.GetProcessDefinitionStream( + AltinnRepoEditingContext.FromOrgRepoDeveloper(org, repo, developer)); return new FileStreamResult(processDefinitionStream, MediaTypeNames.Text.Plain); } [HttpPut("process-definition")] - public async Task SaveProcessDefinition(string org, string repo, CancellationToken cancellationToken) + [Obsolete("This endpoint should be replaced by process-definition-latest, and url fixed after integration with frontend")] + public async Task SaveProcessDefinition(string org, string repo, + CancellationToken cancellationToken) { Request.EnableBuffering(); try @@ -52,7 +63,48 @@ public async Task SaveProcessDefinition(string org, string repo, } string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); - await _processModelingService.SaveProcessDefinitionAsync(AltinnRepoEditingContext.FromOrgRepoDeveloper(org, repo, developer), Request.Body, cancellationToken); + await _processModelingService.SaveProcessDefinitionAsync( + AltinnRepoEditingContext.FromOrgRepoDeveloper(org, repo, developer), Request.Body, cancellationToken); + return Ok(); + } + + [HttpPut("process-definition-latest")] + public async Task UpsertProcessDefinitionAndNotify(string org, string repo, [FromForm] IFormFile content, [FromForm] string metadata, CancellationToken cancellationToken) + { + Request.EnableBuffering(); + + var metadataObject = metadata is not null + ? JsonSerializer.Deserialize(metadata, + new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }) + : null; + + Stream stream = content.OpenReadStream(); + + try + { + await Guard.AssertValidXmlStreamAndRewindAsync(stream); + } + catch (ArgumentException) + { + return BadRequest("BPMN file is not valid XML"); + } + + string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); + await _processModelingService.SaveProcessDefinitionAsync( + AltinnRepoEditingContext.FromOrgRepoDeveloper(org, repo, developer), stream, cancellationToken); + + if (metadataObject?.TaskIdChanges is not null) + { + foreach (TaskIdChange taskIdChange in metadataObject.TaskIdChanges) + { + await _mediator.Publish(new ProcessTaskIdChangedEvent + { + OldId = taskIdChange.OldId, + NewId = taskIdChange.NewId + }, cancellationToken); + } + } + return Ok(); } @@ -64,37 +116,17 @@ public IEnumerable GetTemplates(string org, string repo, SemanticVersion } [HttpPut("templates/{appVersion}/{templateName}")] - public async Task SaveProcessDefinitionFromTemplate(string org, string repo, SemanticVersion appVersion, string templateName, CancellationToken cancellationToken) + public async Task SaveProcessDefinitionFromTemplate(string org, string repo, + SemanticVersion appVersion, string templateName, CancellationToken cancellationToken) { Guard.AssertArgumentNotNull(appVersion, nameof(appVersion)); string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); var editingContext = AltinnRepoEditingContext.FromOrgRepoDeveloper(org, repo, developer); - await _processModelingService.SaveProcessDefinitionFromTemplateAsync(editingContext, templateName, appVersion, cancellationToken); + await _processModelingService.SaveProcessDefinitionFromTemplateAsync(editingContext, templateName, + appVersion, cancellationToken); Stream processDefinitionStream = _processModelingService.GetProcessDefinitionStream(editingContext); return new FileStreamResult(processDefinitionStream, MediaTypeNames.Text.Plain); } - - [HttpPut("tasks/{taskId}/{taskName}")] - public async Task UpdateProcessTaskName(string org, string repo, string taskId, string taskName, CancellationToken cancellationToken) - { - Guard.AssertArgumentNotNull(taskId, nameof(taskId)); - Guard.AssertArgumentNotNull(taskName, nameof(taskName)); - string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); - var editingContext = AltinnRepoEditingContext.FromOrgRepoDeveloper(org, repo, developer); - try - { - Stream updatedProcessDefinitionStream = await _processModelingService.UpdateProcessTaskNameAsync(editingContext, taskId, taskName, cancellationToken); - return new FileStreamResult(updatedProcessDefinitionStream, MediaTypeNames.Text.Plain); - } - catch (InvalidOperationException) - { - return BadRequest("Could not deserialize process definition."); - } - catch (ArgumentException) - { - return BadRequest("Could not find task with given id."); - } - } } } diff --git a/backend/src/Designer/Designer.csproj b/backend/src/Designer/Designer.csproj index 884fbe72545..65ddc481520 100644 --- a/backend/src/Designer/Designer.csproj +++ b/backend/src/Designer/Designer.csproj @@ -29,6 +29,7 @@ + diff --git a/backend/src/Designer/EventHandlers/ProcessTaskIdChanged/ProcessTaskIdChangedApplicationMetadataHandler.cs b/backend/src/Designer/EventHandlers/ProcessTaskIdChanged/ProcessTaskIdChangedApplicationMetadataHandler.cs new file mode 100644 index 00000000000..369231a9d58 --- /dev/null +++ b/backend/src/Designer/EventHandlers/ProcessTaskIdChanged/ProcessTaskIdChangedApplicationMetadataHandler.cs @@ -0,0 +1,16 @@ +using System.Threading; +using System.Threading.Tasks; +using Altinn.Studio.Designer.Events; +using MediatR; + +namespace Altinn.Studio.Designer.EventHandlers.ProcessTaskIdChanged; + +public class ProcessTaskIdChangedApplicationMetadataHandler : INotificationHandler +{ + public Task Handle(ProcessTaskIdChangedEvent notification, CancellationToken cancellationToken) + { + // TODO: Implement logic to handle the event here: https://github.com/Altinn/altinn-studio/issues/12220 + // Here we should think how to handle errors in the handlers. Should we throw exceptions or use websocket to send error messages to the client? + return Task.CompletedTask; + } +} diff --git a/backend/src/Designer/Events/ProcessTaskIdChangedEvent.cs b/backend/src/Designer/Events/ProcessTaskIdChangedEvent.cs new file mode 100644 index 00000000000..24434c39be2 --- /dev/null +++ b/backend/src/Designer/Events/ProcessTaskIdChangedEvent.cs @@ -0,0 +1,9 @@ +using MediatR; + +namespace Altinn.Studio.Designer.Events; + +public class ProcessTaskIdChangedEvent : INotification +{ + public string OldId { get; set; } + public string NewId { get; set; } +} diff --git a/backend/src/Designer/Models/Dto/ProcessDefinitionMetadata.cs b/backend/src/Designer/Models/Dto/ProcessDefinitionMetadata.cs new file mode 100644 index 00000000000..1e91b1c4a01 --- /dev/null +++ b/backend/src/Designer/Models/Dto/ProcessDefinitionMetadata.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Altinn.Studio.Designer.Models.Dto; + +public class ProcessDefinitionMetadata +{ + public List TaskIdChanges { get; set; } +} diff --git a/backend/src/Designer/Models/Dto/TaskIdChange.cs b/backend/src/Designer/Models/Dto/TaskIdChange.cs new file mode 100644 index 00000000000..daede977fc6 --- /dev/null +++ b/backend/src/Designer/Models/Dto/TaskIdChange.cs @@ -0,0 +1,7 @@ +namespace Altinn.Studio.Designer.Models.Dto; + +public class TaskIdChange +{ + public string OldId { get; set; } + public string NewId { get; set; } +} diff --git a/backend/src/Designer/ViewModels/Response/VersionResponse.cs b/backend/src/Designer/Models/Dto/VersionResponse.cs similarity index 87% rename from backend/src/Designer/ViewModels/Response/VersionResponse.cs rename to backend/src/Designer/Models/Dto/VersionResponse.cs index 4befb1b4ffc..d417038e063 100644 --- a/backend/src/Designer/ViewModels/Response/VersionResponse.cs +++ b/backend/src/Designer/Models/Dto/VersionResponse.cs @@ -1,6 +1,6 @@ using NuGet.Versioning; -namespace Altinn.Studio.Designer.ViewModels.Response +namespace Altinn.Studio.Designer.Models.Dto { public class VersionResponse { diff --git a/backend/src/Designer/Program.cs b/backend/src/Designer/Program.cs index e3258c0a571..446b9e34d13 100644 --- a/backend/src/Designer/Program.cs +++ b/backend/src/Designer/Program.cs @@ -249,6 +249,10 @@ void ConfigureServices(IServiceCollection services, IConfiguration configuration // Auto register all settings classes services.RegisterSettingsByBaseType(configuration); + + // Registers all handlers and the mediator + services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)); + logger.LogInformation("// Program.cs // ConfigureServices // Configuration complete"); } diff --git a/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs b/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs index 71dc3035a61..708ca63d278 100644 --- a/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs +++ b/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs @@ -56,37 +56,6 @@ public Stream GetProcessDefinitionStream(AltinnRepoEditingContext altinnRepoEdit return altinnAppGitRepository.GetProcessDefinitionFile(); } - public async Task UpdateProcessTaskNameAsync(AltinnRepoEditingContext altinnRepoEditingContext, string taskId, string taskName, CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - AltinnAppGitRepository altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository(altinnRepoEditingContext.Org, altinnRepoEditingContext.Repo, altinnRepoEditingContext.Developer); - XmlSerializer serializer = new(typeof(Definitions)); - Definitions? definitions; - using (Stream processDefinitionStream = GetProcessDefinitionStream(altinnRepoEditingContext)) - { - definitions = (Definitions?)serializer.Deserialize(processDefinitionStream); - } - - if (definitions == null) - { - throw new InvalidOperationException("Could not deserialize process definition."); - } - - ProcessTask? processTask = (definitions.Process.Tasks?.FirstOrDefault(t => t.Id == taskId)) ?? throw new ArgumentException($"Could not find task with id {taskId}."); - processTask.Name = taskName; - - Stream processStream = new MemoryStream(); - serializer.Serialize(processStream, definitions); - - // Reset stream position to beginning after serialization - processStream.Seek(0, SeekOrigin.Begin); - await altinnAppGitRepository.SaveProcessDefinitionFileAsync(processStream, cancellationToken); - - // Reset stream position to beginning after saving - processStream.Seek(0, SeekOrigin.Begin); - return processStream; - } - private IEnumerable EnumerateTemplateResources(SemanticVersion version) { return typeof(ProcessModelingService).Assembly.GetManifestResourceNames() diff --git a/backend/src/Designer/Services/Interfaces/IProcessModelingService.cs b/backend/src/Designer/Services/Interfaces/IProcessModelingService.cs index cc5e61db789..85d38e079cb 100644 --- a/backend/src/Designer/Services/Interfaces/IProcessModelingService.cs +++ b/backend/src/Designer/Services/Interfaces/IProcessModelingService.cs @@ -39,15 +39,5 @@ public interface IProcessModelingService /// An . /// A of a process definition file. Stream GetProcessDefinitionStream(AltinnRepoEditingContext altinnRepoEditingContext); - - /// - /// Updates the name of a task in the process definition file. - /// - /// n . - /// The ID of the task to update - /// The name to set for the task - /// A that observes if operation is cancelled. - /// - Task UpdateProcessTaskNameAsync(AltinnRepoEditingContext altinnRepoEditingContext, string taskId, string taskName, CancellationToken cancellationToken = default); } } diff --git a/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/GetAppVersionTests.cs b/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/GetAppVersionTests.cs index 05cca89dd7f..fe0f13d1067 100644 --- a/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/GetAppVersionTests.cs +++ b/backend/tests/Designer.Tests/Controllers/AppDevelopmentController/GetAppVersionTests.cs @@ -5,6 +5,7 @@ using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Altinn.Studio.Designer.Models.Dto; using Altinn.Studio.Designer.ViewModels.Response; using Designer.Tests.Controllers.ApiTests; using Designer.Tests.Utils; diff --git a/backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpdateProcessTaskNameTests.cs b/backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpdateProcessTaskNameTests.cs deleted file mode 100644 index f2ae3fb0fa2..00000000000 --- a/backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpdateProcessTaskNameTests.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Net.Mime; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using Designer.Tests.Controllers.ApiTests; -using Designer.Tests.Utils; -using FluentAssertions; -using Microsoft.AspNetCore.Mvc.Testing; -using Xunit; - -namespace Designer.Tests.Controllers.ProcessModelingController -{ - public class UpdateProcessTaskName : DisagnerEndpointsTestsBase, IClassFixture> - { - private static string Url(string org, string repository, string taskId, string taskName) => $"/designer/api/{org}/{repository}/process-modelling/tasks/{taskId}/{taskName}"; - - public UpdateProcessTaskName(WebApplicationFactory factory) : base(factory) - { - } - - [Theory] - [InlineData("ttd", "app-with-process", "testUser", "Task_1", "NewTaskName")] - public async Task UpdateProcessTaskName_ShouldReturnUpdatedProcess(string org, string app, string developer, string taskId, string taskName) - { - string targetRepository = TestDataHelper.GenerateTestRepoName(); - await CopyRepositoryForTest(org, app, developer, targetRepository); - - string url = Url(org, targetRepository, taskId, taskName); - - using var response = await HttpClient.PutAsync(url, null); - response.StatusCode.Should().Be(HttpStatusCode.OK); - - string responseContent = await response.Content.ReadAsStringAsync(); - - responseContent.Should().NotBeNullOrEmpty(); - responseContent.Should().Contain(taskName); - } - - [Theory] - [InlineData("ttd", "app-with-process", "testUser", "Does_not_exist", "NewTaskName")] - public async Task InvalidTaskId_ShouldReturnBadRequest(string org, string app, string developer, string taskId, string taskName) - { - string targetRepository = TestDataHelper.GenerateTestRepoName(); - await CopyRepositoryForTest(org, app, developer, targetRepository); - - string url = Url(org, targetRepository, taskId, taskName); - - using var response = await HttpClient.PutAsync(url, null); - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - } - } -} diff --git a/backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpsertProcessDefinitionAndNotifyTests.cs b/backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpsertProcessDefinitionAndNotifyTests.cs new file mode 100644 index 00000000000..8cc6d87dbad --- /dev/null +++ b/backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpsertProcessDefinitionAndNotifyTests.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Mime; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using System.Xml.Linq; +using Altinn.Studio.Designer.Models.Dto; +using Designer.Tests.Controllers.ApiTests; +using Designer.Tests.Utils; +using FluentAssertions; +using Microsoft.AspNetCore.Mvc.Testing; +using SharedResources.Tests; +using Xunit; + +namespace Designer.Tests.Controllers.ProcessModelingController; + +public class UpsertProcessDefinitionAndNotifyTests : DisagnerEndpointsTestsBase, IClassFixture> +{ + private static string VersionPrefix(string org, string repository) => $"/designer/api/{org}/{repository}/process-modelling/process-definition-latest"; + + public UpsertProcessDefinitionAndNotifyTests(WebApplicationFactory factory) : base(factory) + { + } + + [Theory] + [MemberData(nameof(UpsertProcessDefinitionAndNotifyTestData))] + public async Task UpsertProcessDefinition_ShouldReturnOk(string org, string app, string developer, string bpmnFilePath, ProcessDefinitionMetadata metadata) + { + string targetRepository = TestDataHelper.GenerateTestRepoName(); + await CopyRepositoryForTest(org, app, developer, targetRepository); + string fileContent = SharedResourcesHelper.LoadTestDataAsString(bpmnFilePath); + fileContent = metadata.TaskIdChanges.Aggregate(fileContent, (current, metadataTaskIdChange) => current.Replace(metadataTaskIdChange.OldId, metadataTaskIdChange.NewId)); + using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(fileContent)); + + string url = VersionPrefix(org, targetRepository); + + using var form = new MultipartFormDataContent(); + string metadataString = JsonSerializer.Serialize(metadata, + new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + form.Add(new StreamContent(fileStream), "content", "process.bpmn"); + form.Add(new StringContent(metadataString, Encoding.UTF8, MediaTypeNames.Application.Json), "metadata"); + + using var response = await HttpClient.PutAsync(url, form); + response.StatusCode.Should().Be(HttpStatusCode.OK); + + string savedFile = TestDataHelper.GetFileFromRepo(org, targetRepository, developer, "App/config/process/process.bpmn"); + + XDocument expectedXml = XDocument.Parse(fileContent); + XDocument savedXml = XDocument.Parse(savedFile); + XNode.DeepEquals(savedXml, expectedXml).Should().BeTrue(); + } + + public static IEnumerable UpsertProcessDefinitionAndNotifyTestData() + { + yield return new object[] { "ttd", "empty-app", "testUser", "App/config/process/process.bpmn", + new ProcessDefinitionMetadata + { + TaskIdChanges = new List { new() { OldId = "Task_1", NewId = "SomeNewId" } } + } }; + } +} diff --git a/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs b/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs index ec8a4ad5613..de0487719df 100644 --- a/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs +++ b/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs @@ -37,34 +37,6 @@ public void GetProcessDefinitionTemplates_GivenVersion_ReturnsListOfTemplates(st } } - [Theory] - [InlineData("ttd", "app-with-process", "testUser", "Task_1", "NewTaskName")] - public async void UpdateProcessTaskNameAsync_GivenTaskIdAndTaskName_UpdatesTaskName(string org, string repo, string developer, string taskId, string newTaskName) - { - // Arrange - string targetRepository = TestDataHelper.GenerateTestRepoName(); - var editingContext = AltinnRepoEditingContext.FromOrgRepoDeveloper(org, targetRepository, developer); - await TestDataHelper.CopyRepositoryForTest(org, repo, developer, targetRepository); - - try - { - var altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); - IProcessModelingService processModelingService = new ProcessModelingService(altinnGitRepositoryFactory); - - // Act - using Stream result = await processModelingService.UpdateProcessTaskNameAsync(editingContext, taskId, newTaskName); - XmlSerializer serializer = new(typeof(Definitions)); - Definitions definitions = (Definitions)serializer.Deserialize(result); - - // Assert - definitions.Process.Tasks.First(t => t.Id == taskId).Name.Should().Be(newTaskName); - } - finally - { - TestDataHelper.DeleteAppRepository(org, targetRepository, developer); - } - } - public static IEnumerable TemplatesTestData => new List { new object[] diff --git a/testdata/App/config/process/process.bpmn b/testdata/App/config/process/process.bpmn index 5400f3ee0f9..d3b0916f0a6 100644 --- a/testdata/App/config/process/process.bpmn +++ b/testdata/App/config/process/process.bpmn @@ -1,48 +1,78 @@ - - - - - To_Task_1 - - - To_Task_1 - To_Task_2 - - - To_Task_2 - To_Task_3 - - - To_Task_3 - To_Task_4 - - - To_Task_4 - To_Task_5 - - - To_Task_5 - To_Task_6 - - - To_Task_6 - To_End - - - To_End - - - - - - - - - + + + + + To_Task_1 + + + To_Task_1 + To_Task_2 + + + data + + + + + To_Task_2 + To_Task_3 + + + data + + fill + + + + + + To_Task_3 + To_Task_4 + + + data + + + + + To_Task_4 + To_Task_5 + + + data + + + + + To_Task_5 + To_Task_6 + + + data + + + + + To_Task_6 + To_End + + + confirmation + + confirm + + + + + + To_End + + + + + + + + + From 2108d11013c590babfa9ce736dd9e7d73ae19e4b Mon Sep 17 00:00:00 2001 From: WilliamThorenfeldt <133344438+WilliamThorenfeldt@users.noreply.github.com> Date: Mon, 5 Feb 2024 21:21:51 +0100 Subject: [PATCH 3/7] Trying to fix Playwright tests by bringing parallelism back (#12249) * Removing serial and bringing back paralell * fixing comment in yaml --- .github/workflows/playwright.yml | 2 +- frontend/testing/playwright/playwright.config.ts | 12 ++++++------ .../tests/create-app-only/create-app-only.spec.ts | 2 -- .../playwright/tests/dashboard/dashboard.spec.ts | 4 ---- .../playwright/tests/data-model/data-model.spec.ts | 4 ---- .../playwright/tests/git-sync/git-sync.spec.ts | 4 ---- .../main-navigation-between-sub-apps.spec.ts | 4 ---- .../tests/settings-modal/settings-modal.spec.ts | 4 ---- .../playwright/tests/ui-editor/ui-editor.spec.ts | 4 ---- 9 files changed, 7 insertions(+), 33 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 65292847739..145bcdd55e7 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -40,7 +40,7 @@ jobs: restore-keys: | ${{ runner.os }}-yarn- - - name: Attempt to wait for deploy to environment (12 minutes sleep) + - name: Attempt to wait for deploy to environment (10 minutes sleep) run: sleep 10m shell: bash diff --git a/frontend/testing/playwright/playwright.config.ts b/frontend/testing/playwright/playwright.config.ts index 2bdaa95d35a..58289e3b3c2 100644 --- a/frontend/testing/playwright/playwright.config.ts +++ b/frontend/testing/playwright/playwright.config.ts @@ -13,11 +13,11 @@ export default defineConfig({ trace: 'on-first-retry', baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL, }, - fullyParallel: false, + fullyParallel: true, timeout: 3 * 60 * 1000, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, - workers: process.env.CI ? 1 : undefined, + workers: 1, // Github actions always use only 1, so we set to 1 locally as well reporter: 'html', projects: [ @@ -60,7 +60,7 @@ export default defineConfig({ }, { name: TestNames.MAIN_NAVIGATION_BETWEEN_SUB_APPS, - dependencies: ['setup'], + dependencies: [TestNames.SETUP], testDir: './tests/main-navigation-between-sub-apps/', testMatch: '*.spec.ts', use: { @@ -72,7 +72,7 @@ export default defineConfig({ }, { name: TestNames.GIT_SYNC, - dependencies: ['setup'], + dependencies: [TestNames.SETUP], testDir: './tests/git-sync/', testMatch: '*.spec.ts', use: { @@ -84,7 +84,7 @@ export default defineConfig({ }, { name: TestNames.UI_EDITOR, - dependencies: ['setup'], + dependencies: [TestNames.SETUP], testDir: './tests/ui-editor/', testMatch: '*.spec.ts', use: { @@ -96,7 +96,7 @@ export default defineConfig({ }, { name: TestNames.SETTINGS_MODAL, - dependencies: ['setup'], + dependencies: [TestNames.SETUP], testDir: './tests/settings-modal/', testMatch: '*.spec.ts', use: { diff --git a/frontend/testing/playwright/tests/create-app-only/create-app-only.spec.ts b/frontend/testing/playwright/tests/create-app-only/create-app-only.spec.ts index 17de15ccdbd..753baec0fcc 100644 --- a/frontend/testing/playwright/tests/create-app-only/create-app-only.spec.ts +++ b/frontend/testing/playwright/tests/create-app-only/create-app-only.spec.ts @@ -4,8 +4,6 @@ import { test } from '../../extenders/testExtend'; import { CreateServicePage } from '../../pages/CreateServicePage'; import { DashboardPage } from '../../pages/DashboardPage'; -test.describe.configure({ mode: 'serial' }); - test.afterAll(async ({ request, testAppName }) => { const gitea = new Gitea(); const response = await request.delete(gitea.getDeleteAppEndpoint({ app: testAppName })); diff --git a/frontend/testing/playwright/tests/dashboard/dashboard.spec.ts b/frontend/testing/playwright/tests/dashboard/dashboard.spec.ts index ba7f9ee3705..5f84b22f51b 100644 --- a/frontend/testing/playwright/tests/dashboard/dashboard.spec.ts +++ b/frontend/testing/playwright/tests/dashboard/dashboard.spec.ts @@ -7,10 +7,6 @@ import { DashboardPage } from 'testing/playwright/pages/DashboardPage'; import { OverviewPage } from 'testing/playwright/pages/OverviewPage'; import { Gitea } from '../../helpers/Gitea'; -// This line must be there to ensure that the tests do not run in parallell, and -// that the before all call is being executed before we start the tests -test.describe.configure({ mode: 'serial' }); - // Before the tests starts, we need to create the dashboard app test.beforeAll(async ({ testAppName, request, storageState }) => { // Create 2 apps diff --git a/frontend/testing/playwright/tests/data-model/data-model.spec.ts b/frontend/testing/playwright/tests/data-model/data-model.spec.ts index 864c6445bda..5b5975ac88d 100644 --- a/frontend/testing/playwright/tests/data-model/data-model.spec.ts +++ b/frontend/testing/playwright/tests/data-model/data-model.spec.ts @@ -5,10 +5,6 @@ import { DesignerApi } from '../../helpers/DesignerApi'; import type { StorageState } from '../../types/StorageState'; import { Gitea } from '../../helpers/Gitea'; -// This line must be there to ensure that the tests do not run in parallell, and -// that the before all call is being executed before we start the tests -test.describe.configure({ mode: 'serial' }); - // Before the tests starts, we need to create the data model app test.beforeAll(async ({ testAppName, request, storageState }) => { // Create a new app diff --git a/frontend/testing/playwright/tests/git-sync/git-sync.spec.ts b/frontend/testing/playwright/tests/git-sync/git-sync.spec.ts index f2db4da7a6d..1196ffda835 100644 --- a/frontend/testing/playwright/tests/git-sync/git-sync.spec.ts +++ b/frontend/testing/playwright/tests/git-sync/git-sync.spec.ts @@ -9,10 +9,6 @@ import { UiEditorPage } from '../../pages/UiEditorPage'; import { GiteaPage } from '../../pages/GiteaPage'; import { Gitea } from '../../helpers/Gitea'; -// This line must be there to ensure that the tests do not run in parallell, and -// that the before all call is being executed before we start the tests -test.describe.configure({ mode: 'serial' }); - // Before the tests starts, we need to create the data model app test.beforeAll(async ({ testAppName, request, storageState }) => { // Create a new app diff --git a/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts b/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts index 9608d241054..fa7af1bd82a 100644 --- a/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts +++ b/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts @@ -14,10 +14,6 @@ import { DeployPage } from '../../pages/DeployPage'; import { Header } from '../../components/Header'; import { Gitea } from '../../helpers/Gitea'; -// This line must be there to ensure that the tests do not run in parallell, and -// that the before all call is being executed before we start the tests -test.describe.configure({ mode: 'serial' }); - // Before the tests starts, we need to create the dashboard app test.beforeAll(async ({ testAppName, request, storageState }) => { // Create a new app diff --git a/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts b/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts index 935e11842c3..9de2d0d2801 100644 --- a/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts +++ b/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts @@ -10,10 +10,6 @@ import type { SettingsModalTab } from '../../components/SettingsModal'; import { PolicyEditor } from '../../components/PolicyEditor'; import { Gitea } from 'testing/playwright/helpers/Gitea'; -// This line must be there to ensure that the tests do not run in parallell, and -// that the before all call is being executed before we start the tests -test.describe.configure({ mode: 'serial' }); - // Before the tests starts, we need to create the data model app test.beforeAll(async ({ testAppName, request, storageState }) => { // Create a new app diff --git a/frontend/testing/playwright/tests/ui-editor/ui-editor.spec.ts b/frontend/testing/playwright/tests/ui-editor/ui-editor.spec.ts index 49aaf672ab6..3939503a3bc 100644 --- a/frontend/testing/playwright/tests/ui-editor/ui-editor.spec.ts +++ b/frontend/testing/playwright/tests/ui-editor/ui-editor.spec.ts @@ -6,10 +6,6 @@ import type { StorageState } from '../../types/StorageState'; import { UiEditorPage } from '../../pages/UiEditorPage'; import { Gitea } from '../../helpers/Gitea'; -// This line must be there to ensure that the tests do not run in parallell, and -// that the before all call is being executed before we start the tests -test.describe.configure({ mode: 'serial' }); - // Before the tests starts, we need to create the data model app test.beforeAll(async ({ testAppName, request, storageState }) => { // Create a new app From dfac17d609c480a4489a7fe31aaaf44e5749e965 Mon Sep 17 00:00:00 2001 From: WilliamThorenfeldt <133344438+WilliamThorenfeldt@users.noreply.github.com> Date: Tue, 6 Feb 2024 09:10:33 +0100 Subject: [PATCH 4/7] adding skip to the 2 test that fails constantly (#12251) --- .../main-navigation-between-sub-apps.spec.ts | 2 +- .../playwright/tests/settings-modal/settings-modal.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts b/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts index fa7af1bd82a..c113f4c4714 100644 --- a/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts +++ b/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts @@ -131,7 +131,7 @@ test('That it is possible to navigate from overview to the preview page and back await uiEditor.verifyUiEditorPage(null); }); -test('That it is possible to navigate from overview to the deploy page and back again', async ({ +test.skip('That it is possible to navigate from overview to the deploy page and back again', async ({ page, testAppName, request, diff --git a/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts b/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts index 9de2d0d2801..a2216698a93 100644 --- a/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts +++ b/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts @@ -77,7 +77,7 @@ test('That it is possible to change tab to "Policy editor" tab', async ({ page, await settingsModal.verifyThatTabIsVisible('policy'); }); -test('That it is possible to edit security level on "Policy editor" tab, and that changes are saved', async ({ +test.skip('That it is possible to edit security level on "Policy editor" tab, and that changes are saved', async ({ page, testAppName, }) => { From 7a0ecebd9a5610478478ba474e2d08cc4964b1cc Mon Sep 17 00:00:00 2001 From: WilliamThorenfeldt <133344438+WilliamThorenfeldt@users.noreply.github.com> Date: Tue, 6 Feb 2024 12:45:57 +0100 Subject: [PATCH 5/7] Implementing changes to fix 2 broken Playwright tests (#12253) --- .../testing/playwright/pages/DashboardPage.ts | 1 - .../playwright/tests/dashboard/dashboard.spec.ts | 11 ++++++++--- .../main-navigation-between-sub-apps.spec.ts | 15 ++++++++++----- .../tests/settings-modal/settings-modal.spec.ts | 14 ++++++++++---- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/frontend/testing/playwright/pages/DashboardPage.ts b/frontend/testing/playwright/pages/DashboardPage.ts index 1efd92d82de..ca02617afe9 100644 --- a/frontend/testing/playwright/pages/DashboardPage.ts +++ b/frontend/testing/playwright/pages/DashboardPage.ts @@ -113,7 +113,6 @@ export class DashboardPage extends BasePage { await this.page .getByRole('menuitem', { name: this.textMock('dashboard.edit_app', { appName }), - exact: true, }) .click(); } diff --git a/frontend/testing/playwright/tests/dashboard/dashboard.spec.ts b/frontend/testing/playwright/tests/dashboard/dashboard.spec.ts index 5f84b22f51b..4b4b831b247 100644 --- a/frontend/testing/playwright/tests/dashboard/dashboard.spec.ts +++ b/frontend/testing/playwright/tests/dashboard/dashboard.spec.ts @@ -7,12 +7,17 @@ import { DashboardPage } from 'testing/playwright/pages/DashboardPage'; import { OverviewPage } from 'testing/playwright/pages/OverviewPage'; import { Gitea } from '../../helpers/Gitea'; +const getExtraAppName = (appName: string): string => `extra-app-${appName}`; + // Before the tests starts, we need to create the dashboard app test.beforeAll(async ({ testAppName, request, storageState }) => { // Create 2 apps - const testAppName2: string = `${testAppName}2`; const firstApp = await createApp(testAppName, request, storageState as StorageState); - const secondApp = await createApp(testAppName2, request, storageState as StorageState); + const secondApp = await createApp( + getExtraAppName(testAppName), + request, + storageState as StorageState, + ); expect(firstApp.ok()).toBeTruthy(); expect(secondApp.ok()).toBeTruthy(); @@ -20,7 +25,7 @@ test.beforeAll(async ({ testAppName, request, storageState }) => { test.afterAll(async ({ request, testAppName }) => { const gitea = new Gitea(); - const appsToDelete: string[] = [testAppName, `${testAppName}2`]; + const appsToDelete: string[] = [testAppName, getExtraAppName(testAppName)]; for (const app of appsToDelete) { const response = await request.delete(gitea.getDeleteAppEndpoint({ app })); diff --git a/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts b/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts index c113f4c4714..19c3560a772 100644 --- a/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts +++ b/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts @@ -131,14 +131,20 @@ test('That it is possible to navigate from overview to the preview page and back await uiEditor.verifyUiEditorPage(null); }); -test.skip('That it is possible to navigate from overview to the deploy page and back again', async ({ +test('That it is possible to navigate from overview to the deploy page and back again', async ({ page, testAppName, request, storageState, }) => { + const testDepartmentOrg: string = 'ttd'; + const designerApi = new DesignerApi({ app: testAppName }); - const response = await designerApi.createApp(request, storageState as StorageState, 'ttd'); + const response = await designerApi.createApp( + request, + storageState as StorageState, + testDepartmentOrg, + ); expect(response.ok()).toBeTruthy(); const dashboardPage = new DashboardPage(page, { app: testAppName }); @@ -149,13 +155,12 @@ test.skip('That it is possible to navigate from overview to the deploy page and await dashboardPage.loadDashboardPage(); await dashboardPage.verifyDashboardPage(); - const testDepartmentOrg: string = 'ttd'; - - // Change org to TTD await dashboardPage.clickOnHeaderAvatar(); await dashboardPage.clickOnOrgApplications(); dashboardPage.updateOrgNameEnv(testDepartmentOrg); await dashboardPage.checkThatTTDApplicationsHeaderIsVisible(); + + expect(dashboardPage.org).toEqual('ttd'); await dashboardPage.clickOnTestAppEditButton(testAppName); // As we have changed env.org to 'ttd', we need to update the org of the new classes to make sure it works. diff --git a/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts b/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts index a2216698a93..8de1ea92d37 100644 --- a/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts +++ b/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts @@ -77,7 +77,7 @@ test('That it is possible to change tab to "Policy editor" tab', async ({ page, await settingsModal.verifyThatTabIsVisible('policy'); }); -test.skip('That it is possible to edit security level on "Policy editor" tab, and that changes are saved', async ({ +test('That it is possible to edit security level on "Policy editor" tab, and that changes are saved', async ({ page, testAppName, }) => { @@ -88,21 +88,27 @@ test.skip('That it is possible to edit security level on "Policy editor" tab, an const securityLevel2 = 2; const securityLevel2Text = policyEditor.getSecurityLevelByTextByLevel(securityLevel2); - expect(await policyEditor.getSelectedSecurityLevel()).toEqual(securityLevel2Text); + + const securityValueInitial = await policyEditor.getSelectedSecurityLevel(); + expect(securityValueInitial).toBe(securityLevel2Text); await policyEditor.clickOnSecurityLevelSelect(); await policyEditor.clickOnSecurityLevelSelectOption(3); const securityLevel3 = 3; const securityLevel3Text = policyEditor.getSecurityLevelByTextByLevel(securityLevel3); - expect(await policyEditor.getSelectedSecurityLevel()).toEqual(securityLevel3Text); + + const securityValueAfterChange = await policyEditor.getSelectedSecurityLevel(); + expect(securityValueAfterChange).toBe(securityLevel3Text); await settingsModal.navigateToTab('about'); await settingsModal.verifyThatTabIsVisible('about'); await settingsModal.verifyThatTabIsHidden('policy'); await settingsModal.navigateToTab('policy'); - expect(await policyEditor.getSelectedSecurityLevel()).toEqual(securityLevel3Text); + + const securityValueAfterNavigation = await policyEditor.getSelectedSecurityLevel(); + expect(securityValueAfterNavigation).toBe(securityLevel3Text); }); test('That it is possible to change tab to "Access control" tab', async ({ page, testAppName }) => { From 598b8f660368419fe280b632abf685e99f8e35fb Mon Sep 17 00:00:00 2001 From: Michael Queyrichon Date: Tue, 6 Feb 2024 13:06:07 +0100 Subject: [PATCH 6/7] Fix index name (#12254) --- .../Infrastructure/GitRepository/AltinnAppGitRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Designer/Infrastructure/GitRepository/AltinnAppGitRepository.cs b/backend/src/Designer/Infrastructure/GitRepository/AltinnAppGitRepository.cs index d946bd55b02..0176f15d2ff 100644 --- a/backend/src/Designer/Infrastructure/GitRepository/AltinnAppGitRepository.cs +++ b/backend/src/Designer/Infrastructure/GitRepository/AltinnAppGitRepository.cs @@ -38,7 +38,7 @@ public class AltinnAppGitRepository : AltinnGitRepository private const string LANGUAGE_RESOURCE_FOLDER_NAME = "texts/"; private const string MARKDOWN_TEXTS_FOLDER_NAME = "md/"; private const string PROCESS_DEFINITION_FOLDER_PATH = "App/config/process/"; - private const string CSHTML_PATH = "App/views/Home/index.cshtml"; + private const string CSHTML_PATH = "App/views/Home/Index.cshtml"; private const string SERVICE_CONFIG_FILENAME = "config.json"; private const string LAYOUT_SETTINGS_FILENAME = "Settings.json"; From f73ce31c127910b887cfd42b0390ce908c0d4e06 Mon Sep 17 00:00:00 2001 From: WilliamThorenfeldt <133344438+WilliamThorenfeldt@users.noreply.github.com> Date: Tue, 6 Feb 2024 14:31:35 +0100 Subject: [PATCH 7/7] Trying to fix the two broken playwright tests (#12255) * Implementing changes to fix 2 broken Playwright tests * Fixing the two broken tests in Playwright * Removing timeout * fixing delete ttd test --- .../testing/playwright/helpers/BasePage.ts | 8 +++++++ .../testing/playwright/playwright.config.ts | 1 - .../main-navigation-between-sub-apps.spec.ts | 22 +++++++++++++------ .../settings-modal/settings-modal.spec.ts | 3 +++ 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/frontend/testing/playwright/helpers/BasePage.ts b/frontend/testing/playwright/helpers/BasePage.ts index e4d82924828..1b21f6b5de2 100644 --- a/frontend/testing/playwright/helpers/BasePage.ts +++ b/frontend/testing/playwright/helpers/BasePage.ts @@ -34,4 +34,12 @@ export class BasePage extends RouterRoute { return text; } + + public async waitForXAmountOfMilliseconds(milliseconds: number): Promise { + await new Promise((resolve) => + setTimeout(() => { + return resolve(''); + }, milliseconds), + ); + } } diff --git a/frontend/testing/playwright/playwright.config.ts b/frontend/testing/playwright/playwright.config.ts index 58289e3b3c2..a561b5ddfc2 100644 --- a/frontend/testing/playwright/playwright.config.ts +++ b/frontend/testing/playwright/playwright.config.ts @@ -14,7 +14,6 @@ export default defineConfig({ baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL, }, fullyParallel: true, - timeout: 3 * 60 * 1000, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: 1, // Github actions always use only 1, so we set to 1 locally as well diff --git a/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts b/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts index 19c3560a772..07d74b29116 100644 --- a/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts +++ b/frontend/testing/playwright/tests/main-navigation-between-sub-apps/main-navigation-between-sub-apps.spec.ts @@ -14,6 +14,8 @@ import { DeployPage } from '../../pages/DeployPage'; import { Header } from '../../components/Header'; import { Gitea } from '../../helpers/Gitea'; +const getTtdApp = (appName: string) => `ttd-app-${appName}`; + // Before the tests starts, we need to create the dashboard app test.beforeAll(async ({ testAppName, request, storageState }) => { // Create a new app @@ -28,7 +30,7 @@ test.afterAll(async ({ request, testAppName }) => { expect(response.ok()).toBeTruthy(); const responseTTD = await request.delete( - gitea.getDeleteAppEndpoint({ org: 'ttd', app: testAppName }), + gitea.getDeleteAppEndpoint({ org: 'ttd', app: getTtdApp(testAppName) }), ); expect(responseTTD.ok()).toBeTruthy(); }); @@ -138,8 +140,9 @@ test('That it is possible to navigate from overview to the deploy page and back storageState, }) => { const testDepartmentOrg: string = 'ttd'; + const appName: string = getTtdApp(testAppName); // Need a different app name than the one generated by the user in beforeAll() - const designerApi = new DesignerApi({ app: testAppName }); + const designerApi = new DesignerApi({ app: appName }); const response = await designerApi.createApp( request, storageState as StorageState, @@ -147,10 +150,10 @@ test('That it is possible to navigate from overview to the deploy page and back ); expect(response.ok()).toBeTruthy(); - const dashboardPage = new DashboardPage(page, { app: testAppName }); - const overviewPage = new OverviewPage(page, { app: testAppName }); - const deployPage = new DeployPage(page, { app: testAppName }); - const header = new Header(page, { app: testAppName }); + const dashboardPage = new DashboardPage(page, { app: appName }); + const overviewPage = new OverviewPage(page, { app: appName }); + const deployPage = new DeployPage(page, { app: appName }); + const header = new Header(page, { app: appName }); await dashboardPage.loadDashboardPage(); await dashboardPage.verifyDashboardPage(); @@ -161,13 +164,18 @@ test('That it is possible to navigate from overview to the deploy page and back await dashboardPage.checkThatTTDApplicationsHeaderIsVisible(); expect(dashboardPage.org).toEqual('ttd'); - await dashboardPage.clickOnTestAppEditButton(testAppName); + + await dashboardPage.typeInSearchField(appName); + await dashboardPage.checkThatAppIsVisible(appName); + await dashboardPage.clickOnTestAppEditButton(appName); // As we have changed env.org to 'ttd', we need to update the org of the new classes to make sure it works. overviewPage.updateOrgNameEnv(testDepartmentOrg); deployPage.updateOrgNameEnv(testDepartmentOrg); header.updateOrgNameEnv(testDepartmentOrg); + // overviewPage.waitForXAmountOfMilliseconds(3000); + await overviewPage.verifyOverviewPage(); // Check Navigation diff --git a/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts b/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts index 8de1ea92d37..4109369c628 100644 --- a/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts +++ b/frontend/testing/playwright/tests/settings-modal/settings-modal.spec.ts @@ -101,6 +101,9 @@ test('That it is possible to edit security level on "Policy editor" tab, and tha const securityValueAfterChange = await policyEditor.getSelectedSecurityLevel(); expect(securityValueAfterChange).toBe(securityLevel3Text); + // In dev, the API call to save the policy takes some time, and therefore we add functionality to wait for a while to wait for the save to happen + await settingsModal.waitForXAmountOfMilliseconds(4000); + await settingsModal.navigateToTab('about'); await settingsModal.verifyThatTabIsVisible('about'); await settingsModal.verifyThatTabIsHidden('policy');