/* * Copyright (c) 2026 Proton AG * * This file is part of ProtonVPN. * * ProtonVPN is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ProtonVPN is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ProtonVPN. If not, see . */ using System; using System.Collections.Generic; using System.Threading; using NUnit.Framework; using ProtonVPN.UI.Tests.Enums; using ProtonVPN.UI.Tests.TestsHelper; using ProtonVPN.UI.Tests.UiTools; namespace ProtonVPN.UI.Tests.Robots; public class FeaturesRobot { private const string NET_SHIELD_ENABLED_FLYOUT_TEXT_1 = "Ads blocked"; private const string NET_SHIELD_ENABLED_FLYOUT_TEXT_2 = "Trackers stopped"; private const string NET_SHIELD_ENABLED_FLYOUT_TEXT_3 = "Data saved"; private const string DISCONNECTED_KILL_SWITCH_ENABLED_FLYOUT_TEXT_1 = "Advanced kill switch disables your internet to protect your IP address while you're not connected to Proton VPN."; private const string DISCONNECTED_KILL_SWITCH_ENABLED_FLYOUT_TEXT_2 = "To get back online, connect to VPN or disable advanced kill switch"; private const string CONNECTED_KILL_SWITCH_ENABLED_FLYOUT_TEXT_1 = "Blocks all internet access when you're not connected to Proton VPN"; private const string CONNECTED_KILL_SWITCH_ENABLED_FLYOUT_TEXT_2 = "Advanced kill switch applies even if you intentionally disconnect from VPN"; private const string SPLIT_TUNNELING_NO_APP_SELECTED_FLYOUT_TEXT = "Select apps"; private const string PORT_UNAVAILABLE_FLYOUT_TEXT_1 = "Connect to a P2P server to improve torrenting speeds"; private const string PORT_UNAVAILABLE_FLYOUT_TEXT_2 = "Unavailable"; protected Element NetShieldWidgetButton = Element.ByAutomationId("NetShieldWidgetButton"); protected Element KillSwitchWidgetButton = Element.ByAutomationId("KillSwitchWidgetButton"); protected Element PortForwardingWidgetButton = Element.ByAutomationId("PortForwardingWidgetButton"); protected Element SplitTunnelingWidgetButton = Element.ByAutomationId("SplitTunnelingWidgetButton"); protected Element CopyPortNumberFromActivePortSection = Element.ByAutomationId("CopyPortNumberCondensedButton"); protected Element WidgetFlyout = Element.ByAutomationId("WidgetFlyout"); protected Element MenuFlyout = Element.ByClassName("MenuFlyout"); protected Element WidgetFlyoutToggle => WidgetFlyout.FindDescendant(Element.ByClassName("Button")); protected Element CopyPortNumberFromFlyoutMenu = Element.ByAutomationId("CopyPortNumberCompactButton"); public FeaturesRobot HoverOverNetShieldWidget() { NetShieldWidgetButton.Hover(); Thread.Sleep(TestConstants.AnimationDelay); return this; } public FeaturesRobot HoverOverKillSwitchWidget() { KillSwitchWidgetButton.Hover(); Thread.Sleep(TestConstants.AnimationDelay); return this; } public FeaturesRobot HoverOverPortForwardingWidget() { PortForwardingWidgetButton.Hover(); Thread.Sleep(TestConstants.AnimationDelay); return this; } public FeaturesRobot HoverOverSplitTunnelingWidget() { SplitTunnelingWidgetButton.Hover(); Thread.Sleep(TestConstants.AnimationDelay); return this; } public FeaturesRobot ClickNetShieldWidget() { NetShieldWidgetButton.Click(); return this; } public FeaturesRobot ClickKillSwitchWidget() { KillSwitchWidgetButton.Click(); return this; } public FeaturesRobot ClickPortForwardingWidget() { PortForwardingWidgetButton.Click(); return this; } public FeaturesRobot ClickSplitTunnelingWidget() { SplitTunnelingWidgetButton.Click(); return this; } public FeaturesRobot ClickCopyPortNumberFromActivePortSection() { CopyPortNumberFromActivePortSection.Click(); return this; } public FeaturesRobot ClickCopyPortNumberFromFlyoutMenu() { CopyPortNumberFromFlyoutMenu.Click(); return this; } public FeaturesRobot EnableNetShield(NetShieldMode netShieldMode) { string netshieldModeString; switch (netShieldMode) { case NetShieldMode.BlockMalwareOnly: netshieldModeString = "Block malware only"; break; case NetShieldMode.BlockAdsMalwareTrackers: netshieldModeString = "Block ads, trackers, and malware"; break; case NetShieldMode.BlockAdsMalwareTrackersAdultContent: netshieldModeString = "Block ads, trackers, malware, and adult content"; break; default: throw new ArgumentException($"Unknown mode: {netShieldMode}"); } ToggleFeature(netshieldModeString); return this; } public FeaturesRobot EnableKillSwitch(KillSwitchMode killSwitchMode) { ToggleFeature(killSwitchMode.ToString()); return this; } public FeaturesRobot EnableSplitTunneling(SplitTunnelingMode splitTunnelingMode) { ToggleFeature(splitTunnelingMode.ToString()); return this; } public FeaturesRobot EnableFeature() { ToggleFeature(SimpleToggle.On.ToString()); return this; } public FeaturesRobot DisableFeature() { ToggleFeature(SimpleToggle.Off.ToString()); return this; } public class Verifications : FeaturesRobot { public Verifications IsNetShieldTextInFlyoutMenu() { List allChildren = GetFlyoutChildren(); Assert.That(allChildren, Does.Contain(NET_SHIELD_ENABLED_FLYOUT_TEXT_1)); Assert.That(allChildren, Does.Contain(NET_SHIELD_ENABLED_FLYOUT_TEXT_2)); Assert.That(allChildren, Does.Contain(NET_SHIELD_ENABLED_FLYOUT_TEXT_3)); return this; } public Verifications IsAdvancedKillSwitchTextInFlyoutMenu(bool isConnected) { List allChildren = GetFlyoutChildren(); Assert.That(allChildren, Does.Contain(KillSwitchMode.Advanced.ToString())); Assert.That(allChildren, Does.Contain(isConnected ? CONNECTED_KILL_SWITCH_ENABLED_FLYOUT_TEXT_1 : DISCONNECTED_KILL_SWITCH_ENABLED_FLYOUT_TEXT_1)); Assert.That(allChildren, Does.Contain(isConnected ? CONNECTED_KILL_SWITCH_ENABLED_FLYOUT_TEXT_2 : DISCONNECTED_KILL_SWITCH_ENABLED_FLYOUT_TEXT_2)); return this; } public Verifications IsSplitTunnelingAppAvailableInFlyoutMenu(string splitTunnelingMode) { List allChildren = GetFlyoutChildren(); Assert.That(allChildren, Does.Contain(splitTunnelingMode)); Assert.That(allChildren, Does.Not.Contain(SPLIT_TUNNELING_NO_APP_SELECTED_FLYOUT_TEXT)); return this; } public Verifications IsSplitTunnelingAppUnavailableInFlyoutMenu(string splitTunnelingMode) { List allChildren = GetFlyoutChildren(); Assert.That(allChildren, Does.Contain(splitTunnelingMode.Replace("1", "0"))); Assert.That(allChildren, Does.Contain(SPLIT_TUNNELING_NO_APP_SELECTED_FLYOUT_TEXT)); return this; } public Verifications IsPortForwardingEnabled() { CopyPortNumberFromActivePortSection.WaitUntilDisplayed(); return this; } public Verifications IsLastChangedTimerDisplayed() { List allChildren = GetFlyoutChildren(); Assert.That(allChildren, Has.Some.Match("Last changed: \\d+ second[s]? ago")); return this; } public Verifications IsPortUnavailable() { List allChildren = GetFlyoutChildren(); Assert.That(allChildren, Does.Contain(PORT_UNAVAILABLE_FLYOUT_TEXT_1)); Assert.That(allChildren, Does.Contain(PORT_UNAVAILABLE_FLYOUT_TEXT_2)); return this; } } private void ToggleFeature(string optionToSelect) { List allChildren = GetFlyoutChildren(); if (!allChildren.Contains(optionToSelect)) { WidgetFlyoutToggle.Click(); Thread.Sleep(TestConstants.UserInputSimulationDelay); MenuFlyout.FindDescendant(Element.ByName(optionToSelect)).DoubleClick(); Thread.Sleep(TestConstants.UserInputSimulationDelay); } } private List GetFlyoutChildren() { WidgetFlyout.WaitUntilDisplayed(); return WidgetFlyout.GetAllChildrenNames(); } public Verifications Verify => new(); }