diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 267cde56b..de92b2e28 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -269,4 +269,13 @@ jobs: app_name: ${{ matrix.app_name }} test_project_name: ${{ matrix.test_project_name }} project_type: service + secrets: inherit + + run-ui-e2e-tests: + if: ${{ startsWith(inputs.environment, 'Test') || inputs.environment == 'Pre-production' }} + name: Run E2E Test Suite + needs: [ deploy-ui-services ] + uses: ./.github/workflows/run-e2e-tests.yml + with: + environment: ${{ inputs.environment }} secrets: inherit \ No newline at end of file diff --git a/.github/workflows/e2e-seed-database.yml b/.github/workflows/e2e-seed-database.yml new file mode 100644 index 000000000..8fe71e619 --- /dev/null +++ b/.github/workflows/e2e-seed-database.yml @@ -0,0 +1,106 @@ +name: Seed Database with E2E Test Data +run-name: Seed ${{ inputs.environment }} Database with E2E Test Data (${{ inputs.action }}) + +on: + workflow_dispatch: + inputs: + environment: + description: The environment target to seed test data in + default: 'Development' + type: choice + options: + - 'Development' + - 'Test' + - 'Test2' + - 'Pre-production' + action: + description: Whether to run the setup or teardown script + default: 'Teardown' + type: choice + options: + - 'Setup' + - 'Teardown' + workflow_call: + inputs: + environment: + required: true + type: string + action: + required: true + type: string + +permissions: + id-token: write + contents: read + +jobs: + run-seed-script: + name: Run ${{ inputs.action }} Script + runs-on: ubuntu-24.04 + environment: ${{ inputs.environment }} + defaults: + run: + working-directory: "test/e2e-seed-data-framework" + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + + - name: Install NodeJS + uses: actions/setup-node@v4 + with: + node-version: "lts/Jod" # 22 LTS + + - name: Install NPM Packages + shell: bash + run: npm i + + - name: Get Workflow Runner IP + id: runner-ip + uses: ./.github/actions/get-runner-ip-address + + - name: Azure CLI Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Add Azure Firewall Rule + uses: ./.github/actions/azure-firewall-ip + with: + ip_address: ${{ steps.runner-ip.outputs.ip_address }} + action: "Add" + az_resource_group: ${{ vars.AZURE_RESOURCE_PREFIX }}-familyhubs + az_sql_server_name: ${{ vars.AZURE_RESOURCE_PREFIX }}-as-fh-sql-server + az_firewall_rule_name: E2E-SEED-SCRIPT + + - name: Create Environment Variables + shell: bash + run: | + cat <<'EOF' > .env + IDS_START_FROM=1000000 + CONNECTION_STRING_SERVICEDIRECTORY='${{ secrets.PLAYWRIGHT_CONNECTION_STRING_SERVICE_DIRECTORY_DATABASE }}' + CONNECTION_STRING_REFERRAL='${{ secrets.PLAYWRIGHT_CONNECTION_STRING_REFERRAL_DATABASE }}' + CONNECTION_STRING_REPORT='${{ secrets.PLAYWRIGHT_CONNECTION_STRING_REPORT_DATABASE }}' + ENCRYPTION_KEY='${{ secrets.PLAYWRIGHT_REFERRAL_COLUMN_ENCRYPTION_KEY }}' + INITIALISATION_VECTOR='${{ secrets.PLAYWRIGHT_REFERRAL_COLUMN_INITIALISATION_VECTOR }}' + EXAMPLE_SEED=False + EOF + + - name: Run ${{ inputs.action }} + shell: bash + run: | + SCRIPT=$(tr '[:upper:]' '[:lower:]' <<< "${{ inputs.action }}") + npm run $SCRIPT:dev + + - name: Remove Azure Firewall Rule + if: always() + uses: ./.github/actions/azure-firewall-ip + with: + ip_address: ${{ steps.runner-ip.outputs.ip_address }} + action: "Remove" + az_resource_group: ${{ vars.AZURE_RESOURCE_PREFIX }}-familyhubs + az_sql_server_name: ${{ vars.AZURE_RESOURCE_PREFIX }}-as-fh-sql-server + az_firewall_rule_name: E2E-SEED-SCRIPT \ No newline at end of file diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml new file mode 100644 index 000000000..9cceef1e7 --- /dev/null +++ b/.github/workflows/run-e2e-tests.yml @@ -0,0 +1,132 @@ +name: Run E2E Test Suite +run-name: Run E2E Test Suite on ${{ inputs.environment }} + +on: + workflow_dispatch: + inputs: + environment: + description: The environment to run the E2E tests on + default: 'Development' + type: choice + options: + - 'Development' + - 'Test' + - 'Test2' + - 'Pre-production' + workflow_call: + inputs: + environment: + required: true + type: string + +permissions: + id-token: write + contents: read + +jobs: + pre-test-teardown: + name: Pre-Test Database Teardown + uses: ./.github/workflows/e2e-seed-database.yml + with: + environment: ${{ inputs.environment }} + action: 'Teardown' + secrets: inherit + + pre-test-setup: + needs: [ pre-test-teardown ] + name: Pre-Test Database Setup + uses: ./.github/workflows/e2e-seed-database.yml + with: + environment: ${{ inputs.environment }} + action: 'Setup' + secrets: inherit + + run-test-suite: + needs: [ pre-test-setup ] + name: ${{ matrix.job_name }} + strategy: + fail-fast: false + matrix: + suite: [ find-e2e-tests, manage-e2e-tests ] + include: + - suite: find-e2e-tests + job_name: E2E - Find UI + base_url: ${{ vars.PLAYWRIGHT_FIND_BASE_URL }} + - suite: manage-e2e-tests + job_name: E2E - Manage UI + base_url: ${{ vars.PLAYWRIGHT_MANAGE_BASE_URL }} + runs-on: ubuntu-24.04 + environment: ${{ inputs.environment }} + defaults: + run: + working-directory: "test/${{ matrix.suite }}" + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + + - name: Install NodeJS + uses: actions/setup-node@v4 + with: + node-version: "lts/Jod" # 22 LTS + + - name: Install NPM Packages + shell: bash + run: npm i + + - name: Install Playwright Dependencies + shell: bash + run: npx playwright install-deps + + - name: Get Playwright Version + id: playwright-version + shell: bash + run: | + ARRAY=($(npx playwright --version)) + VERSION=${ARRAY[1]} + echo "VERSION=$VERSION" >> $GITHUB_OUTPUT + + - name: Playwright Browser Cache + id: playwright-cache + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: ${{ runner.os }}-PlayWright-${{ steps.playwright-version.outputs.VERSION }} + + - name: Install Playwright Browsers + if: steps.playwright-cache.outputs.cache-hit != 'true' + shell: bash + run: npx playwright install + + - name: Create Environment Variables + shell: bash + run: | + cat <<'EOF' > .env + BASE_URL='${{ vars.PLAYWRIGHT_ENVIRONMENT_PREFIX }}${{ matrix.base_url }}' + USER_NAME='${{ secrets.PLAYWRIGHT_USER_NAME }}' + PASSWORD='${{ secrets.PLAYWRIGHT_PASSWORD }}' + DFE_ADMIN_USER='${{ secrets.PLAYWRIGHT_GOVLOGIN_DFE_ADMIN_USER }}' + GOV_LOGIN_PASSWORD='${{ secrets.PLAYWRIGHT_GOVLOGIN_PASSWORD }}' + EOF + + - name: Run Playwright Test Suite + shell: bash + run: npx playwright test --workers `nproc` + + - name: Upload Report + uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report-${{ matrix.suite }} + path: "**/playwright-report/" + + # Intentionally doesn't run after test failure to facilitate analysis + post-test-teardown: + needs: [ run-test-suite ] + name: Post-Test Database Teardown + uses: ./.github/workflows/e2e-seed-database.yml + with: + environment: ${{ inputs.environment }} + action: 'Teardown' + secrets: inherit \ No newline at end of file diff --git a/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Dashboard/ColumnHeader.cs b/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Dashboard/ColumnHeader.cs index 1df3923c1..b7320c825 100644 --- a/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Dashboard/ColumnHeader.cs +++ b/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Dashboard/ColumnHeader.cs @@ -51,7 +51,7 @@ public string ContentAsHtml { url += $"&{_extraQueryParams}"; } - return $"{_columnImmutable.DisplayName}"; + return $"{_columnImmutable.DisplayName}"; } } diff --git a/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/FamilyHubsUi/FamilyHubsLayoutModel.cs b/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/FamilyHubsUi/FamilyHubsLayoutModel.cs index ad77b7c41..e515263d1 100644 --- a/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/FamilyHubsUi/FamilyHubsLayoutModel.cs +++ b/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/FamilyHubsUi/FamilyHubsLayoutModel.cs @@ -13,5 +13,7 @@ public FamilyHubsLayoutModel(IOptions familyHubsUiOptions) FamilyHubsUiOptions = familyHubsUiOptions; } + public bool IsError { get; set; } + public PageModel? PageModel { get; set; } } \ No newline at end of file diff --git a/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Header/IHasErrorStatePageModel.cs b/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Header/IHasErrorStatePageModel.cs new file mode 100644 index 000000000..3eaf485a8 --- /dev/null +++ b/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Header/IHasErrorStatePageModel.cs @@ -0,0 +1,8 @@ +using FamilyHubs.SharedKernel.Razor.ErrorNext; + +namespace FamilyHubs.SharedKernel.Razor.Header; + +public interface IHasErrorStatePageModel +{ + public IErrorState Errors { get; } +} diff --git a/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Layout/ViewStart.cs b/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Layout/ViewStart.cs index acb5df682..3e3693eac 100644 --- a/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Layout/ViewStart.cs +++ b/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Layout/ViewStart.cs @@ -1,6 +1,7 @@ using FamilyHubs.SharedKernel.Razor.AlternativeServices; using FamilyHubs.SharedKernel.Razor.FamilyHubsUi; using FamilyHubs.SharedKernel.Razor.FamilyHubsUi.Extensions; +using FamilyHubs.SharedKernel.Razor.Header; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.ViewFeatures; @@ -20,6 +21,12 @@ public static void InitialiseFamilyHubs(FamilyHubsLayoutModel familyHubsLayoutMo familyHubsLayoutModel.FamilyHubsUiOptions = Options.Create(altFamilyHubsUiOptions); } } + + if (pageModel is IHasErrorStatePageModel hasErrorStatePageModel) + { + familyHubsLayoutModel.IsError = hasErrorStatePageModel.Errors.HasErrors; + } + viewData.SetFamilyHubsLayoutModel(familyHubsLayoutModel); } } \ No newline at end of file diff --git a/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Pages/Shared/_Head.cshtml b/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Pages/Shared/_Head.cshtml index adb511e81..f71712fdb 100644 --- a/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Pages/Shared/_Head.cshtml +++ b/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Pages/Shared/_Head.cshtml @@ -1,23 +1,26 @@ -@model FamilyHubs.SharedKernel.Razor.FamilyHubsUi.Options.IFamilyHubsUiOptions +@model FamilyHubs.SharedKernel.Razor.FamilyHubsUi.FamilyHubsLayoutModel +@{ + var familyHubsUiOptions = Model.FamilyHubsUiOptions.Value; +} - - + + -@ViewData["Title"] - @Model.ServiceName - GOV.UK - +@if (Model.IsError) { <text>Error: </text> }@ViewData["Title"] - @familyHubsUiOptions.ServiceName - GOV.UK + - - - - - - + + + + + + - + - + diff --git a/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Pages/Shared/_LargeSetPagination.cshtml b/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Pages/Shared/_LargeSetPagination.cshtml index bf9bf4715..fd682f685 100644 --- a/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Pages/Shared/_LargeSetPagination.cshtml +++ b/src/shared/web-components/src/FamilyHubs.SharedKernel.Razor/Pages/Shared/_LargeSetPagination.cshtml @@ -12,7 +12,9 @@ - Previous + + Previous page + } @@ -37,7 +39,9 @@ {