Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/build-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,73 @@ jobs:
smoke-test: false
service: ${{ vars.TEAMS_MSG_SERVICE_NAME }}

post-deploy-tests:
name: Run Integration Tests on Deployed Test Environment
needs: deploy
if: ${{ matrix.environment == 'test' }}
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

# Set up .NET
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'

- name: Restore dependencies
run: dotnet restore
- name: Install Playwright browsers (.NET)
run: pwsh ./Tests/SAPPub.Integration.Tests/bin/Release/net8.0/playwright.ps1 install

- name: Run Integration Tests with Coverage
env:
HEADED: 0
PLAYWRIGHT_IGNORE_HTTPS_ERRORS: true
ASPNETCORE_ENVIRONMENT: CI
run:
dotnet test Tests/SAPPub.Integration.Tests/SAPPub.IntegrationTests.csproj \
--no-build \
--configuration Release \
--results-directory "TestResults" \
--logger "trx;LogFileName=$(basename "$proj" .csproj).trx" \
--collect "XPlat Code Coverage"\
/p:CollectCoverage=true \
/p:CoverletOutputFormat=cobertura \
/p:DeterministicReport=true \
/p:UseSourceLink=true \
/p:Exclude='[*]AspNetCoreGeneratedDocument.*%2c[*]AspNetCore.Views.*' \
/p:ExcludeByAttribute='GeneratedCodeAttribute*%2cObsoleteAttribute*%2cExcludeFromCodeCoverage' \
--verbosity normal
done

- name: Upload Test Result Files
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: "**/TestResults/**/*"
retention-days: 5

- name: Upload UI artifacts on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: ui-test-artifacts
path: |
**/test-artifacts/screenshots/**
**/test-artifacts/videos/**
**/test-artifacts/traces/**

- name: Publish Test Results
uses: dorny/test-reporter@v2
if: always()
with:
reporter: 'dotnet-trx'
path: "**/TestResults/**/*.trx"
name: 'Test Results'

# ---------------------------
# MANUAL DEPLOY
# ---------------------------
Expand Down
10 changes: 6 additions & 4 deletions SAPPub.Web/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,14 @@ public static void Main(string[] args)
// Only required for real runtime environments
if (builder.Environment.IsDevelopment() || builder.Environment.IsProduction() || builder.Environment.IsStaging())
throw new InvalidOperationException("Connection string 'PostgresConnectionString' is not configured.");

// For Testing/UITests: use a harmless dummy so nothing accidentally connects
connectionString = "Host=127.0.0.1;Port=1;Database=x;Username=x;Password=x;Timeout=1;Command Timeout=1";
}

builder.Services.AddSingleton<NpgsqlDataSource>(_ => NpgsqlDataSource.Create(connectionString));
builder.Services.AddSingleton<NpgsqlDataSource>(_ =>
{
var builder = new NpgsqlDataSourceBuilder(connectionString);
builder.EnableParameterLogging();
return builder.Build();
});

builder.Services.AddDependencies(builder.Environment, builder.Configuration);
builder.Services.AddLuceneDependencies();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@
@(Model.SelectedGrade == GcseGradeDataSelection.Grade5AndAbove ? "Grade 5 is comparable to the top of the old grade C." : "The bottom of grade 4 is comparable to the bottom of the old grade C")
</p>

@if(false)
{
<input type="radio" name="dataView" id="current-view" class="govuk-visually-hidden" aria-labelledby="all-gcse-show-current-data-btn" checked />
<input type="radio" name="dataView" id="data-overtime-view" class="govuk-visually-hidden" aria-labelledby="all-gcse-show-data-over-time-btn" />

Expand Down Expand Up @@ -170,7 +172,36 @@
<div class="govuk-button-group">
<label id="all-gcse-show-current-data-btn" for="current-view" class="govuk-button govuk-button--secondary">Show current data</label>
</div>
</div>
</div>
}
else
{
<div id="all-gcse-data-over-time-table-container" class="all-gcse-data-over-time-table-container">
<table id="all-gcse-data-overtime-table" class="govuk-table">
<thead class="govuk-table__head">
<tr class="govuk-table__row">
<th scope="col" class="govuk-table__header"></th>
@foreach (var label in Model.AllGcseOverTimeData.Labels)
{
<th scope="col" class="govuk-table__header">@label</th>
}
</tr>
</thead>
<tbody class="govuk-table__body">
@foreach (var dataset in Model.AllGcseOverTimeData.Datasets.Select((value, i) => (value, i)))
{
<tr class="govuk-table__row">
<th scope="row" class="govuk-table__header">@dataset.value.Label</th>
@foreach (var data in dataset.value.Data)
{
<td class="govuk-table__cell">@data%</td>
}
</tr>
}
</tbody>
</table>
</div>
}
</div>

<div class="data-container">
Expand Down
5 changes: 4 additions & 1 deletion SAPPub.Web/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
}
},
"ConnectionStrings": {
"PostgresConnectionString": "Host=localhost;Port=5432;Database=SAPData;Username=postgres;Password=postgres"
"PostgresConnectionString": ""
},
"Playwright": {
"Headed": true
}
}
11 changes: 9 additions & 2 deletions SAPPub.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.1.11312.151
# Visual Studio Version 17
VisualStudioVersion = 17.14.37027.9
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SAPPub.Web", "SAPPub.Web\SAPPub.Web.csproj", "{24624574-4DE4-4A91-8273-7AE3EEE13537}"
EndProject
Expand All @@ -28,6 +28,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SAPData", "SAPData\SAPData.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SAPData.Unit.Tests", "SAPData.Tests.Unit\SAPData.Unit.Tests.csproj", "{2FD6F8C0-9D97-7714-4445-AD6E47BEB671}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SAPPub.IntegrationTests", "Tests\SAPPub.Integration.Tests\SAPPub.IntegrationTests.csproj", "{D90BBB38-FA34-5ABD-F17F-F30AF1D84C6A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -66,6 +68,10 @@ Global
{2FD6F8C0-9D97-7714-4445-AD6E47BEB671}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2FD6F8C0-9D97-7714-4445-AD6E47BEB671}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2FD6F8C0-9D97-7714-4445-AD6E47BEB671}.Release|Any CPU.Build.0 = Release|Any CPU
{D90BBB38-FA34-5ABD-F17F-F30AF1D84C6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D90BBB38-FA34-5ABD-F17F-F30AF1D84C6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D90BBB38-FA34-5ABD-F17F-F30AF1D84C6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D90BBB38-FA34-5ABD-F17F-F30AF1D84C6A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -75,6 +81,7 @@ Global
{EB1D1EAE-E264-4FF5-ACB9-8614FD694C69} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{5F164DC3-0185-4D92-B689-7A004F81C232} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{2FD6F8C0-9D97-7714-4445-AD6E47BEB671} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{D90BBB38-FA34-5ABD-F17F-F30AF1D84C6A} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0D7E6B09-2E5D-4BA4-899B-FBCC5A7F92CD}
Expand Down
77 changes: 77 additions & 0 deletions Tests/SAPPub.Integration.Tests/Infrastructure/BasePageTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Microsoft.Playwright;
using Microsoft.Playwright.Xunit;

namespace SAPPub.Integration.Tests;

public abstract class BasePageTest : PageTest
{
private readonly WebApplicationSetupFixture _fixture;

// ReSharper disable once ConvertToPrimaryConstructor
protected BasePageTest(WebApplicationSetupFixture fixture)
{
_fixture = fixture;

if (_fixture.IsHeaded())
{
Environment.SetEnvironmentVariable("HEADED", "1");
}
}

public override BrowserNewContextOptions ContextOptions()
{
return new BrowserNewContextOptions
{
BaseURL = _fixture.BaseUrl.TrimEnd('/'),
IgnoreHTTPSErrors = true,
ViewportSize = new() { Width = 1280, Height = 720 },
Locale = "en-GB",
TimezoneId = "Europe/London",
JavaScriptEnabled = true,
};
}

public override async Task InitializeAsync()
{
await base.InitializeAsync();

Page.SetDefaultTimeout((float)TimeSpan.FromSeconds(60).TotalMilliseconds);
Page.SetDefaultNavigationTimeout((float)TimeSpan.FromSeconds(100).TotalMilliseconds);
}

public async Task WaitForSearchInputsAsync(int timeoutMs = 5000)
{
var selector = "input[name='__Query'], input[name='Query'][type='hidden'], input[name='Query']";
await Page.WaitForSelectorAsync(selector, new() { Timeout = timeoutMs });
await Page.WaitForTimeoutAsync(100);
}
public async Task<ILocator> GetQueryInputLocatorAsync(int checkTimeoutMs = 1000)
{
var jsLocator = Page.Locator("input[name='__Query']");
try
{
if (await jsLocator.CountAsync() > 0)
{
var isVisible = await jsLocator.IsVisibleAsync();
if (isVisible) return jsLocator;
}

var serverLocator = Page.Locator("input[name='Query']");
if (await serverLocator.CountAsync() > 0) return serverLocator;

var found = await Page.WaitForSelectorAsync("input[name='__Query'], input[name='Query']", new() { Timeout = checkTimeoutMs });
if (found != null)
{
var nameAttr = await found.GetAttributeAsync("name");
if (nameAttr == "__Query")
return Page.Locator("input[name='__Query']");
return Page.Locator("input[name='Query']");
}
return Page.Locator("input[name='Query']");
}
catch
{
return Page.Locator("input[name='Query']");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace SAPPub.Integration.Tests;

[CollectionDefinition("Integration Tests", DisableParallelization = true)]
public class IntegrationTestCollection : ICollectionFixture<WebApplicationSetupFixture>
{
}

Loading