From 6e1a53de2954f54dd4f859034d9eccdc1c879f45 Mon Sep 17 00:00:00 2001
From: CLAWLOR
Date: Thu, 2 Apr 2026 12:56:31 +0100
Subject: [PATCH 1/2] not working - maybe start again. remove Custom factrory
and AboutPage tests that use a different setup?
---
.../AboutSchoolPageTests.cs | 208 ++++++++++++++++++
.../Infrastructure/BasePageTest.cs | 75 +++++++
.../CustomWebApplicationFactory.cs | 62 ++++++
.../IntegrationTestCollection.cs | 7 +
.../TestWebApplicationFactory.cs | 197 +++++++++++++++++
.../WebApplicationSetupFixture.cs | 27 +++
SAPPub.Integration.Tests/PageTests.cs | 58 +++++
.../SAPPub.Integration.Tests.csproj | 34 +++
SAPPub.Integration.Tests/SearchTests.cs | 163 ++++++++++++++
SAPPub.Integration.Tests/UnitTest1.cs | 11 +
...icPerformanceEnglishAndMathsResults.cshtml | 33 ++-
SAPPub.sln | 11 +-
.../UI/Infrastructure/BasePageTest.cs | 2 +-
13 files changed, 884 insertions(+), 4 deletions(-)
create mode 100644 SAPPub.Integration.Tests/AboutSchoolPageTests.cs
create mode 100644 SAPPub.Integration.Tests/Infrastructure/BasePageTest.cs
create mode 100644 SAPPub.Integration.Tests/Infrastructure/CustomWebApplicationFactory.cs
create mode 100644 SAPPub.Integration.Tests/Infrastructure/IntegrationTestCollection.cs
create mode 100644 SAPPub.Integration.Tests/Infrastructure/TestWebApplicationFactory.cs
create mode 100644 SAPPub.Integration.Tests/Infrastructure/WebApplicationSetupFixture.cs
create mode 100644 SAPPub.Integration.Tests/PageTests.cs
create mode 100644 SAPPub.Integration.Tests/SAPPub.Integration.Tests.csproj
create mode 100644 SAPPub.Integration.Tests/SearchTests.cs
create mode 100644 SAPPub.Integration.Tests/UnitTest1.cs
diff --git a/SAPPub.Integration.Tests/AboutSchoolPageTests.cs b/SAPPub.Integration.Tests/AboutSchoolPageTests.cs
new file mode 100644
index 00000000..c1377654
--- /dev/null
+++ b/SAPPub.Integration.Tests/AboutSchoolPageTests.cs
@@ -0,0 +1,208 @@
+namespace SAPPub.Integration.Tests;
+
+[Collection("Integration Tests")]
+public class AboutSchoolPageTests(WebApplicationSetupFixture fixture) : BasePageTest(fixture)
+{
+ private Dictionary _schoolUrnToUrlMap = new Dictionary
+ {
+ //["105574"] = "school/105574/Loreto%20High%20School%20Chorlton/secondary/about",
+ //["137552"] = "school/137552/Stewards%20Academy%20-%20Science%20Specialist%2C%20Harlow/secondary/about",
+ //["100273"] = "school/100273/Saint%20Paul%20Roman%20Catholic%20Infant%20School/secondary/about",
+ //["107564"] = "school/107564/Todmorden%20High%20School/secondary/about"
+ ["105574"] = "school/105574",
+ ["137552"] = "school/137552",
+ ["100273"] = "school/100273",
+ ["107564"] = "school/107564"
+
+ };
+
+ [Fact]
+ public async Task AboutSchoolPage_LoadsSuccessfully()
+ {
+ // Arrange && Act
+ var response = await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+
+ // Assert
+ Assert.NotNull(response);
+ Assert.Equal(200, response.Status);
+ }
+
+ [Fact]
+ public async Task AboutSchoolPage_HasCorrectTitle()
+ {
+ // Arrange
+ await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+
+ // Act
+ var title = await Page.TitleAsync();
+
+ // Assert
+ Assert.Contains("About the school", title);
+ }
+
+ [Fact]
+ public async Task AboutSchoolPage_DisplaysMainHeading()
+ {
+ // Arrange
+ await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+
+ // Act
+ var heading = await Page.Locator("h1").TextContentAsync();
+
+ // Assert
+ Assert.NotNull(heading);
+ Assert.NotEmpty(heading!.Trim());
+ }
+
+ [Fact]
+ public async Task AboutSchoolPage_Displays_SchoolName_Caption()
+ {
+ // Arrange
+ await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+
+ // Act
+ var schoolNameCaptionLocator = Page.Locator("#school-name-caption");
+ var isVisible = await schoolNameCaptionLocator.IsVisibleAsync();
+ var schoolNameCaption = await schoolNameCaptionLocator.TextContentAsync();
+
+ // Assert
+ Assert.True(isVisible);
+ Assert.NotNull(schoolNameCaption);
+ Assert.Equal("Loreto High School Chorlton", schoolNameCaption);
+ }
+
+ [Theory]
+ [InlineData("105574", false, false)]
+ [InlineData("137552", true, false)]
+ [InlineData("107564", true, true)]
+ public async Task AboutSchoolPage_Displays_School_Closed_Info(string urn, bool isSchoolClosed, bool hasSchoolClosedDate)
+ {
+ // Act
+ await Page.GotoAsync(_schoolUrnToUrlMap[urn]);
+
+ // Assert
+ var schoolClosedCard = Page.GetByTestId("school-closed-custom-card");
+
+ Assert.Equal(isSchoolClosed, await schoolClosedCard.IsVisibleAsync());
+
+ if (isSchoolClosed)
+ {
+ var value = await schoolClosedCard.Locator("p").TextContentAsync();
+
+ var expectedText = hasSchoolClosedDate ? "This school closed on 23 March 2025" : "Closed";
+
+ Assert.NotNull(value);
+ Assert.Equal(expectedText, value.Trim());
+ }
+ }
+
+ [Theory]
+ [InlineData("105574", false)]
+ [InlineData("137552", true)]
+ public async Task AboutSchoolPage_DisplaysSchoolDetails(string urn, bool trustNameIsExpected)
+ {
+ // Act
+ await Page.GotoAsync(_schoolUrnToUrlMap[urn]);
+
+ // Assert
+ var detailsSummary = Page.Locator("#school-details-summary");
+
+ Assert.True(await detailsSummary.IsVisibleAsync());
+ var row = detailsSummary
+ .Locator(".govuk-summary-list__row")
+ .Filter(new() { Has = Page.Locator(".govuk-summary-list__key", new() { HasText = " Academy Trust " }) });
+
+ if (trustNameIsExpected)
+ {
+ var value = await row.Locator(".govuk-summary-list__value").TextContentAsync();
+ Assert.NotNull(value);
+ Assert.Equal("THE PASSMORES CO-OPERATIVE LEARNING COMMUNITY", value.Trim());
+ }
+ else
+ {
+ Assert.False(await row.IsVisibleAsync());
+ }
+ }
+
+ [Fact]
+ public async Task AboutSchoolPage_DisplaysSchoolLocation()
+ {
+ // Arrange
+ await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+
+ // Act
+ var isVisible = await Page.Locator("#school-location-summary").IsVisibleAsync();
+
+ // Assert
+ Assert.True(isVisible);
+ }
+
+ [Fact]
+ public async Task AboutSchoolPage_DisplaysSpecialistUnit()
+ {
+ // Arrange
+ await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+
+ // Act
+ var isVisible = await Page.Locator("#details-sen").IsVisibleAsync();
+
+ // Assert
+ Assert.True(isVisible);
+ }
+
+ [Fact]
+ public async Task AboutSchoolPage_DisplaysSchoolFeatures()
+ {
+ // Arrange
+ await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+
+ // Act
+ var isVisible = await Page.Locator("#school-features-summary").IsVisibleAsync();
+
+ // Assert
+ Assert.True(isVisible);
+ }
+
+ [Fact]
+ public async Task AboutSchoolPage_DisplaysSchoolPolicies()
+ {
+ // Arrange
+ await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+
+ // Act
+ var schoolPoliciesSummaryCard = Page.Locator("#school-policies-summary");
+ var contactSchoolInfo = schoolPoliciesSummaryCard.GetByTestId("contact-school-info");
+
+ // Assert
+ Assert.True(await schoolPoliciesSummaryCard.IsVisibleAsync());
+ Assert.False(await contactSchoolInfo.IsVisibleAsync());
+ }
+
+ [Fact]
+ public async Task AboutSchoolPage_DisplaysSchoolPolicies_ContactSchoolText()
+ {
+ // Arrange
+ await Page.GotoAsync(_schoolUrnToUrlMap["100273"]);
+
+ // Act
+ var schoolPoliciesSummaryCard = Page.Locator("#school-policies-summary");
+ var contactSchoolInfo = schoolPoliciesSummaryCard.GetByTestId("contact-school-info");
+ var isVisible = await contactSchoolInfo.IsVisibleAsync();
+
+ // Assert
+ Assert.True(isVisible);
+ }
+
+ [Fact]
+ public async Task AboutSchoolPage_DisplaysPagination()
+ {
+ // Arrange
+ await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+
+ // Act
+ var isVisible = await Page.Locator("#about-the-school-pagination").IsVisibleAsync();
+
+ // Assert
+ Assert.True(isVisible);
+ }
+}
\ No newline at end of file
diff --git a/SAPPub.Integration.Tests/Infrastructure/BasePageTest.cs b/SAPPub.Integration.Tests/Infrastructure/BasePageTest.cs
new file mode 100644
index 00000000..f5e35bae
--- /dev/null
+++ b/SAPPub.Integration.Tests/Infrastructure/BasePageTest.cs
@@ -0,0 +1,75 @@
+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;
+
+ //Uncomment to run tests in headed mode
+ 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 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']");
+ }
+ }
+}
\ No newline at end of file
diff --git a/SAPPub.Integration.Tests/Infrastructure/CustomWebApplicationFactory.cs b/SAPPub.Integration.Tests/Infrastructure/CustomWebApplicationFactory.cs
new file mode 100644
index 00000000..33ddfd75
--- /dev/null
+++ b/SAPPub.Integration.Tests/Infrastructure/CustomWebApplicationFactory.cs
@@ -0,0 +1,62 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using SAPPub.Web;
+
+namespace SAPPub.Integration.Tests;
+
+public class CustomWebApplicationFactory : WebApplicationFactory
+{
+ protected override void ConfigureWebHost(IWebHostBuilder builder)
+ {
+ var isCi = string.Equals(
+ Environment.GetEnvironmentVariable("GITHUB_ACTIONS"),
+ "true",
+ StringComparison.OrdinalIgnoreCase);
+
+ // Use a dedicated env name for CI to avoid accidentally using real infra.
+ builder.UseEnvironment(isCi ? "UITests" : "Development");
+
+ builder.ConfigureAppConfiguration((context, config) =>
+ {
+ // Ensure we read appsettings + appsettings.{ENV}.json + env vars
+ config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
+ .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: false)
+ .AddEnvironmentVariables();
+
+ // guarantee a connection string exists (but it can be dummy).
+ //Port = 1 makes it effectively unreachable if anything accidentally tries to connect.
+ //config.AddInMemoryCollection(new Dictionary
+ //{
+ // ["ConnectionStrings:PostgresConnectionString"] =
+ // "Host=127.0.0.1;Port=1;Database=x;Username=x;Password=x;Timeout=1;Command Timeout=1"
+ //});
+ });
+
+ //builder.ConfigureServices((context, services) =>
+ //{
+ // services.RemoveAll(typeof(IGenericRepository<>));
+ // services.AddTransient(typeof(IGenericRepository<>), typeof(FakeGenericRepository<>));
+
+ // // BUT still register NpgsqlDataSource so DI smoke tests pass
+ // services.RemoveAll();
+ // services.AddSingleton(sp =>
+ // {
+ // var cfg = sp.GetRequiredService();
+ // var cs = cfg.GetConnectionString("PostgresConnectionString")
+ // ?? throw new InvalidOperationException("Connection string 'PostgresConnectionString' is not configured.");
+
+ // return NpgsqlDataSource.Create(cs);
+ // });
+
+ // return;
+ //});
+ }
+
+ protected override IHost CreateHost(IHostBuilder builder)
+ {
+ builder.UseContentRoot(Directory.GetCurrentDirectory());
+ return base.CreateHost(builder);
+ }
+}
diff --git a/SAPPub.Integration.Tests/Infrastructure/IntegrationTestCollection.cs b/SAPPub.Integration.Tests/Infrastructure/IntegrationTestCollection.cs
new file mode 100644
index 00000000..77a9c08a
--- /dev/null
+++ b/SAPPub.Integration.Tests/Infrastructure/IntegrationTestCollection.cs
@@ -0,0 +1,7 @@
+namespace SAPPub.Integration.Tests;
+
+[CollectionDefinition("Integration Tests", DisableParallelization = true)]
+public class IntegrationTestCollection : ICollectionFixture
+{
+}
+
diff --git a/SAPPub.Integration.Tests/Infrastructure/TestWebApplicationFactory.cs b/SAPPub.Integration.Tests/Infrastructure/TestWebApplicationFactory.cs
new file mode 100644
index 00000000..0f86f99f
--- /dev/null
+++ b/SAPPub.Integration.Tests/Infrastructure/TestWebApplicationFactory.cs
@@ -0,0 +1,197 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting.Server;
+using Microsoft.AspNetCore.Hosting.Server.Features;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using SAPPub.Core.Interfaces.Repositories.Generic;
+using SAPPub.Web;
+using SAPPub.Web.Helpers;
+
+namespace SAPPub.Integration.Tests;
+
+public class TestWebApplicationFactory : WebApplicationFactory
+{
+ private IHost? _host;
+ private static string? _cachedWebProjectPath;
+
+ protected override void ConfigureWebHost(IWebHostBuilder builder)
+ {
+ builder.UseUrls("http://127.0.0.1:0", "https://127.0.0.1:0");
+ builder.UseEnvironment("UITests");
+
+ // Set content root to web project so static files (wwwroot) are found
+ var webProjectPath = GetWebProjectPath();
+ builder.UseContentRoot(webProjectPath);
+ builder.UseWebRoot(Path.Combine(webProjectPath, "wwwroot"));
+
+ var testDataFilePath = GetTestDataFilePath();
+ var configurationValues = CreateConfigurationValues(testDataFilePath);
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(configurationValues)
+ .Build();
+
+ builder
+ .UseConfiguration(configuration)
+ .ConfigureAppConfiguration(configurationBuilder =>
+ {
+ configurationBuilder.AddInMemoryCollection(configurationValues);
+ })
+ .ConfigureServices(services =>
+ {
+ services.RemoveAll(typeof(IGenericRepository<>));
+ services.AddSingleton(typeof(IGenericRepository<>), typeof(FakeGenericRepository<>));
+ });
+ }
+
+ protected override IHost CreateHost(IHostBuilder builder)
+ {
+ // Use web project path for content root (for static files)
+ var webProjectPath = GetWebProjectPath();
+ builder.UseContentRoot(webProjectPath);
+
+ // Create the host for TestServer
+ var testHost = builder.Build();
+
+ // Modify the host builder to use Kestrel with correct content root
+ builder.ConfigureWebHost(webHostBuilder =>
+ {
+ webHostBuilder.UseKestrel();
+ webHostBuilder.UseContentRoot(webProjectPath);
+ webHostBuilder.UseWebRoot(Path.Combine(webProjectPath, "wwwroot"));
+ });
+
+ // Create and start the Kestrel server
+ _host = builder.Build();
+ _host.Start();
+
+ // Extract the selected dynamic port
+ var server = _host.Services.GetRequiredService();
+ var addresses = server.Features.Get();
+
+ ClientOptions.BaseAddress = addresses!.Addresses
+ .Select(x => new Uri(x))
+ .Last();
+
+ LogStartupInfo(webProjectPath);
+
+ testHost.Start();
+ return testHost;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _host?.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Path Resolution
+
+ private static string GetWebProjectPath()
+ {
+ // Cache the path to avoid repeated file system walks
+ if (_cachedWebProjectPath != null)
+ {
+ return _cachedWebProjectPath;
+ }
+
+ var currentDir = AppContext.BaseDirectory;
+ var directory = new DirectoryInfo(currentDir);
+
+ // Walk up to find solution root
+ while (directory != null && !directory.GetFiles("*.sln").Any())
+ {
+ directory = directory.Parent;
+ }
+
+ if (directory == null)
+ {
+ throw new InvalidOperationException(
+ $"Could not find solution root from {currentDir}");
+ }
+
+ // Find web project
+ var possiblePaths = new[]
+ {
+ Path.Combine(directory.FullName, "SAPPub.Web"),
+ Path.Combine(directory.FullName, "src", "SAPPub.Web"),
+ };
+
+ _cachedWebProjectPath = possiblePaths.FirstOrDefault(Directory.Exists)
+ ?? throw new InvalidOperationException(
+ $"Could not find SAPPub.Web. Searched: {string.Join(", ", possiblePaths)}");
+
+ return _cachedWebProjectPath;
+ }
+
+ private static string GetTestDataFilePath()
+ {
+ // First try test project's TestData folder
+ var testProjectPath = Path.Combine(
+ AppContext.BaseDirectory,
+ "UI",
+ "TestData",
+ "Establishments-UI-Test-Data.csv");
+
+ if (File.Exists(testProjectPath))
+ {
+ return testProjectPath;
+ }
+
+ // Try web project's TestData folder
+ var webProjectPath = Path.Combine(
+ GetWebProjectPath(),
+ "UI",
+ "TestData",
+ "Establishments-UI-Test-Data.csv");
+
+ if (File.Exists(webProjectPath))
+ {
+ return webProjectPath;
+ }
+
+ throw new FileNotFoundException(
+ $"Test data file not found. Searched:\n- {testProjectPath}\n- {webProjectPath}");
+ }
+
+ #endregion
+
+ #region Configuration
+
+ private static Dictionary CreateConfigurationValues(string testDataFilePath)
+ {
+ return new Dictionary
+ {
+ { "Establishments:CsvPath", testDataFilePath },
+ };
+ }
+
+ #endregion
+
+ #region Logging
+
+ private void LogStartupInfo(string webProjectPath)
+ {
+ var wwwrootPath = Path.Combine(webProjectPath, "wwwroot");
+ var jsPath = Path.Combine(wwwrootPath, "js");
+
+ if (Directory.Exists(jsPath))
+ {
+ var jsFiles = Directory.GetFiles(jsPath, "*.js", SearchOption.AllDirectories);
+ // Check for key files
+ var autocomplete = Path.Combine(jsPath, "accessible-autocomplete.min.js");
+ var debounce = Path.Combine(jsPath, "lodash.debounce", "index.js");
+
+ }
+ else
+ {
+ Console.WriteLine($"❌ JS folder not found: {jsPath}");
+ }
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/SAPPub.Integration.Tests/Infrastructure/WebApplicationSetupFixture.cs b/SAPPub.Integration.Tests/Infrastructure/WebApplicationSetupFixture.cs
new file mode 100644
index 00000000..a1b16618
--- /dev/null
+++ b/SAPPub.Integration.Tests/Infrastructure/WebApplicationSetupFixture.cs
@@ -0,0 +1,27 @@
+namespace SAPPub.Integration.Tests;
+
+public class WebApplicationSetupFixture : IAsyncLifetime
+{
+ private TestWebApplicationFactory? _factory;
+
+ public string BaseUrl { get; private set; } = null!;
+
+ public Task InitializeAsync()
+ {
+ _factory = new TestWebApplicationFactory();
+
+ if (_factory.Server == null) throw new InvalidOperationException("Test Server not started");
+
+ BaseUrl = _factory.ClientOptions.BaseAddress.ToString();
+
+ return Task.CompletedTask;
+ }
+
+ public async Task DisposeAsync()
+ {
+ if (_factory != null)
+ {
+ await _factory.DisposeAsync();
+ }
+ }
+}
diff --git a/SAPPub.Integration.Tests/PageTests.cs b/SAPPub.Integration.Tests/PageTests.cs
new file mode 100644
index 00000000..23877812
--- /dev/null
+++ b/SAPPub.Integration.Tests/PageTests.cs
@@ -0,0 +1,58 @@
+using System.Net;
+
+namespace SAPPub.Integration.Tests;
+
+public class PageTests : IClassFixture
+{
+ private readonly HttpClient _client;
+ public PageTests(CustomWebApplicationFactory factory)
+ {
+ _client = factory.CreateClient();
+ }
+
+ [Fact]
+ public async Task HomePage_ReturnsSuccess()
+ {
+ // Act
+ var response = await _client.GetAsync("/");
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/html", response.Content.Headers.ContentType?.MediaType);
+ }
+
+ [Theory]
+ [InlineData("/")]
+ [InlineData("/health")]
+ [InlineData("/healthcheck")]
+ public async Task CommonPages_ReturnSuccess(string url)
+ {
+ // Act
+ var response = await _client.GetAsync(url);
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task Response_ContainsSecurityHeaders()
+ {
+ // Act
+ var response = await _client.GetAsync("/");
+ // Assert
+ Assert.True(response.Headers.Contains("X-Content-Type-Options"));
+ Assert.True(response.Headers.Contains("X-Frame-Options"));
+ Assert.True(response.Headers.Contains("Referrer-Policy"));
+ var xFrameOptions = response.Headers.GetValues("X-Frame-Options").First();
+ Assert.Equal("DENY", xFrameOptions);
+ }
+
+ [Fact]
+ public async Task Response_ContainsContentSecurityPolicy()
+ {
+ // Act
+ var response = await _client.GetAsync("/");
+ // Assert
+ Assert.True(response.Headers.Contains("Content-Security-Policy"));
+ var csp = response.Headers.GetValues("Content-Security-Policy").First();
+ Assert.Contains("default-src 'self'", csp);
+ }
+}
\ No newline at end of file
diff --git a/SAPPub.Integration.Tests/SAPPub.Integration.Tests.csproj b/SAPPub.Integration.Tests/SAPPub.Integration.Tests.csproj
new file mode 100644
index 00000000..5e359e59
--- /dev/null
+++ b/SAPPub.Integration.Tests/SAPPub.Integration.Tests.csproj
@@ -0,0 +1,34 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SAPPub.Integration.Tests/SearchTests.cs b/SAPPub.Integration.Tests/SearchTests.cs
new file mode 100644
index 00000000..0c8cf02f
--- /dev/null
+++ b/SAPPub.Integration.Tests/SearchTests.cs
@@ -0,0 +1,163 @@
+using Microsoft.Playwright;
+
+namespace SAPPub.Integration.Tests;
+
+[Collection("Integration Tests")]
+public class SearchTests(WebApplicationSetupFixture fixture) : BasePageTest(fixture)
+{
+ private string _pageUrl = "search";
+
+ [Fact]
+ public async Task SearchPage_LoadsSuccessfully()
+ {
+ // Arrange && Act
+ var response = await Page.GotoAsync(_pageUrl);
+
+ // Assert
+ Assert.NotNull(response);
+ Assert.Equal(200, response.Status);
+ }
+
+ [Fact]
+ public async Task SearchPage_EnterSchoolName_ShowsViewWithResults()
+ {
+ // Arrange
+ var searchTerm = "durham";
+ var response = await Page.GotoAsync(_pageUrl);
+
+ // Act
+ await Page.FillAsync("#NameSearchTerm", searchTerm);
+ await Page.ClickAsync("#search");
+ await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+
+ // Assert
+ Assert.NotNull(response);
+ Assert.Equal(200, response.Status);
+ // assert text box contains search term
+ var searchBoxValue = await Page.InputValueAsync("#NameSearchTerm");
+ Assert.Equal(searchTerm, searchBoxValue);
+
+ // assert that at least one search result is displayed
+ var rows = Page.Locator(".govuk-summary-list .govuk-summary-list__row");
+ var rowHandles = await rows.ElementHandlesAsync();
+ int count = await rows.CountAsync();
+ Assert.True(count > 0, "Expected at least one search result, but found none.");
+ }
+
+ [Fact]
+ public async Task SearchPage_EnterSchoolName_NoResults_ShowsViewWithNoResults()
+ {
+ // Arrange
+ var searchTerm = "xyz";
+ var response = await Page.GotoAsync(_pageUrl);
+
+ // Act
+ await Page.FillAsync("#NameSearchTerm", searchTerm);
+ await Page.ClickAsync("#search");
+ await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+
+ // Assert
+ Assert.NotNull(response);
+ Assert.Equal(200, response.Status);
+ // assert text box contains search term
+ var searchBoxValue = await Page.InputValueAsync("#NameSearchTerm ");
+ Assert.Equal(searchTerm, searchBoxValue);
+
+ // assert no list items are displayed
+ var rows = Page.Locator(".govuk-summary-list .govuk-summary-list__row");
+ var rowHandles = await rows.ElementHandlesAsync();
+ int count = await rows.CountAsync();
+ Assert.True(count == 0, "Expected no search results, but found some.");
+
+ // assert that the no results message is displayed
+ var noResultsMessage = await Page.Locator("[data-testid='no-results-heading']").InnerTextAsync();
+ Assert.Contains("Try another search", noResultsMessage);
+ }
+
+ [Fact]
+ public async Task SearchPage_EnterValidPostcode_ShowsViewWithResults()
+ {
+ // Arrange
+ var searchTerm = "M21 7SW";
+ var response = await Page.GotoAsync(_pageUrl);
+
+ // Act
+ await Page.FillAsync("#LocationSearchTerm", searchTerm);
+ await Page.ClickAsync("#search");
+ await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+
+ // Assert
+ Assert.NotNull(response);
+ Assert.Equal(200, response.Status);
+ // assert text box contains search term
+ var searchBoxValue = await Page.InputValueAsync("#LocationSearchTerm");
+ Assert.Equal(searchTerm, searchBoxValue);
+
+ // assert that at least one search result is displayed
+ var rows = Page.Locator(".govuk-summary-list .govuk-summary-list__row");
+ var rowHandles = await rows.ElementHandlesAsync();
+ int count = await rows.CountAsync();
+ Assert.True(count > 0, "Expected at least one search result, but found none.");
+ }
+
+ [Fact]
+ public async Task SearchPage_EnterInvalidPostcode_ShowsViewWithErrorMessge()
+ {
+ // Arrange
+ var searchTerm = "NE1";
+ var response = await Page.GotoAsync(_pageUrl);
+
+ // Act
+ await Page.FillAsync("#LocationSearchTerm", searchTerm);
+ await Page.ClickAsync("#search");
+ await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+
+ // Assert
+ Assert.NotNull(response);
+ Assert.Equal(200, response.Status);
+ // assert text box contains search term
+ var searchBoxValue = await Page.InputValueAsync("#LocationSearchTerm");
+ Assert.Equal(searchTerm, searchBoxValue);
+
+ // error box is displayed with correct message
+ var errorLink = Page.Locator(".govuk-error-summary__list a[href='#LocationSearchTerm']");
+ Assert.Equal("Enter a full postcode", await errorLink.InnerTextAsync());
+
+ // assert no list items are displayed
+ var rows = Page.Locator(".govuk-summary-list .govuk-summary-list__row");
+ var rowHandles = await rows.ElementHandlesAsync();
+ int count = await rows.CountAsync();
+ Assert.True(count == 0, "Expected no search results, but found some.");
+ }
+
+ [Fact]
+ public async Task SearchPage_Enter_Closed_SchoolName_ShowsViewWithResults()
+ {
+ // Arrange
+ var searchTerm = "Todmorden High School";
+ var response = await Page.GotoAsync(_pageUrl);
+
+ // Act
+ await Page.FillAsync("#NameSearchTerm", searchTerm);
+ await Page.ClickAsync("#search");
+ await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+
+ // Assert
+ Assert.NotNull(response);
+ Assert.Equal(200, response.Status);
+ // assert text box contains search term
+ var searchBoxValue = await Page.InputValueAsync("#NameSearchTerm");
+ Assert.Equal(searchTerm, searchBoxValue);
+
+ // assert that at least one search result is displayed
+ var rows = Page.GetByTestId("school-closed-tag");
+
+ var rowHandles = await rows.ElementHandlesAsync();
+ int count = await rows.CountAsync();
+ Assert.True(count == 1, "Expected one closed school, but found more than one.");
+
+ var value = await rows.First.TextContentAsync();
+ Assert.NotNull(value);
+ Assert.Equal("Closed in March 2025", value.Trim());
+ }
+}
diff --git a/SAPPub.Integration.Tests/UnitTest1.cs b/SAPPub.Integration.Tests/UnitTest1.cs
new file mode 100644
index 00000000..e2db4097
--- /dev/null
+++ b/SAPPub.Integration.Tests/UnitTest1.cs
@@ -0,0 +1,11 @@
+namespace SAPPub.Integration.Tests
+{
+ public class UnitTest1
+ {
+ [Fact]
+ public void Test1()
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/SAPPub.Web/Views/SecondarySchool/AcademicPerformanceEnglishAndMathsResults.cshtml b/SAPPub.Web/Views/SecondarySchool/AcademicPerformanceEnglishAndMathsResults.cshtml
index afd65595..d2109f52 100644
--- a/SAPPub.Web/Views/SecondarySchool/AcademicPerformanceEnglishAndMathsResults.cshtml
+++ b/SAPPub.Web/Views/SecondarySchool/AcademicPerformanceEnglishAndMathsResults.cshtml
@@ -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")
+ @if(false)
+ {
@@ -170,7 +172,36 @@
-
+
+ }
+ else
+ {
+
+
+
+
+
+ @foreach (var label in Model.AllGcseOverTimeData.Labels)
+ {
+
+ }
+
+
+
+ @foreach (var dataset in Model.AllGcseOverTimeData.Datasets.Select((value, i) => (value, i)))
+ {
+
+
+ @foreach (var data in dataset.value.Data)
+ {
+ | @data% |
+ }
+
+ }
+
+
+
+ }
diff --git a/SAPPub.sln b/SAPPub.sln
index 44933707..6c972bff 100644
--- a/SAPPub.sln
+++ b/SAPPub.sln
@@ -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
@@ -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.Integration.Tests", "SAPPub.Integration.Tests\SAPPub.Integration.Tests.csproj", "{F638C830-CD9C-47A3-8763-33611A98CD3B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -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
+ {F638C830-CD9C-47A3-8763-33611A98CD3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F638C830-CD9C-47A3-8763-33611A98CD3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F638C830-CD9C-47A3-8763-33611A98CD3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F638C830-CD9C-47A3-8763-33611A98CD3B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -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}
+ {F638C830-CD9C-47A3-8763-33611A98CD3B} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0D7E6B09-2E5D-4BA4-899B-FBCC5A7F92CD}
diff --git a/Tests/SAPPub.Web.Tests/UI/Infrastructure/BasePageTest.cs b/Tests/SAPPub.Web.Tests/UI/Infrastructure/BasePageTest.cs
index d571a7a7..ff1c83a9 100644
--- a/Tests/SAPPub.Web.Tests/UI/Infrastructure/BasePageTest.cs
+++ b/Tests/SAPPub.Web.Tests/UI/Infrastructure/BasePageTest.cs
@@ -13,7 +13,7 @@ protected BasePageTest(WebApplicationSetupFixture fixture)
_fixture = fixture;
//Uncomment to run tests in headed mode
- //Environment.SetEnvironmentVariable("HEADED", "1");
+ Environment.SetEnvironmentVariable("HEADED", "1");
}
public override BrowserNewContextOptions ContextOptions()
From 479f408aff8bdfe9a3cb563bc36d3f4b5c9a104f Mon Sep 17 00:00:00 2001
From: CLAWLOR
Date: Thu, 9 Apr 2026 18:50:04 +0100
Subject: [PATCH 2/2] first tests - wip
---
.github/workflows/build-and-deploy.yml | 67 +++++++
.../CustomWebApplicationFactory.cs | 62 -------
SAPPub.Integration.Tests/PageTests.cs | 58 -------
SAPPub.Integration.Tests/SearchTests.cs | 163 ------------------
SAPPub.Integration.Tests/UnitTest1.cs | 11 --
SAPPub.Web/Program.cs | 10 +-
SAPPub.Web/appsettings.Development.json | 5 +-
SAPPub.sln | 12 +-
.../Infrastructure/BasePageTest.cs | 6 +-
.../IntegrationTestCollection.cs | 0
.../TestWebApplicationFactory.cs | 62 ++++---
.../WebApplicationSetupFixture.cs | 7 +
.../SAPPub.IntegrationTests.csproj | 10 +-
.../AboutSchoolPageTests.cs | 98 +++--------
.../AttainmentPageTests.cs | 78 +++++++++
15 files changed, 237 insertions(+), 412 deletions(-)
delete mode 100644 SAPPub.Integration.Tests/Infrastructure/CustomWebApplicationFactory.cs
delete mode 100644 SAPPub.Integration.Tests/PageTests.cs
delete mode 100644 SAPPub.Integration.Tests/SearchTests.cs
delete mode 100644 SAPPub.Integration.Tests/UnitTest1.cs
rename {SAPPub.Integration.Tests => Tests/SAPPub.Integration.Tests}/Infrastructure/BasePageTest.cs (95%)
rename {SAPPub.Integration.Tests => Tests/SAPPub.Integration.Tests}/Infrastructure/IntegrationTestCollection.cs (100%)
rename {SAPPub.Integration.Tests => Tests/SAPPub.Integration.Tests}/Infrastructure/TestWebApplicationFactory.cs (73%)
rename {SAPPub.Integration.Tests => Tests/SAPPub.Integration.Tests}/Infrastructure/WebApplicationSetupFixture.cs (74%)
rename SAPPub.Integration.Tests/SAPPub.Integration.Tests.csproj => Tests/SAPPub.Integration.Tests/SAPPub.IntegrationTests.csproj (84%)
rename {SAPPub.Integration.Tests => Tests/SAPPub.Integration.Tests/SecondarySchoolTests}/AboutSchoolPageTests.cs (50%)
create mode 100644 Tests/SAPPub.Integration.Tests/SecondarySchoolTests/AttainmentPageTests.cs
diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml
index 1a34972f..b0bd8295 100644
--- a/.github/workflows/build-and-deploy.yml
+++ b/.github/workflows/build-and-deploy.yml
@@ -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
# ---------------------------
diff --git a/SAPPub.Integration.Tests/Infrastructure/CustomWebApplicationFactory.cs b/SAPPub.Integration.Tests/Infrastructure/CustomWebApplicationFactory.cs
deleted file mode 100644
index 33ddfd75..00000000
--- a/SAPPub.Integration.Tests/Infrastructure/CustomWebApplicationFactory.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Mvc.Testing;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Hosting;
-using SAPPub.Web;
-
-namespace SAPPub.Integration.Tests;
-
-public class CustomWebApplicationFactory : WebApplicationFactory
-{
- protected override void ConfigureWebHost(IWebHostBuilder builder)
- {
- var isCi = string.Equals(
- Environment.GetEnvironmentVariable("GITHUB_ACTIONS"),
- "true",
- StringComparison.OrdinalIgnoreCase);
-
- // Use a dedicated env name for CI to avoid accidentally using real infra.
- builder.UseEnvironment(isCi ? "UITests" : "Development");
-
- builder.ConfigureAppConfiguration((context, config) =>
- {
- // Ensure we read appsettings + appsettings.{ENV}.json + env vars
- config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
- .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: false)
- .AddEnvironmentVariables();
-
- // guarantee a connection string exists (but it can be dummy).
- //Port = 1 makes it effectively unreachable if anything accidentally tries to connect.
- //config.AddInMemoryCollection(new Dictionary
- //{
- // ["ConnectionStrings:PostgresConnectionString"] =
- // "Host=127.0.0.1;Port=1;Database=x;Username=x;Password=x;Timeout=1;Command Timeout=1"
- //});
- });
-
- //builder.ConfigureServices((context, services) =>
- //{
- // services.RemoveAll(typeof(IGenericRepository<>));
- // services.AddTransient(typeof(IGenericRepository<>), typeof(FakeGenericRepository<>));
-
- // // BUT still register NpgsqlDataSource so DI smoke tests pass
- // services.RemoveAll();
- // services.AddSingleton(sp =>
- // {
- // var cfg = sp.GetRequiredService();
- // var cs = cfg.GetConnectionString("PostgresConnectionString")
- // ?? throw new InvalidOperationException("Connection string 'PostgresConnectionString' is not configured.");
-
- // return NpgsqlDataSource.Create(cs);
- // });
-
- // return;
- //});
- }
-
- protected override IHost CreateHost(IHostBuilder builder)
- {
- builder.UseContentRoot(Directory.GetCurrentDirectory());
- return base.CreateHost(builder);
- }
-}
diff --git a/SAPPub.Integration.Tests/PageTests.cs b/SAPPub.Integration.Tests/PageTests.cs
deleted file mode 100644
index 23877812..00000000
--- a/SAPPub.Integration.Tests/PageTests.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System.Net;
-
-namespace SAPPub.Integration.Tests;
-
-public class PageTests : IClassFixture
-{
- private readonly HttpClient _client;
- public PageTests(CustomWebApplicationFactory factory)
- {
- _client = factory.CreateClient();
- }
-
- [Fact]
- public async Task HomePage_ReturnsSuccess()
- {
- // Act
- var response = await _client.GetAsync("/");
- // Assert
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- Assert.Equal("text/html", response.Content.Headers.ContentType?.MediaType);
- }
-
- [Theory]
- [InlineData("/")]
- [InlineData("/health")]
- [InlineData("/healthcheck")]
- public async Task CommonPages_ReturnSuccess(string url)
- {
- // Act
- var response = await _client.GetAsync(url);
- // Assert
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- }
-
- [Fact]
- public async Task Response_ContainsSecurityHeaders()
- {
- // Act
- var response = await _client.GetAsync("/");
- // Assert
- Assert.True(response.Headers.Contains("X-Content-Type-Options"));
- Assert.True(response.Headers.Contains("X-Frame-Options"));
- Assert.True(response.Headers.Contains("Referrer-Policy"));
- var xFrameOptions = response.Headers.GetValues("X-Frame-Options").First();
- Assert.Equal("DENY", xFrameOptions);
- }
-
- [Fact]
- public async Task Response_ContainsContentSecurityPolicy()
- {
- // Act
- var response = await _client.GetAsync("/");
- // Assert
- Assert.True(response.Headers.Contains("Content-Security-Policy"));
- var csp = response.Headers.GetValues("Content-Security-Policy").First();
- Assert.Contains("default-src 'self'", csp);
- }
-}
\ No newline at end of file
diff --git a/SAPPub.Integration.Tests/SearchTests.cs b/SAPPub.Integration.Tests/SearchTests.cs
deleted file mode 100644
index 0c8cf02f..00000000
--- a/SAPPub.Integration.Tests/SearchTests.cs
+++ /dev/null
@@ -1,163 +0,0 @@
-using Microsoft.Playwright;
-
-namespace SAPPub.Integration.Tests;
-
-[Collection("Integration Tests")]
-public class SearchTests(WebApplicationSetupFixture fixture) : BasePageTest(fixture)
-{
- private string _pageUrl = "search";
-
- [Fact]
- public async Task SearchPage_LoadsSuccessfully()
- {
- // Arrange && Act
- var response = await Page.GotoAsync(_pageUrl);
-
- // Assert
- Assert.NotNull(response);
- Assert.Equal(200, response.Status);
- }
-
- [Fact]
- public async Task SearchPage_EnterSchoolName_ShowsViewWithResults()
- {
- // Arrange
- var searchTerm = "durham";
- var response = await Page.GotoAsync(_pageUrl);
-
- // Act
- await Page.FillAsync("#NameSearchTerm", searchTerm);
- await Page.ClickAsync("#search");
- await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
-
- // Assert
- Assert.NotNull(response);
- Assert.Equal(200, response.Status);
- // assert text box contains search term
- var searchBoxValue = await Page.InputValueAsync("#NameSearchTerm");
- Assert.Equal(searchTerm, searchBoxValue);
-
- // assert that at least one search result is displayed
- var rows = Page.Locator(".govuk-summary-list .govuk-summary-list__row");
- var rowHandles = await rows.ElementHandlesAsync();
- int count = await rows.CountAsync();
- Assert.True(count > 0, "Expected at least one search result, but found none.");
- }
-
- [Fact]
- public async Task SearchPage_EnterSchoolName_NoResults_ShowsViewWithNoResults()
- {
- // Arrange
- var searchTerm = "xyz";
- var response = await Page.GotoAsync(_pageUrl);
-
- // Act
- await Page.FillAsync("#NameSearchTerm", searchTerm);
- await Page.ClickAsync("#search");
- await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
-
- // Assert
- Assert.NotNull(response);
- Assert.Equal(200, response.Status);
- // assert text box contains search term
- var searchBoxValue = await Page.InputValueAsync("#NameSearchTerm ");
- Assert.Equal(searchTerm, searchBoxValue);
-
- // assert no list items are displayed
- var rows = Page.Locator(".govuk-summary-list .govuk-summary-list__row");
- var rowHandles = await rows.ElementHandlesAsync();
- int count = await rows.CountAsync();
- Assert.True(count == 0, "Expected no search results, but found some.");
-
- // assert that the no results message is displayed
- var noResultsMessage = await Page.Locator("[data-testid='no-results-heading']").InnerTextAsync();
- Assert.Contains("Try another search", noResultsMessage);
- }
-
- [Fact]
- public async Task SearchPage_EnterValidPostcode_ShowsViewWithResults()
- {
- // Arrange
- var searchTerm = "M21 7SW";
- var response = await Page.GotoAsync(_pageUrl);
-
- // Act
- await Page.FillAsync("#LocationSearchTerm", searchTerm);
- await Page.ClickAsync("#search");
- await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
-
- // Assert
- Assert.NotNull(response);
- Assert.Equal(200, response.Status);
- // assert text box contains search term
- var searchBoxValue = await Page.InputValueAsync("#LocationSearchTerm");
- Assert.Equal(searchTerm, searchBoxValue);
-
- // assert that at least one search result is displayed
- var rows = Page.Locator(".govuk-summary-list .govuk-summary-list__row");
- var rowHandles = await rows.ElementHandlesAsync();
- int count = await rows.CountAsync();
- Assert.True(count > 0, "Expected at least one search result, but found none.");
- }
-
- [Fact]
- public async Task SearchPage_EnterInvalidPostcode_ShowsViewWithErrorMessge()
- {
- // Arrange
- var searchTerm = "NE1";
- var response = await Page.GotoAsync(_pageUrl);
-
- // Act
- await Page.FillAsync("#LocationSearchTerm", searchTerm);
- await Page.ClickAsync("#search");
- await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
-
- // Assert
- Assert.NotNull(response);
- Assert.Equal(200, response.Status);
- // assert text box contains search term
- var searchBoxValue = await Page.InputValueAsync("#LocationSearchTerm");
- Assert.Equal(searchTerm, searchBoxValue);
-
- // error box is displayed with correct message
- var errorLink = Page.Locator(".govuk-error-summary__list a[href='#LocationSearchTerm']");
- Assert.Equal("Enter a full postcode", await errorLink.InnerTextAsync());
-
- // assert no list items are displayed
- var rows = Page.Locator(".govuk-summary-list .govuk-summary-list__row");
- var rowHandles = await rows.ElementHandlesAsync();
- int count = await rows.CountAsync();
- Assert.True(count == 0, "Expected no search results, but found some.");
- }
-
- [Fact]
- public async Task SearchPage_Enter_Closed_SchoolName_ShowsViewWithResults()
- {
- // Arrange
- var searchTerm = "Todmorden High School";
- var response = await Page.GotoAsync(_pageUrl);
-
- // Act
- await Page.FillAsync("#NameSearchTerm", searchTerm);
- await Page.ClickAsync("#search");
- await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
-
- // Assert
- Assert.NotNull(response);
- Assert.Equal(200, response.Status);
- // assert text box contains search term
- var searchBoxValue = await Page.InputValueAsync("#NameSearchTerm");
- Assert.Equal(searchTerm, searchBoxValue);
-
- // assert that at least one search result is displayed
- var rows = Page.GetByTestId("school-closed-tag");
-
- var rowHandles = await rows.ElementHandlesAsync();
- int count = await rows.CountAsync();
- Assert.True(count == 1, "Expected one closed school, but found more than one.");
-
- var value = await rows.First.TextContentAsync();
- Assert.NotNull(value);
- Assert.Equal("Closed in March 2025", value.Trim());
- }
-}
diff --git a/SAPPub.Integration.Tests/UnitTest1.cs b/SAPPub.Integration.Tests/UnitTest1.cs
deleted file mode 100644
index e2db4097..00000000
--- a/SAPPub.Integration.Tests/UnitTest1.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace SAPPub.Integration.Tests
-{
- public class UnitTest1
- {
- [Fact]
- public void Test1()
- {
-
- }
- }
-}
\ No newline at end of file
diff --git a/SAPPub.Web/Program.cs b/SAPPub.Web/Program.cs
index b09328f2..2e8decca 100644
--- a/SAPPub.Web/Program.cs
+++ b/SAPPub.Web/Program.cs
@@ -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.Create(connectionString));
+ builder.Services.AddSingleton(_ =>
+ {
+ var builder = new NpgsqlDataSourceBuilder(connectionString);
+ builder.EnableParameterLogging();
+ return builder.Build();
+ });
builder.Services.AddDependencies(builder.Environment, builder.Configuration);
builder.Services.AddLuceneDependencies();
diff --git a/SAPPub.Web/appsettings.Development.json b/SAPPub.Web/appsettings.Development.json
index 027d3798..37653a82 100644
--- a/SAPPub.Web/appsettings.Development.json
+++ b/SAPPub.Web/appsettings.Development.json
@@ -6,6 +6,9 @@
}
},
"ConnectionStrings": {
- "PostgresConnectionString": "Host=localhost;Port=5432;Database=SAPData;Username=postgres;Password=postgres"
+ "PostgresConnectionString": ""
+ },
+ "Playwright": {
+ "Headed": true
}
}
diff --git a/SAPPub.sln b/SAPPub.sln
index 6c972bff..63df417f 100644
--- a/SAPPub.sln
+++ b/SAPPub.sln
@@ -28,7 +28,7 @@ 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.Integration.Tests", "SAPPub.Integration.Tests\SAPPub.Integration.Tests.csproj", "{F638C830-CD9C-47A3-8763-33611A98CD3B}"
+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
@@ -68,10 +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
- {F638C830-CD9C-47A3-8763-33611A98CD3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F638C830-CD9C-47A3-8763-33611A98CD3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F638C830-CD9C-47A3-8763-33611A98CD3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F638C830-CD9C-47A3-8763-33611A98CD3B}.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
@@ -81,7 +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}
- {F638C830-CD9C-47A3-8763-33611A98CD3B} = {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}
diff --git a/SAPPub.Integration.Tests/Infrastructure/BasePageTest.cs b/Tests/SAPPub.Integration.Tests/Infrastructure/BasePageTest.cs
similarity index 95%
rename from SAPPub.Integration.Tests/Infrastructure/BasePageTest.cs
rename to Tests/SAPPub.Integration.Tests/Infrastructure/BasePageTest.cs
index f5e35bae..d3faba69 100644
--- a/SAPPub.Integration.Tests/Infrastructure/BasePageTest.cs
+++ b/Tests/SAPPub.Integration.Tests/Infrastructure/BasePageTest.cs
@@ -12,8 +12,10 @@ protected BasePageTest(WebApplicationSetupFixture fixture)
{
_fixture = fixture;
- //Uncomment to run tests in headed mode
- Environment.SetEnvironmentVariable("HEADED", "1");
+ if (_fixture.IsHeaded())
+ {
+ Environment.SetEnvironmentVariable("HEADED", "1");
+ }
}
public override BrowserNewContextOptions ContextOptions()
diff --git a/SAPPub.Integration.Tests/Infrastructure/IntegrationTestCollection.cs b/Tests/SAPPub.Integration.Tests/Infrastructure/IntegrationTestCollection.cs
similarity index 100%
rename from SAPPub.Integration.Tests/Infrastructure/IntegrationTestCollection.cs
rename to Tests/SAPPub.Integration.Tests/Infrastructure/IntegrationTestCollection.cs
diff --git a/SAPPub.Integration.Tests/Infrastructure/TestWebApplicationFactory.cs b/Tests/SAPPub.Integration.Tests/Infrastructure/TestWebApplicationFactory.cs
similarity index 73%
rename from SAPPub.Integration.Tests/Infrastructure/TestWebApplicationFactory.cs
rename to Tests/SAPPub.Integration.Tests/Infrastructure/TestWebApplicationFactory.cs
index 0f86f99f..987864be 100644
--- a/SAPPub.Integration.Tests/Infrastructure/TestWebApplicationFactory.cs
+++ b/Tests/SAPPub.Integration.Tests/Infrastructure/TestWebApplicationFactory.cs
@@ -5,9 +5,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
-using SAPPub.Core.Interfaces.Repositories.Generic;
using SAPPub.Web;
-using SAPPub.Web.Helpers;
namespace SAPPub.Integration.Tests;
@@ -16,33 +14,49 @@ public class TestWebApplicationFactory : WebApplicationFactory
private IHost? _host;
private static string? _cachedWebProjectPath;
+ public IConfiguration GetAppConfiguration()
+ {
+ using var scope = Services.CreateScope();
+ return scope.ServiceProvider.GetRequiredService();
+ }
+
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseUrls("http://127.0.0.1:0", "https://127.0.0.1:0");
- builder.UseEnvironment("UITests");
// Set content root to web project so static files (wwwroot) are found
- var webProjectPath = GetWebProjectPath();
- builder.UseContentRoot(webProjectPath);
- builder.UseWebRoot(Path.Combine(webProjectPath, "wwwroot"));
-
- var testDataFilePath = GetTestDataFilePath();
- var configurationValues = CreateConfigurationValues(testDataFilePath);
- var configuration = new ConfigurationBuilder()
- .AddInMemoryCollection(configurationValues)
- .Build();
-
- builder
- .UseConfiguration(configuration)
- .ConfigureAppConfiguration(configurationBuilder =>
- {
- configurationBuilder.AddInMemoryCollection(configurationValues);
- })
- .ConfigureServices(services =>
- {
- services.RemoveAll(typeof(IGenericRepository<>));
- services.AddSingleton(typeof(IGenericRepository<>), typeof(FakeGenericRepository<>));
- });
+ //var webProjectPath = GetWebProjectPath();
+ //builder.UseContentRoot(webProjectPath);
+ //builder.UseWebRoot(Path.Combine(webProjectPath, "wwwroot"));
+
+
+ //---------------------------------------------------------------------------------------
+ // shouldn't need any of this - pipeline should use the web app's standard config
+ // but leaving here for now in case we need to override config or services for testing purposes
+ // ------------------------------------------------------------------------------------------------
+ //var testDataFilePath = GetTestDataFilePath();
+ //var configurationValues = CreateConfigurationValues(testDataFilePath);
+ //var configuration = new ConfigurationBuilder()
+ // .AddInMemoryCollection(configurationValues)
+ // .Build();
+
+ //builder
+ // .UseConfiguration(configuration)
+ // .ConfigureAppConfiguration(configurationBuilder =>
+ // {
+ // configurationBuilder.AddInMemoryCollection(configurationValues);
+ // })
+ // .ConfigureServices(services =>
+ // {
+ // services.RemoveAll(typeof(IGenericRepository<>));
+ // services.AddSingleton(typeof(IGenericRepository<>), typeof(FakeGenericRepository<>));
+ // });
+
+ //builder.ConfigureAppConfiguration((context, config) =>
+ //{
+ // config.AddJsonFile("appsettings.json")
+ // .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true);
+ //});
}
protected override IHost CreateHost(IHostBuilder builder)
diff --git a/SAPPub.Integration.Tests/Infrastructure/WebApplicationSetupFixture.cs b/Tests/SAPPub.Integration.Tests/Infrastructure/WebApplicationSetupFixture.cs
similarity index 74%
rename from SAPPub.Integration.Tests/Infrastructure/WebApplicationSetupFixture.cs
rename to Tests/SAPPub.Integration.Tests/Infrastructure/WebApplicationSetupFixture.cs
index a1b16618..f46f6663 100644
--- a/SAPPub.Integration.Tests/Infrastructure/WebApplicationSetupFixture.cs
+++ b/Tests/SAPPub.Integration.Tests/Infrastructure/WebApplicationSetupFixture.cs
@@ -17,6 +17,13 @@ public Task InitializeAsync()
return Task.CompletedTask;
}
+ public bool IsHeaded()
+ {
+ return _factory?
+ .GetAppConfiguration()
+ .GetSection("Playwright")["Headed"] is string headed && bool.TryParse(headed, out var isHeaded) && isHeaded;
+ }
+
public async Task DisposeAsync()
{
if (_factory != null)
diff --git a/SAPPub.Integration.Tests/SAPPub.Integration.Tests.csproj b/Tests/SAPPub.Integration.Tests/SAPPub.IntegrationTests.csproj
similarity index 84%
rename from SAPPub.Integration.Tests/SAPPub.Integration.Tests.csproj
rename to Tests/SAPPub.Integration.Tests/SAPPub.IntegrationTests.csproj
index 5e359e59..32c57a03 100644
--- a/SAPPub.Integration.Tests/SAPPub.Integration.Tests.csproj
+++ b/Tests/SAPPub.Integration.Tests/SAPPub.IntegrationTests.csproj
@@ -1,4 +1,4 @@
-
+
net8.0
@@ -16,15 +16,15 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
-
-
+
+
-
+
diff --git a/SAPPub.Integration.Tests/AboutSchoolPageTests.cs b/Tests/SAPPub.Integration.Tests/SecondarySchoolTests/AboutSchoolPageTests.cs
similarity index 50%
rename from SAPPub.Integration.Tests/AboutSchoolPageTests.cs
rename to Tests/SAPPub.Integration.Tests/SecondarySchoolTests/AboutSchoolPageTests.cs
index c1377654..adf075d6 100644
--- a/SAPPub.Integration.Tests/AboutSchoolPageTests.cs
+++ b/Tests/SAPPub.Integration.Tests/SecondarySchoolTests/AboutSchoolPageTests.cs
@@ -1,26 +1,17 @@
-namespace SAPPub.Integration.Tests;
+using SAPPub.Integration.Tests;
+
+namespace SAPPub.IntegrationTests.SecondarySchoolTests;
[Collection("Integration Tests")]
public class AboutSchoolPageTests(WebApplicationSetupFixture fixture) : BasePageTest(fixture)
{
- private Dictionary _schoolUrnToUrlMap = new Dictionary
- {
- //["105574"] = "school/105574/Loreto%20High%20School%20Chorlton/secondary/about",
- //["137552"] = "school/137552/Stewards%20Academy%20-%20Science%20Specialist%2C%20Harlow/secondary/about",
- //["100273"] = "school/100273/Saint%20Paul%20Roman%20Catholic%20Infant%20School/secondary/about",
- //["107564"] = "school/107564/Todmorden%20High%20School/secondary/about"
- ["105574"] = "school/105574",
- ["137552"] = "school/137552",
- ["100273"] = "school/100273",
- ["107564"] = "school/107564"
-
- };
+ private string PageUrl(string urn) => $"/school/{urn}";
[Fact]
public async Task AboutSchoolPage_LoadsSuccessfully()
{
// Arrange && Act
- var response = await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+ var response = await Page.GotoAsync(PageUrl("105574"));
// Assert
Assert.NotNull(response);
@@ -30,8 +21,8 @@ public async Task AboutSchoolPage_LoadsSuccessfully()
[Fact]
public async Task AboutSchoolPage_HasCorrectTitle()
{
- // Arrange
- await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+ // PageUrl
+ await Page.GotoAsync(PageUrl("105574"));
// Act
var title = await Page.TitleAsync();
@@ -44,7 +35,7 @@ public async Task AboutSchoolPage_HasCorrectTitle()
public async Task AboutSchoolPage_DisplaysMainHeading()
{
// Arrange
- await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+ await Page.GotoAsync(PageUrl("105574"));
// Act
var heading = await Page.Locator("h1").TextContentAsync();
@@ -58,7 +49,7 @@ public async Task AboutSchoolPage_DisplaysMainHeading()
public async Task AboutSchoolPage_Displays_SchoolName_Caption()
{
// Arrange
- await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+ await Page.GotoAsync(PageUrl("105574"));
// Act
var schoolNameCaptionLocator = Page.Locator("#school-name-caption");
@@ -72,13 +63,11 @@ public async Task AboutSchoolPage_Displays_SchoolName_Caption()
}
[Theory]
- [InlineData("105574", false, false)]
- [InlineData("137552", true, false)]
- [InlineData("107564", true, true)]
- public async Task AboutSchoolPage_Displays_School_Closed_Info(string urn, bool isSchoolClosed, bool hasSchoolClosedDate)
+ [InlineData("114311", true, "31 December 2022")]
+ public async Task AboutSchoolPage_Displays_School_Closed_Info(string urn, bool isSchoolClosed, string? date)
{
// Act
- await Page.GotoAsync(_schoolUrnToUrlMap[urn]);
+ await Page.GotoAsync(PageUrl(urn));
// Assert
var schoolClosedCard = Page.GetByTestId("school-closed-custom-card");
@@ -89,7 +78,7 @@ public async Task AboutSchoolPage_Displays_School_Closed_Info(string urn, bool i
{
var value = await schoolClosedCard.Locator("p").TextContentAsync();
- var expectedText = hasSchoolClosedDate ? "This school closed on 23 March 2025" : "Closed";
+ var expectedText = date != null ? $"This school closed on {date}" : "Closed";
Assert.NotNull(value);
Assert.Equal(expectedText, value.Trim());
@@ -97,12 +86,12 @@ public async Task AboutSchoolPage_Displays_School_Closed_Info(string urn, bool i
}
[Theory]
- [InlineData("105574", false)]
- [InlineData("137552", true)]
- public async Task AboutSchoolPage_DisplaysSchoolDetails(string urn, bool trustNameIsExpected)
+ [InlineData("105574", null)]
+ [InlineData("137552", "THE PASSMORES CO-OPERATIVE LEARNING COMMUNITY")]
+ public async Task AboutSchoolPage_DisplaysTrustNameRow_WhenTrustNameNotNull(string urn, string? trustName)
{
// Act
- await Page.GotoAsync(_schoolUrnToUrlMap[urn]);
+ await Page.GotoAsync(PageUrl(urn));
// Assert
var detailsSummary = Page.Locator("#school-details-summary");
@@ -112,11 +101,11 @@ public async Task AboutSchoolPage_DisplaysSchoolDetails(string urn, bool trustNa
.Locator(".govuk-summary-list__row")
.Filter(new() { Has = Page.Locator(".govuk-summary-list__key", new() { HasText = " Academy Trust " }) });
- if (trustNameIsExpected)
+ if (trustName != null)
{
var value = await row.Locator(".govuk-summary-list__value").TextContentAsync();
Assert.NotNull(value);
- Assert.Equal("THE PASSMORES CO-OPERATIVE LEARNING COMMUNITY", value.Trim());
+ Assert.Equal(trustName, value.Trim());
}
else
{
@@ -128,7 +117,7 @@ public async Task AboutSchoolPage_DisplaysSchoolDetails(string urn, bool trustNa
public async Task AboutSchoolPage_DisplaysSchoolLocation()
{
// Arrange
- await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+ await Page.GotoAsync(PageUrl("105574"));
// Act
var isVisible = await Page.Locator("#school-location-summary").IsVisibleAsync();
@@ -141,7 +130,7 @@ public async Task AboutSchoolPage_DisplaysSchoolLocation()
public async Task AboutSchoolPage_DisplaysSpecialistUnit()
{
// Arrange
- await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+ await Page.GotoAsync(PageUrl("105574"));
// Act
var isVisible = await Page.Locator("#details-sen").IsVisibleAsync();
@@ -154,7 +143,7 @@ public async Task AboutSchoolPage_DisplaysSpecialistUnit()
public async Task AboutSchoolPage_DisplaysSchoolFeatures()
{
// Arrange
- await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
+ await Page.GotoAsync(PageUrl("105574"));
// Act
var isVisible = await Page.Locator("#school-features-summary").IsVisibleAsync();
@@ -162,47 +151,4 @@ public async Task AboutSchoolPage_DisplaysSchoolFeatures()
// Assert
Assert.True(isVisible);
}
-
- [Fact]
- public async Task AboutSchoolPage_DisplaysSchoolPolicies()
- {
- // Arrange
- await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
-
- // Act
- var schoolPoliciesSummaryCard = Page.Locator("#school-policies-summary");
- var contactSchoolInfo = schoolPoliciesSummaryCard.GetByTestId("contact-school-info");
-
- // Assert
- Assert.True(await schoolPoliciesSummaryCard.IsVisibleAsync());
- Assert.False(await contactSchoolInfo.IsVisibleAsync());
- }
-
- [Fact]
- public async Task AboutSchoolPage_DisplaysSchoolPolicies_ContactSchoolText()
- {
- // Arrange
- await Page.GotoAsync(_schoolUrnToUrlMap["100273"]);
-
- // Act
- var schoolPoliciesSummaryCard = Page.Locator("#school-policies-summary");
- var contactSchoolInfo = schoolPoliciesSummaryCard.GetByTestId("contact-school-info");
- var isVisible = await contactSchoolInfo.IsVisibleAsync();
-
- // Assert
- Assert.True(isVisible);
- }
-
- [Fact]
- public async Task AboutSchoolPage_DisplaysPagination()
- {
- // Arrange
- await Page.GotoAsync(_schoolUrnToUrlMap["105574"]);
-
- // Act
- var isVisible = await Page.Locator("#about-the-school-pagination").IsVisibleAsync();
-
- // Assert
- Assert.True(isVisible);
- }
}
\ No newline at end of file
diff --git a/Tests/SAPPub.Integration.Tests/SecondarySchoolTests/AttainmentPageTests.cs b/Tests/SAPPub.Integration.Tests/SecondarySchoolTests/AttainmentPageTests.cs
new file mode 100644
index 00000000..6d946a5d
--- /dev/null
+++ b/Tests/SAPPub.Integration.Tests/SecondarySchoolTests/AttainmentPageTests.cs
@@ -0,0 +1,78 @@
+using Microsoft.Playwright;
+using SAPPub.Integration.Tests;
+using System.Text.RegularExpressions;
+
+namespace SAPPub.IntegrationTests.SecondarySchoolTests;
+
+[Collection("Integration Tests")]
+public class AttainmentPageTests(WebApplicationSetupFixture fixture) : BasePageTest(fixture)
+{
+ private string PageUrl(string urn) => $"/school/{urn}";
+
+ [Theory]
+ [InlineData("136745", 39.8, 44.1, 46)]
+ [InlineData("137638", 44.2, 44.1, 46)]
+ [InlineData("142894", 39.2, 44.1, 46)]
+ [InlineData("144496", 49.6, 44.1, 46)]
+ [InlineData("144991", 45.6, 44.1, 46)]
+ [InlineData("145564", 50.4, 44.1, 46)]
+ [InlineData("147531", 49.1, 44.1, 46)]
+ [InlineData("147894", 57.5, 44.1, 46)]
+ [InlineData("147711", 45.8, 44.1, 46)]
+ [InlineData("136971", 46.4, 44.1, 46)]
+ [InlineData("137903", 46.9, 44.1, 46)]
+ [InlineData("148109", 44.7, 44.1, 46)]
+ [InlineData("145253", 41.5, 44.1, 46)]
+ [InlineData("148706", 39.4, 44.1, 46)]
+ [InlineData("138717", 48.2, 44.1, 46)]
+ [InlineData("149962", 35.7, 44.1, 46)]
+ [InlineData("136770", 43.9, 44.1, 46)]
+ [InlineData("114308", 53.4, 44.1, 46)]
+ [InlineData("137696", 45.2, 44.1, 46)]
+ [InlineData("149251", 37.4, 44.1, 46)]
+ [InlineData("114312", 55.2, 44.1, 46)]
+ [InlineData("147122", 41.4, 44.1, 46)]
+ [InlineData("136451", 52.2, 44.1, 46)]
+ [InlineData("149739", 47.8, 44.1, 46)]
+ [InlineData("147670", 51.5, 44.1, 46)]
+ [InlineData("138075", 51.8, 44.1, 46)]
+ [InlineData("137702", 46.4, 44.1, 46)]
+ [InlineData("143583", 57.7, 44.1, 46)]
+ [InlineData("148304", 44.5, 44.1, 46)]
+ [InlineData("138172", 44.9, 44.1, 46)]
+ public async Task AboutSchoolPage_LoadsSuccessfully(string urn, double expectedAttainmentSchool, double expectedAttainmentLA, double expectedAttainmentEngland)
+ {
+ // Arrange && Act
+ var _ = await Page.GotoAsync(PageUrl(urn));
+ var response = await ClickAcademicPerformanceLinkAsync();
+
+ // Assert
+ var schoolAttainment8 = await GetScoreAsync("attainment8-establishment-card", "The attainment 8 score for this school is");
+ Assert.NotNull(schoolAttainment8);
+ Assert.Equal(expectedAttainmentSchool.ToString("F1"), schoolAttainment8);
+ }
+
+ private Task ClickAcademicPerformanceLinkAsync()
+ {
+ var response = Page.RunAndWaitForResponseAsync(
+ async () =>
+ {
+ await Page.GetByRole(AriaRole.Link, new() { Name = "Academic performance" }).ClickAsync();
+ },
+ response => response.Url.Contains("/academic-performance-attainment-and-progress") && response.Status == 200
+ );
+ return response;
+ }
+
+ public async Task GetScoreAsync(string dataTestid, string textString)
+ {
+ var card = Page.Locator($"[data-testid='{dataTestid}']");
+ var p = card.Locator("p.govuk-body", new() { HasTextString = textString });
+ var input = await p.InnerTextAsync();
+ var match = Regex.Matches(input, @"\d+(\.\d+)?")
+ .Cast()
+ .Last();
+
+ return match.Value;
+ }
+}