diff --git a/tests/e2e/ui_tests/windows_os/Tests/ZeroPercentUpdateTest.cs b/tests/e2e/ui_tests/windows_os/Tests/ZeroPercentUpdateTest.cs new file mode 100644 index 00000000..cb4a4e25 --- /dev/null +++ b/tests/e2e/ui_tests/windows_os/Tests/ZeroPercentUpdateTest.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using ProtonMailBridge.UI.Tests.Results; +using ProtonMailBridge.UI.Tests.TestsHelper; +using ProtonMailBridge.UI.Tests.Windows; + +namespace ProtonMailBridge.UI.Tests.Tests +{ + public class ZeroPercentUpdateTest : TestSession + { + private readonly LoginWindow _loginWindow = new(); + private readonly SettingsMenuWindow _settingsMenuWindow = new(); + private readonly HelpMenuWindow _helpMenuWindow = new(); + private readonly ZeroPercentUpdateWindow _zeroPercentWindow = new(); + + [SetUp] + public void TestInitialize() + { + LaunchApp(); + } + + [Test] + [Category("ZeroPercentUpdateRollout")] + public void EnableBetaAccessVerifyBetaIsEnabledVerifyNotificationAndRestartBridge() + { + _zeroPercentWindow.ClickStartSetupButton(); + _zeroPercentWindow.CLickCancelButton(); + _helpMenuWindow.ClickHelpButton(); + _zeroPercentWindow.SaveCurrentVersionAndTagNumber(); + Thread.Sleep(2000); + ClientCleanup(); + _zeroPercentWindow.editTheVault(); + LaunchApp(); + _loginWindow.SignIn(TestUserData.GetPaidUser()); + _settingsMenuWindow.ClickSettingsButton(); + _zeroPercentWindow.VerifyBetaAccessIsEnabled(); + _zeroPercentWindow.RestartBridgeNotification(); + _helpMenuWindow.ClickHelpButton(); + Thread.Sleep(2000); + _zeroPercentWindow.VerifyVersionAndTagNumberOnRelaunch(); + } + + + [TearDown] + public void TestCleanup() + { + ClientCleanup(); + } + } +} diff --git a/tests/e2e/ui_tests/windows_os/Windows/ZeroPercentUpdateWindow.cs b/tests/e2e/ui_tests/windows_os/Windows/ZeroPercentUpdateWindow.cs new file mode 100644 index 00000000..f2d251ce --- /dev/null +++ b/tests/e2e/ui_tests/windows_os/Windows/ZeroPercentUpdateWindow.cs @@ -0,0 +1,261 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json.Nodes; +using System.Text.Json; +using System.Threading.Tasks; +using ProtonMailBridge.UI.Tests.TestsHelper; +using FlaUI.Core.Definitions; +using FlaUI.Core.AutomationElements; +using FlaUI.Core.Input; +// using System.Windows.Forms; + +namespace ProtonMailBridge.UI.Tests.Windows +{ + public class ZeroPercentUpdateWindow : UIActions + { + + private TextBox BridgeUpdateIsReady => Window.FindFirstDescendant(cf => cf.ByControlType(ControlType.Text).And(cf.ByName("Bridge update is ready"))).AsTextBox(); + private Button StartSetupButton => Window.FindFirstDescendant(cf => cf.ByControlType(ControlType.Button).And(cf.ByName("Start setup"))).AsButton(); + private Button CancelButton => Window.FindFirstDescendant(cf => cf.ByControlType(ControlType.Button).And(cf.ByName("Cancel"))).AsButton(); + + private TextBox VersionBuildNumberTextBox => Window.FindFirstDescendant(cf => cf.ByControlType(ControlType.Pane)).FindAllDescendants(cf => cf.ByControlType(ControlType.Text))[13].AsTextBox(); + + public void editTheVault() + { + try + { + string executablePath = Environment.GetEnvironmentVariable("UI_TEST_VAULT_EDITOR_EXE_PATH"); + + if (string.IsNullOrEmpty(executablePath)) + { + Console.WriteLine("Executable path is not set in the environment variables."); + return; + } + + if (!System.IO.File.Exists(executablePath)) + { + Console.WriteLine($"Executable not found at path: {executablePath}"); + return; + } + string readArgument = "read"; + string writeArgument = "write"; + string vaultJson; + + // start the read process + ProcessStartInfo readProcessInfo = new ProcessStartInfo + { + FileName = executablePath, + Arguments = readArgument, + RedirectStandardOutput = true, // Capture the output + UseShellExecute = false, // Required to redirect output + CreateNoWindow = true // Optional: Run without showing a console window + }; + + //read the output + using (Process readProcess = Process.Start(readProcessInfo)) + { + if (readProcess == null) + { + Assert.Fail("Failed to start the read process."); + return; + } + + string output = readProcess.StandardOutput.ReadToEnd(); + readProcess.WaitForExit(); + + // Write the output to a file + string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); + vaultJson = Path.Combine(desktopPath, "vault.json"); + File.WriteAllText(vaultJson, output); + + Console.WriteLine($"Vault data has been written to: {vaultJson}"); + } + + //update the json file, UpdateChannel to early and UpdateRollout to 0 + string jsonContent = File.ReadAllText(vaultJson); + JsonNode json = JsonNode.Parse(jsonContent); + + if (json == null) + { + Assert.Fail("Can not parse the vault.json file"); + return; + } + else + { + var settingsNode = json["Settings"]; + + settingsNode["UpdateChannel"] = "early"; + settingsNode["UpdateRollout"] = 0; + // must use UnsafeRelaxedJsonEscaping for the serialization of the + character in the timestamp + File.WriteAllText(vaultJson, json.ToJsonString(new JsonSerializerOptions { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping })); + } + + // read the new content from the json file + jsonContent = File.ReadAllText(vaultJson); + // start the write process + Process writeProcess = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = executablePath, + Arguments = writeArgument, + RedirectStandardInput = true, // Pass input via standard input + RedirectStandardOutput = true, // Capture standard output + RedirectStandardError = true, // Capture error output + UseShellExecute = false, // Required for redirection + CreateNoWindow = true // Run without showing a window + } + }; + + writeProcess.Start(); + + // pass the json file content to the process + using (StreamWriter writer = writeProcess.StandardInput) + { + if (writer.BaseStream.CanWrite) + { + writer.Write(jsonContent); + } + } + + // read the output and errors + string writeOutput = writeProcess.StandardOutput.ReadToEnd(); + string error = writeProcess.StandardError.ReadToEnd(); + + writeProcess.WaitForExit(); + + if (!string.IsNullOrWhiteSpace(writeOutput)) + { + Console.WriteLine("Output from write process:"); + Console.WriteLine(writeOutput); + } + + if (!string.IsNullOrWhiteSpace(error)) + { + Console.WriteLine("Error from write process:"); + Console.WriteLine(error); + } + + // Check exit code for success/failure + if (writeProcess.ExitCode != 0) + { + Assert.Fail("Write process exited with error code: " + writeProcess.ExitCode); + } + } + catch (Exception ex) + { + Assert.Fail(ex.Message); + } + + } + + public ZeroPercentUpdateWindow VerifyBetaAccessIsEnabled() + { + CheckBox BetaAccess = Window.FindFirstDescendant(cf => cf.ByControlType(ControlType.CheckBox).And(cf.ByName("Beta access toggle"))).AsCheckBox(); + Assert.That(BetaAccess.IsToggled, Is.True); + return this; + } + + public ZeroPercentUpdateWindow RestartBridgeNotification() + { + WaitUntilElementIsVisible(() => BridgeUpdateIsReady, 60); + Assert.That(BridgeUpdateIsReady.IsAvailable, Is.True); + Button RestartBridge = Window.FindFirstDescendant(cf => cf.ByControlType(ControlType.Button).And(cf.ByName("Restart Bridge"))).AsButton(); + RestartBridge.Click(); + Thread.Sleep(5000); + LaunchApp(); + Window.Focus(); + Thread.Sleep(5000); + return this; + } + + private static int[] PreviousVersionNumbers = null; + private static string PreviousTagNumber = null; + + private int[] GetVersionNumbers() + { + string text = VersionBuildNumberTextBox.Name; + + string versionString = text.Substring(text.IndexOf("v") + 1).Split(' ')[0]; + return Array.ConvertAll(versionString.Split('.'), int.Parse); + } + private string GetTagNumber() + { + string text = VersionBuildNumberTextBox.Name; + return text.Substring(text.IndexOf("(br-") + 4).Split(')')[0]; + } + public void SaveCurrentVersionAndTagNumber() + { + PreviousVersionNumbers = GetVersionNumbers(); + PreviousTagNumber = GetTagNumber(); + } + public void VerifyVersionAndTagNumberOnRelaunch() + { + int[] newVersionNumbers = GetVersionNumbers(); + string newTagNumber = GetTagNumber(); + + if (PreviousVersionNumbers == null || PreviousTagNumber == null) + { + throw new Exception("Previous version or tag number is not set."); + } + + bool isVersionGreater = false; + for (int i = 0; i < PreviousVersionNumbers.Length; i++) + { + if (newVersionNumbers[i] > PreviousVersionNumbers[i]) + { + isVersionGreater = true; + break; + } + else if (newVersionNumbers[i] < PreviousVersionNumbers[i]) + { + throw new Exception($"Current version {string.Join('.', newVersionNumbers)} is not greater than previous version {string.Join('.', PreviousVersionNumbers)}."); + } + } + + if (!isVersionGreater) + { + throw new Exception($"Current version {string.Join('.', newVersionNumbers)} is not greater than previous version {string.Join('.', PreviousVersionNumbers)}."); + } + + if (newTagNumber == PreviousTagNumber) + { + throw new Exception($"Current tag number (br-{newTagNumber}) is the same as the previous tag number (br-{PreviousTagNumber})."); + } + } + + public ZeroPercentUpdateWindow ClickStartSetupButton() + { + StartSetupButton.Click(); + return this; + } + public ZeroPercentUpdateWindow CLickCancelButton() + { + CancelButton.Click(); + return this; + } + private void WaitUntilElementIsVisible(Func findElementFunc, int numOfSeconds) + { + TimeSpan timeout = TimeSpan.FromSeconds(numOfSeconds); + Stopwatch stopwatch = Stopwatch.StartNew(); + + + while (stopwatch.Elapsed < timeout) + { + //if element is visible the processing is completed + var element = findElementFunc(); + if (element != null) + { + return; + } + + Wait.UntilInputIsProcessed(); + Thread.Sleep(500); + } + } + } +}