diff --git a/ProjectLighthouse.Localization/LocalizationManager.cs b/ProjectLighthouse.Localization/LocalizationManager.cs index c3ef79c0..d27d7d2f 100644 --- a/ProjectLighthouse.Localization/LocalizationManager.cs +++ b/ProjectLighthouse.Localization/LocalizationManager.cs @@ -11,8 +11,6 @@ public static class LocalizationManager 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) // We map that value back here. language = mapLanguageBack(language); diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/ErrorModalPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/ErrorModalPartial.cshtml index 4ee54119..2c7ef9de 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/ErrorModalPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/ErrorModalPartial.cshtml @@ -1,6 +1,6 @@ @model (string Title, string Message) -
+
@Model.Title
diff --git a/ProjectLighthouse.Tests.WebsiteTests/Extensions/WebDriverExtensions.cs b/ProjectLighthouse.Tests.WebsiteTests/Extensions/WebDriverExtensions.cs new file mode 100644 index 00000000..0dcaf297 --- /dev/null +++ b/ProjectLighthouse.Tests.WebsiteTests/Extensions/WebDriverExtensions.cs @@ -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; +} \ No newline at end of file diff --git a/ProjectLighthouse.Tests.WebsiteTests/Integration/AdminTests.cs b/ProjectLighthouse.Tests.WebsiteTests/Integration/AdminTests.cs index 79d15561..7078c163 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/Integration/AdminTests.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/Integration/AdminTests.cs @@ -14,14 +14,15 @@ namespace ProjectLighthouse.Tests.WebsiteTests.Integration; [Trait("Category", "Integration")] 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] public async Task ShouldShowAdminPanelButtonWhenAdmin() { await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); - Random random = new(); - UserEntity user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure")); + this.Driver.Manage().Cookies.DeleteAllCookies(); + + UserEntity user = await database.CreateUser($"unitTestUser{CryptoHelper.GenerateRandomInt32()}", CryptoHelper.BCryptHash("i'm an engineering failure")); WebTokenEntity webToken = new() { @@ -39,15 +40,16 @@ public class AdminTests : LighthouseWebTest this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); 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] public async Task ShouldNotShowAdminPanelButtonWhenNotAdmin() { await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); - Random random = new(); - UserEntity user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure")); + this.Driver.Manage().Cookies.DeleteAllCookies(); + + UserEntity user = await database.CreateUser($"unitTestUser{CryptoHelper.GenerateRandomInt32()}", CryptoHelper.BCryptHash("i'm an engineering failure")); WebTokenEntity webToken = new() { @@ -65,6 +67,6 @@ public class AdminTests : LighthouseWebTest this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); this.Driver.Navigate().Refresh(); - Assert.DoesNotContain("Admin", this.Driver.FindElement(By.XPath(adminPanelButtonXPath)).Text); + Assert.Empty(this.Driver.FindElements(By.XPath(adminPanelButtonXPath))); } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests.WebsiteTests/Integration/AuthenticationTests.cs b/ProjectLighthouse.Tests.WebsiteTests/Integration/AuthenticationTests.cs index f1d1f107..4151d9bc 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/Integration/AuthenticationTests.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/Integration/AuthenticationTests.cs @@ -3,11 +3,13 @@ using System.Linq; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Localization.StringLists; using LBPUnion.ProjectLighthouse.Tests.Helpers; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using Microsoft.EntityFrameworkCore; using OpenQA.Selenium; +using ProjectLighthouse.Tests.WebsiteTests.Extensions; using Xunit; namespace ProjectLighthouse.Tests.WebsiteTests.Integration; @@ -19,10 +21,10 @@ public class AuthenticationTests : LighthouseWebTest public async Task ShouldLoginWithPassword() { await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); - Random random = new(); + this.Driver.Manage().Cookies.DeleteAllCookies(); 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"); @@ -33,16 +35,15 @@ public class AuthenticationTests : LighthouseWebTest WebTokenEntity? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId); Assert.NotNull(webToken); - - await database.RemoveUser(user); } [Fact] public async Task ShouldNotLoginWithNoPassword() { await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); - Random random = new(); - UserEntity user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("just like the hindenberg,")); + this.Driver.Manage().Cookies.DeleteAllCookies(); + + UserEntity user = await database.CreateUser($"unitTestUser{CryptoHelper.GenerateRandomInt32()}", CryptoHelper.BCryptHash("just like the hindenberg,")); 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); 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] public async Task ShouldNotLoginWithWrongPassword() { await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); - Random random = new(); - UserEntity user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure")); + this.Driver.Manage().Cookies.DeleteAllCookies(); + + UserEntity user = await database.CreateUser($"unitTestUser{CryptoHelper.GenerateRandomInt32()}", CryptoHelper.BCryptHash("i'm an engineering failure")); 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); Assert.Null(webToken); - - await database.RemoveUser(user); } [Fact] @@ -82,8 +83,7 @@ public class AuthenticationTests : LighthouseWebTest const string loggedInAsUsernameTextXPath = "/html/body/div/div/div/div/p[1]"; await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); - Random random = new(); - 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() { @@ -102,8 +102,7 @@ public class AuthenticationTests : LighthouseWebTest Assert.DoesNotContain(user.Username, this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)).Text); this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); 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); } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests.WebsiteTests/Integration/LighthouseWebTest.cs b/ProjectLighthouse.Tests.WebsiteTests/Integration/LighthouseWebTest.cs index 81698cf4..48b61077 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/Integration/LighthouseWebTest.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/Integration/LighthouseWebTest.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Localization; using LBPUnion.ProjectLighthouse.Servers.Website.Startup; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server.Features; @@ -21,6 +22,8 @@ public class LighthouseWebTest : IDisposable protected LighthouseWebTest() { ServerConfiguration.Instance.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse_tests;database=lighthouse_tests"; + ServerConfiguration.Instance.TwoFactorConfiguration.TwoFactorEnabled = false; + this.webHost.Start(); IServerAddressesFeature? serverAddressesFeature = this.webHost.ServerFeatures.Get(); @@ -38,8 +41,14 @@ public class LighthouseWebTest : IDisposable } 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() { this.Driver.Close(); diff --git a/ProjectLighthouse.Tests.WebsiteTests/Integration/RegisterTests.cs b/ProjectLighthouse.Tests.WebsiteTests/Integration/RegisterTests.cs index 5edc8e56..b1441bc2 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/Integration/RegisterTests.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/Integration/RegisterTests.cs @@ -3,10 +3,12 @@ using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Localization.StringLists; using LBPUnion.ProjectLighthouse.Tests.Helpers; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using Microsoft.EntityFrameworkCore; using OpenQA.Selenium; +using ProjectLighthouse.Tests.WebsiteTests.Extensions; using Xunit; namespace ProjectLighthouse.Tests.WebsiteTests.Integration; @@ -18,6 +20,7 @@ public class RegisterTests : LighthouseWebTest public async Task ShouldRegister() { await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); + this.Driver.Manage().Cookies.DeleteAllCookies(); ServerConfiguration.Instance.Authentication.RegistrationEnabled = true; @@ -38,7 +41,7 @@ public class RegisterTests : LighthouseWebTest UserEntity? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); Assert.NotNull(user); - await database.RemoveUser(user); + Assert.Equal("/", this.Driver.GetPath()); ServerConfiguration.Instance.Authentication.RegistrationEnabled = false; } @@ -47,6 +50,7 @@ public class RegisterTests : LighthouseWebTest public async Task ShouldNotRegisterWithMismatchingPasswords() { await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); + this.Driver.Manage().Cookies.DeleteAllCookies(); ServerConfiguration.Instance.Authentication.RegistrationEnabled = true; @@ -67,6 +71,9 @@ public class RegisterTests : LighthouseWebTest UserEntity? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); Assert.Null(user); + Assert.Equal("/register", this.Driver.GetPath()); + Assert.Equal(Translate(ErrorStrings.PasswordDoesntMatch), this.Driver.GetErrorMessage()); + ServerConfiguration.Instance.Authentication.RegistrationEnabled = false; } @@ -74,6 +81,7 @@ public class RegisterTests : LighthouseWebTest public async Task ShouldNotRegisterWithTakenUsername() { await using DatabaseContext database = await IntegrationHelper.GetIntegrationDatabase(); + this.Driver.Manage().Cookies.DeleteAllCookies(); ServerConfiguration.Instance.Authentication.RegistrationEnabled = true; @@ -95,7 +103,9 @@ public class RegisterTests : LighthouseWebTest 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; } diff --git a/ProjectLighthouse.Tests.WebsiteTests/ProjectLighthouse.Tests.WebsiteTests.csproj b/ProjectLighthouse.Tests.WebsiteTests/ProjectLighthouse.Tests.WebsiteTests.csproj index 3e1a3e0f..d38e93e8 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/ProjectLighthouse.Tests.WebsiteTests.csproj +++ b/ProjectLighthouse.Tests.WebsiteTests/ProjectLighthouse.Tests.WebsiteTests.csproj @@ -32,6 +32,7 @@ + diff --git a/ProjectLighthouse/Helpers/CryptoHelper.cs b/ProjectLighthouse/Helpers/CryptoHelper.cs index 068e9eb6..cfa896a2 100644 --- a/ProjectLighthouse/Helpers/CryptoHelper.cs +++ b/ProjectLighthouse/Helpers/CryptoHelper.cs @@ -60,6 +60,12 @@ public static class CryptoHelper /// The randomly generated integer public static int GenerateRandomInt32(int fromInclusive, int toExclusive) => RandomNumberGenerator.GetInt32(fromInclusive, toExclusive); + /// + /// Generates a random 32 bit integer between 0 and 2^32 - 1 + /// + /// The randomly generated integer + public static int GenerateRandomInt32() => RandomNumberGenerator.GetInt32(0, int.MaxValue); + public static string ToBase64(string str) { byte[] bytes = Encoding.UTF8.GetBytes(str);