diff --git a/src/Tests/AliasVault.E2ETests/Common/PlaywrightInputHelper.cs b/src/Tests/AliasVault.E2ETests/Common/PlaywrightInputHelper.cs
new file mode 100644
index 000000000..b9decb2b8
--- /dev/null
+++ b/src/Tests/AliasVault.E2ETests/Common/PlaywrightInputHelper.cs
@@ -0,0 +1,113 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) lanedirt. All rights reserved.
+// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
+//
+//-----------------------------------------------------------------------
+
+namespace AliasVault.E2ETests.Common;
+
+///
+/// Playwright input helper class.
+///
+/// The IPage instance for the current test.
+public class PlaywrightInputHelper(IPage page)
+{
+ private static readonly Random Random = new();
+
+ ///
+ /// Generate a random string of specified length.
+ ///
+ /// Length of the string.
+ /// The random generated string.
+ public static string GenerateRandomString(int length = 10)
+ {
+ const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ return new string(Enumerable.Repeat(chars, length)
+ .Select(s => s[Random.Next(s.Length)]).ToArray());
+ }
+
+ ///
+ /// Generate a random email address.
+ ///
+ /// The random email address.
+ public static string GenerateRandomEmail()
+ {
+ return $"{GenerateRandomString(5)}@example.com";
+ }
+
+ ///
+ /// Generate a random number.
+ ///
+ /// The random number.
+ public static string GenerateRandomNumber()
+ {
+ return Random.Next(0, 10000).ToString();
+ }
+
+ ///
+ /// Generate a random password of specified length.
+ ///
+ /// The length of the password.
+ /// The random generated password.
+ public static string GenerateRandomPassword(int length = 12)
+ {
+ const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()";
+ return new string(Enumerable.Repeat(chars, length)
+ .Select(s => s[Random.Next(s.Length)]).ToArray());
+ }
+
+ ///
+ /// Helper method to fill specified input fields on a page with given values.
+ ///
+ /// Dictionary with html element ids and values to input as field value.
+ /// Async task.
+ public async Task FillInputFields(Dictionary? fieldValues = null)
+ {
+ var inputFields = page.Locator("input");
+ var count = await inputFields.CountAsync();
+ for (int i = 0; i < count; i++)
+ {
+ var input = inputFields.Nth(i);
+ var inputId = await input.GetAttributeAsync("id");
+
+ // If fieldValues dictionary is provided and the inputId is found in it, fill the input with the value.
+ if (inputId is not null && fieldValues is not null && fieldValues.TryGetValue(inputId, out var fieldValue))
+ {
+ await input.FillAsync(fieldValue);
+ }
+ }
+ }
+
+ ///
+ /// Helper method to fill all empty input fields on a page with random data if not provided.
+ ///
+ /// Async task.
+ public async Task FillEmptyInputFieldsWithRandom()
+ {
+ var inputFields = page.Locator("input");
+ var count = await inputFields.CountAsync();
+ for (int i = 0; i < count; i++)
+ {
+ var input = inputFields.Nth(i);
+ var inputType = await input.GetAttributeAsync("type");
+
+ // If is not empty, skip.
+ if (!string.IsNullOrEmpty(await input.InputValueAsync()))
+ {
+ continue;
+ }
+
+ // Generate appropriate random data based on input type.
+ string randomData = inputType switch
+ {
+ "email" => GenerateRandomEmail(),
+ "number" => GenerateRandomNumber(),
+ "password" => GenerateRandomPassword(),
+ _ => GenerateRandomString(), // Default for all other types.
+ };
+
+ await input.FillAsync(randomData);
+ }
+ }
+}
diff --git a/src/Tests/AliasVault.E2ETests/Common/PlaywrightTest.cs b/src/Tests/AliasVault.E2ETests/Common/PlaywrightTest.cs
index 2cb49fc6c..b79162188 100644
--- a/src/Tests/AliasVault.E2ETests/Common/PlaywrightTest.cs
+++ b/src/Tests/AliasVault.E2ETests/Common/PlaywrightTest.cs
@@ -54,6 +54,11 @@ public class PlaywrightTest
///
protected IPage Page { get; private set; }
+ ///
+ /// Gets the input helper for Playwright tests.
+ ///
+ protected PlaywrightInputHelper InputHelper { get; private set; } = null!;
+
///
/// One time setup for the Playwright test which runs before all tests in the class.
///
@@ -97,6 +102,7 @@ public class PlaywrightTest
});
Page = await Context.NewPageAsync();
+ InputHelper = new(Page);
// Register a new account via the UI
await Register();
diff --git a/src/Tests/AliasVault.E2ETests/AliasTests.cs b/src/Tests/AliasVault.E2ETests/Tests/AliasTests.cs
similarity index 57%
rename from src/Tests/AliasVault.E2ETests/AliasTests.cs
rename to src/Tests/AliasVault.E2ETests/Tests/AliasTests.cs
index 900c77211..024dc6f75 100644
--- a/src/Tests/AliasVault.E2ETests/AliasTests.cs
+++ b/src/Tests/AliasVault.E2ETests/Tests/AliasTests.cs
@@ -5,7 +5,7 @@
//
//-----------------------------------------------------------------------
-namespace AliasVault.E2ETests;
+namespace AliasVault.E2ETests.Tests;
///
/// End-to-end tests for the alias management.
@@ -75,8 +75,7 @@ public class AliasTests : PlaywrightTest
// Replace the service name with "Alias service after".
var serviceNameAfter = "Alias service after";
- await FillInputFields(
- page: Page,
+ await InputHelper.FillInputFields(
fieldValues: new Dictionary
{
{ "service-name", serviceNameAfter },
@@ -91,86 +90,6 @@ public class AliasTests : PlaywrightTest
Assert.That(pageContent, Does.Contain(serviceNameAfter), "Alias not updated correctly.");
}
- ///
- /// Helper method to fill specified input fields on a page with given values.
- ///
- /// IPage instance where to fill the input fields for.
- /// Dictionary with html element ids and values to input as field value.
- /// Async task.
- private static async Task FillInputFields(IPage page, Dictionary? fieldValues = null)
- {
- var inputFields = page.Locator("input");
- var count = await inputFields.CountAsync();
- for (int i = 0; i < count; i++)
- {
- var input = inputFields.Nth(i);
- var inputId = await input.GetAttributeAsync("id");
-
- // If fieldValues dictionary is provided and the inputId is found in it, fill the input with the value.
- if (inputId is not null && fieldValues is not null && fieldValues.TryGetValue(inputId, out var fieldValue))
- {
- await input.FillAsync(fieldValue);
- }
- }
- }
-
- ///
- /// Helper method to fill all empty input fields on a page with random data if not provided.
- ///
- /// IPage instance where to fill the input fields for.
- /// Async task.
- private static async Task FillEmptyInputFieldsWithRandom(IPage page)
- {
- var inputFields = page.Locator("input");
- var count = await inputFields.CountAsync();
- for (int i = 0; i < count; i++)
- {
- var input = inputFields.Nth(i);
- var inputType = await input.GetAttributeAsync("type");
-
- // If is not empty, skip.
- if (!string.IsNullOrEmpty(await input.InputValueAsync()))
- {
- continue;
- }
-
- // Generate appropriate random data based on input type.
- string randomData = inputType switch
- {
- "email" => GenerateRandomEmail(),
- "number" => GenerateRandomNumber(),
- "password" => GenerateRandomPassword(),
- _ => GenerateRandomString(), // Default for all other types.
- };
-
- await input.FillAsync(randomData);
- }
- }
-
- private static string GenerateRandomString(int length = 10)
- {
- const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
- return new string(Enumerable.Repeat(chars, length)
- .Select(s => s[Random.Next(s.Length)]).ToArray());
- }
-
- private static string GenerateRandomEmail()
- {
- return $"{GenerateRandomString(5)}@example.com";
- }
-
- private static string GenerateRandomNumber()
- {
- return Random.Next(0, 10000).ToString();
- }
-
- private static string GenerateRandomPassword(int length = 12)
- {
- const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()";
- return new string(Enumerable.Repeat(chars, length)
- .Select(s => s[Random.Next(s.Length)]).ToArray());
- }
-
///
/// Create new alias.
///
@@ -186,8 +105,8 @@ public class AliasTests : PlaywrightTest
Assert.That(generateButton, Is.Not.Null, "Generate button not found.");
// Fill all input fields with specified values and remaining empty fields with random data.
- await FillInputFields(Page, formValues);
- await FillEmptyInputFieldsWithRandom(Page);
+ await InputHelper.FillInputFields(formValues);
+ await InputHelper.FillEmptyInputFieldsWithRandom();
var submitButton = Page.Locator("text=Save Alias").First;
await submitButton.ClickAsync();
diff --git a/src/Tests/AliasVault.E2ETests/AuthTests.cs b/src/Tests/AliasVault.E2ETests/Tests/AuthTests.cs
similarity index 98%
rename from src/Tests/AliasVault.E2ETests/AuthTests.cs
rename to src/Tests/AliasVault.E2ETests/Tests/AuthTests.cs
index aa5ccb1a5..9d721c4da 100644
--- a/src/Tests/AliasVault.E2ETests/AuthTests.cs
+++ b/src/Tests/AliasVault.E2ETests/Tests/AuthTests.cs
@@ -5,7 +5,7 @@
//
//-----------------------------------------------------------------------
-namespace AliasVault.E2ETests;
+namespace AliasVault.E2ETests.Tests;
///
/// End-to-end tests for authentication.
diff --git a/src/Tests/AliasVault.E2ETests/Tests/UnlockTests.cs b/src/Tests/AliasVault.E2ETests/Tests/UnlockTests.cs
new file mode 100644
index 000000000..1b145ed82
--- /dev/null
+++ b/src/Tests/AliasVault.E2ETests/Tests/UnlockTests.cs
@@ -0,0 +1,49 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) lanedirt. All rights reserved.
+// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
+//
+//-----------------------------------------------------------------------
+
+namespace AliasVault.E2ETests.Tests;
+
+///
+/// End-to-end tests for the database unlock functionality.
+///
+[Parallelizable(ParallelScope.Self)]
+[TestFixture]
+public class UnlockTests : PlaywrightTest
+{
+ private static readonly Random Random = new();
+
+ ///
+ /// Test that the unlock page is displayed after hard refresh which should
+ /// clear the encryption key from memory.
+ ///
+ /// Async task.
+ [Test]
+ public async Task UnlockPageTest()
+ {
+ // Soft navigate to "aliases" page to test the unlock redirect.
+ var startUrl = "aliases";
+ await NavigateUsingBlazorRouter(startUrl);
+
+ // Hard refresh the page.
+ await Page.ReloadAsync();
+
+ // Check if the unlock page is displayed.
+ await WaitForURLAsync("**/unlock", "unlock");
+
+ // Check if by entering password the unlock page is replaced by the alias listing page.
+ await InputHelper.FillInputFields(new Dictionary
+ {
+ { "password", TestUserPassword },
+ });
+
+ var submitButton = Page.GetByRole(AriaRole.Button, new() { Name = "Unlock" });
+ await submitButton.ClickAsync();
+
+ // Check if we get redirected back to the page we were trying to access.
+ await WaitForURLAsync("**/" + startUrl, "AliasVault");
+ }
+}