Improve website tests (#842)

* Improve website tests

* Use DefaultLang instead of hard coding English
This commit is contained in:
Josh 2023-08-03 20:09:32 -05:00 committed by GitHub
parent 17022c36b8
commit 65f317d9bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 65 additions and 27 deletions

View file

@ -11,8 +11,6 @@ public static class LocalizationManager
public static string GetLocalizedString(TranslationAreas translationArea, string language, string key) public static string GetLocalizedString(TranslationAreas translationArea, string language, string key)
{ {
// return $"{translationArea.ToString()}.{language}.{key}";
// ASP.NET requires specific names for certain languages (like ja for japanese as opposed to the standard ja-JP) // ASP.NET requires specific names for certain languages (like ja for japanese as opposed to the standard ja-JP)
// We map that value back here. // We map that value back here.
language = mapLanguageBack(language); language = mapLanguageBack(language);

View file

@ -1,6 +1,6 @@
@model (string Title, string Message) @model (string Title, string Message)
<div class="ui negative message"> <div class="ui negative message" id="error-message">
<div class="header"> <div class="header">
@Model.Title @Model.Title
</div> </div>

View file

@ -0,0 +1,13 @@
using System;
using OpenQA.Selenium;
namespace ProjectLighthouse.Tests.WebsiteTests.Extensions;
public static class WebDriverExtensions
{
private static Uri GetUri(this IWebDriver driver) => new(driver.Url);
public static string GetPath(this IWebDriver driver) => driver.GetUri().AbsolutePath;
public static string GetErrorMessage(this IWebDriver driver) => driver.FindElement(By.CssSelector("#error-message > p")).Text;
}

View file

@ -14,14 +14,15 @@ namespace ProjectLighthouse.Tests.WebsiteTests.Integration;
[Trait("Category", "Integration")] [Trait("Category", "Integration")]
public class AdminTests : LighthouseWebTest public class AdminTests : LighthouseWebTest
{ {
private const string adminPanelButtonXPath = "/html/body/div/header/div/div/div/a[2]"; private const string adminPanelButtonXPath = "/html/body/div/header/div/div/div/a[2]/span";
[Fact] [Fact]
public async Task ShouldShowAdminPanelButtonWhenAdmin() public async Task ShouldShowAdminPanelButtonWhenAdmin()
{ {
await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase();
Random random = new(); this.Driver.Manage().Cookies.DeleteAllCookies();
UserEntity user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure"));
UserEntity user = await database.CreateUser($"unitTestUser{CryptoHelper.GenerateRandomInt32()}", CryptoHelper.BCryptHash("i'm an engineering failure"));
WebTokenEntity webToken = new() WebTokenEntity webToken = new()
{ {
@ -39,15 +40,16 @@ public class AdminTests : LighthouseWebTest
this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken));
this.Driver.Navigate().Refresh(); this.Driver.Navigate().Refresh();
Assert.Contains("Admin", this.Driver.FindElement(By.XPath(adminPanelButtonXPath)).Text); Assert.Equal("Admin", this.Driver.FindElement(By.XPath(adminPanelButtonXPath)).Text);
} }
[Fact] [Fact]
public async Task ShouldNotShowAdminPanelButtonWhenNotAdmin() public async Task ShouldNotShowAdminPanelButtonWhenNotAdmin()
{ {
await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase();
Random random = new(); this.Driver.Manage().Cookies.DeleteAllCookies();
UserEntity user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure"));
UserEntity user = await database.CreateUser($"unitTestUser{CryptoHelper.GenerateRandomInt32()}", CryptoHelper.BCryptHash("i'm an engineering failure"));
WebTokenEntity webToken = new() WebTokenEntity webToken = new()
{ {
@ -65,6 +67,6 @@ public class AdminTests : LighthouseWebTest
this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken));
this.Driver.Navigate().Refresh(); this.Driver.Navigate().Refresh();
Assert.DoesNotContain("Admin", this.Driver.FindElement(By.XPath(adminPanelButtonXPath)).Text); Assert.Empty(this.Driver.FindElements(By.XPath(adminPanelButtonXPath)));
} }
} }

View file

@ -3,11 +3,13 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Localization.StringLists;
using LBPUnion.ProjectLighthouse.Tests.Helpers; using LBPUnion.ProjectLighthouse.Tests.Helpers;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Entities.Token;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using OpenQA.Selenium; using OpenQA.Selenium;
using ProjectLighthouse.Tests.WebsiteTests.Extensions;
using Xunit; using Xunit;
namespace ProjectLighthouse.Tests.WebsiteTests.Integration; namespace ProjectLighthouse.Tests.WebsiteTests.Integration;
@ -19,10 +21,10 @@ public class AuthenticationTests : LighthouseWebTest
public async Task ShouldLoginWithPassword() public async Task ShouldLoginWithPassword()
{ {
await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase();
Random random = new(); this.Driver.Manage().Cookies.DeleteAllCookies();
string password = CryptoHelper.Sha256Hash(CryptoHelper.GenerateRandomBytes(64).ToArray()); string password = CryptoHelper.Sha256Hash(CryptoHelper.GenerateRandomBytes(64).ToArray());
UserEntity user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash(CryptoHelper.Sha256Hash(password))); UserEntity user = await database.CreateUser($"unitTestUser{CryptoHelper.GenerateRandomInt32()}", CryptoHelper.BCryptHash(CryptoHelper.Sha256Hash(password)));
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
@ -33,16 +35,15 @@ public class AuthenticationTests : LighthouseWebTest
WebTokenEntity? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId); WebTokenEntity? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId);
Assert.NotNull(webToken); Assert.NotNull(webToken);
await database.RemoveUser(user);
} }
[Fact] [Fact]
public async Task ShouldNotLoginWithNoPassword() public async Task ShouldNotLoginWithNoPassword()
{ {
await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase();
Random random = new(); this.Driver.Manage().Cookies.DeleteAllCookies();
UserEntity user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("just like the hindenberg,"));
UserEntity user = await database.CreateUser($"unitTestUser{CryptoHelper.GenerateRandomInt32()}", CryptoHelper.BCryptHash("just like the hindenberg,"));
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
@ -53,15 +54,17 @@ public class AuthenticationTests : LighthouseWebTest
WebTokenEntity? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId); WebTokenEntity? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId);
Assert.Null(webToken); Assert.Null(webToken);
await database.RemoveUser(user); Assert.Equal("/login", this.Driver.GetPath());
Assert.Equal("The username or password you entered is invalid.", this.Driver.GetErrorMessage());
} }
[Fact] [Fact]
public async Task ShouldNotLoginWithWrongPassword() public async Task ShouldNotLoginWithWrongPassword()
{ {
await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase();
Random random = new(); this.Driver.Manage().Cookies.DeleteAllCookies();
UserEntity user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure"));
UserEntity user = await database.CreateUser($"unitTestUser{CryptoHelper.GenerateRandomInt32()}", CryptoHelper.BCryptHash("i'm an engineering failure"));
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
@ -72,8 +75,6 @@ public class AuthenticationTests : LighthouseWebTest
WebTokenEntity? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId); WebTokenEntity? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId);
Assert.Null(webToken); Assert.Null(webToken);
await database.RemoveUser(user);
} }
[Fact] [Fact]
@ -82,8 +83,7 @@ public class AuthenticationTests : LighthouseWebTest
const string loggedInAsUsernameTextXPath = "/html/body/div/div/div/div/p[1]"; const string loggedInAsUsernameTextXPath = "/html/body/div/div/div/div/p[1]";
await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase();
Random random = new(); UserEntity user = await database.CreateUser($"unitTestUser{CryptoHelper.GenerateRandomInt32()}", CryptoHelper.BCryptHash("i'm an engineering failure"));
UserEntity user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure"));
WebTokenEntity webToken = new() WebTokenEntity webToken = new()
{ {
@ -102,8 +102,7 @@ public class AuthenticationTests : LighthouseWebTest
Assert.DoesNotContain(user.Username, this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)).Text); Assert.DoesNotContain(user.Username, this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)).Text);
this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken));
navigation.Refresh(); navigation.Refresh();
Assert.Contains(user.Username, this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)).Text);
await database.RemoveUser(user); Assert.Equal(Translate(LandingPageStrings.LoggedInAs, user.Username), this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)).Text);
} }
} }

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Localization;
using LBPUnion.ProjectLighthouse.Servers.Website.Startup; using LBPUnion.ProjectLighthouse.Servers.Website.Startup;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Hosting.Server.Features;
@ -21,6 +22,8 @@ public class LighthouseWebTest : IDisposable
protected LighthouseWebTest() protected LighthouseWebTest()
{ {
ServerConfiguration.Instance.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse_tests;database=lighthouse_tests"; ServerConfiguration.Instance.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse_tests;database=lighthouse_tests";
ServerConfiguration.Instance.TwoFactorConfiguration.TwoFactorEnabled = false;
this.webHost.Start(); this.webHost.Start();
IServerAddressesFeature? serverAddressesFeature = this.webHost.ServerFeatures.Get<IServerAddressesFeature>(); IServerAddressesFeature? serverAddressesFeature = this.webHost.ServerFeatures.Get<IServerAddressesFeature>();
@ -38,8 +41,14 @@ public class LighthouseWebTest : IDisposable
} }
this.Driver = new ChromeDriver(chromeOptions); this.Driver = new ChromeDriver(chromeOptions);
this.Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1);
} }
protected static string Translate(TranslatableString translatableString) => translatableString.Translate(LocalizationManager.DefaultLang);
protected static string Translate(TranslatableString translatableString, params object?[] objects) =>
translatableString.Translate(LocalizationManager.DefaultLang, objects);
public void Dispose() public void Dispose()
{ {
this.Driver.Close(); this.Driver.Close();

View file

@ -3,10 +3,12 @@ using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Localization.StringLists;
using LBPUnion.ProjectLighthouse.Tests.Helpers; using LBPUnion.ProjectLighthouse.Tests.Helpers;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using OpenQA.Selenium; using OpenQA.Selenium;
using ProjectLighthouse.Tests.WebsiteTests.Extensions;
using Xunit; using Xunit;
namespace ProjectLighthouse.Tests.WebsiteTests.Integration; namespace ProjectLighthouse.Tests.WebsiteTests.Integration;
@ -18,6 +20,7 @@ public class RegisterTests : LighthouseWebTest
public async Task ShouldRegister() public async Task ShouldRegister()
{ {
await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase();
this.Driver.Manage().Cookies.DeleteAllCookies();
ServerConfiguration.Instance.Authentication.RegistrationEnabled = true; ServerConfiguration.Instance.Authentication.RegistrationEnabled = true;
@ -38,7 +41,7 @@ public class RegisterTests : LighthouseWebTest
UserEntity? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); UserEntity? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username);
Assert.NotNull(user); Assert.NotNull(user);
await database.RemoveUser(user); Assert.Equal("/", this.Driver.GetPath());
ServerConfiguration.Instance.Authentication.RegistrationEnabled = false; ServerConfiguration.Instance.Authentication.RegistrationEnabled = false;
} }
@ -47,6 +50,7 @@ public class RegisterTests : LighthouseWebTest
public async Task ShouldNotRegisterWithMismatchingPasswords() public async Task ShouldNotRegisterWithMismatchingPasswords()
{ {
await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase();
this.Driver.Manage().Cookies.DeleteAllCookies();
ServerConfiguration.Instance.Authentication.RegistrationEnabled = true; ServerConfiguration.Instance.Authentication.RegistrationEnabled = true;
@ -67,6 +71,9 @@ public class RegisterTests : LighthouseWebTest
UserEntity? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); UserEntity? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username);
Assert.Null(user); Assert.Null(user);
Assert.Equal("/register", this.Driver.GetPath());
Assert.Equal(Translate(ErrorStrings.PasswordDoesntMatch), this.Driver.GetErrorMessage());
ServerConfiguration.Instance.Authentication.RegistrationEnabled = false; ServerConfiguration.Instance.Authentication.RegistrationEnabled = false;
} }
@ -74,6 +81,7 @@ public class RegisterTests : LighthouseWebTest
public async Task ShouldNotRegisterWithTakenUsername() public async Task ShouldNotRegisterWithTakenUsername()
{ {
await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase();
this.Driver.Manage().Cookies.DeleteAllCookies();
ServerConfiguration.Instance.Authentication.RegistrationEnabled = true; ServerConfiguration.Instance.Authentication.RegistrationEnabled = true;
@ -95,7 +103,9 @@ public class RegisterTests : LighthouseWebTest
this.Driver.FindElement(By.Id("submit")).Click(); this.Driver.FindElement(By.Id("submit")).Click();
Assert.Contains("The username you've chosen is already taken.", this.Driver.PageSource); Assert.Equal("/register", this.Driver.GetPath());
Assert.Equal(Translate(ErrorStrings.UsernameTaken), this.Driver.GetErrorMessage());
ServerConfiguration.Instance.Authentication.RegistrationEnabled = false; ServerConfiguration.Instance.Authentication.RegistrationEnabled = false;
} }

View file

@ -32,6 +32,7 @@
<ProjectReference Include="..\ProjectLighthouse.Servers.Website\ProjectLighthouse.Servers.Website.csproj" /> <ProjectReference Include="..\ProjectLighthouse.Servers.Website\ProjectLighthouse.Servers.Website.csproj" />
<ProjectReference Include="..\ProjectLighthouse.Tests\ProjectLighthouse.Tests.csproj" /> <ProjectReference Include="..\ProjectLighthouse.Tests\ProjectLighthouse.Tests.csproj" />
<ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj" /> <ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj" />
<ProjectReference Include="..\ProjectLighthouse.Localization\ProjectLighthouse.Localization.csproj"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -60,6 +60,12 @@ public static class CryptoHelper
/// <returns>The randomly generated integer</returns> /// <returns>The randomly generated integer</returns>
public static int GenerateRandomInt32(int fromInclusive, int toExclusive) => RandomNumberGenerator.GetInt32(fromInclusive, toExclusive); public static int GenerateRandomInt32(int fromInclusive, int toExclusive) => RandomNumberGenerator.GetInt32(fromInclusive, toExclusive);
/// <summary>
/// Generates a random 32 bit integer between 0 and 2^32 - 1
/// </summary>
/// <returns>The randomly generated integer</returns>
public static int GenerateRandomInt32() => RandomNumberGenerator.GetInt32(0, int.MaxValue);
public static string ToBase64(string str) public static string ToBase64(string str)
{ {
byte[] bytes = Encoding.UTF8.GetBytes(str); byte[] bytes = Encoding.UTF8.GetBytes(str);