Skip to content

Commit 6ee0001

Browse files
committed
Add WindowSwitcher class
1 parent b731bb4 commit 6ee0001

File tree

3 files changed

+168
-1
lines changed

3 files changed

+168
-1
lines changed

dotnet/src/support/Extensions/WebDriverExtensions.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
// under the License.
1818
// </copyright>
1919

20+
using OpenQA.Selenium.Support.UI;
2021
using System;
2122
using System.Reflection;
2223

@@ -117,7 +118,7 @@ public static void ExecuteJavaScript(this IWebDriver driver, string script, para
117118

118119
private static object? ExecuteJavaScriptInternal(IWebDriver driver, string script, object?[] args)
119120
{
120-
IJavaScriptExecutor? executor = GetDriverAs<IJavaScriptExecutor>(driver)
121+
IJavaScriptExecutor executor = GetDriverAs<IJavaScriptExecutor>(driver)
121122
?? throw new WebDriverException("Driver does not implement IJavaScriptExecutor");
122123

123124
return executor.ExecuteScript(script, args);
@@ -142,4 +143,18 @@ public static void ExecuteJavaScript(this IWebDriver driver, string script, para
142143

143144
return convertedDriver;
144145
}
146+
147+
/// <summary>
148+
/// Performs an action that opens a new browser window, and returns an object that allows to switch between
149+
/// the new and the old windows easily.
150+
/// </summary>
151+
/// <param name="driver">The driver instance to extend.</param>
152+
/// <param name="actionThatOpensNewWindow">The action delegate that should open the new browser window.</param>
153+
/// <returns>An instance of <see cref="WindowSwitcher"/> that allows to switch between the old and new windows.</returns>
154+
public static WindowSwitcher WithWindowOpenedBy(this IWebDriver driver, Action actionThatOpensNewWindow)
155+
{
156+
var finder = new PopupWindowFinder(driver);
157+
var newHandle = finder.Invoke(actionThatOpensNewWindow);
158+
return new WindowSwitcher(driver, newHandle);
159+
}
145160
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using System;
2+
using OpenQA.Selenium.Support.Extensions;
3+
4+
namespace OpenQA.Selenium.Support.UI;
5+
6+
/// <summary>
7+
/// Provides a mechanism to easily switch between two browser windows.
8+
/// </summary>
9+
public class WindowSwitcher
10+
{
11+
private readonly IWebDriver driver;
12+
private readonly string newWindowHandle;
13+
private readonly string originalHandle;
14+
15+
/// <summary>
16+
/// Initializes a new instance of the <see cref="WindowSwitcher"/> class.
17+
/// </summary>
18+
/// <param name="driver">The <see cref="IWebDriver"/> instance that controls the two windows.</param>
19+
/// <param name="newWindowHandle">The handle of the new window.</param>
20+
/// <remarks>
21+
/// <para>
22+
/// It is recommended to use the <see cref="WebDriverExtensions.WithWindowOpenedBy"/> to instantiate this
23+
/// class.
24+
/// </para>
25+
/// <para>
26+
/// The current driver window handle is used to identify the existing window, while
27+
/// <paramref name="newWindowHandle"/> should be the handle of another, newly opened, window.
28+
/// </para>
29+
/// </remarks>
30+
public WindowSwitcher(IWebDriver driver, string newWindowHandle)
31+
{
32+
this.driver = driver;
33+
this.newWindowHandle = newWindowHandle;
34+
originalHandle = this.driver.CurrentWindowHandle;
35+
}
36+
37+
/// <summary>
38+
/// Performs the provided action on the newly opened window, and returns to use the original window afterward.
39+
/// </summary>
40+
/// <param name="action">The action to perform on the newly opened window.</param>
41+
/// <example>
42+
/// <code>
43+
/// driver.WithWindowOpenedBy(() => driver.FindElement(By.Id("openWindowId")).Click())
44+
/// .Do(() => {
45+
/// // Perform whatever you want with the new window, for example:
46+
/// driver.FindElement(By.Id("anElementOnTheNewWindow").Click();
47+
/// Assert.That(driver.Title, Is.EqualTo("The new window!"));
48+
/// });
49+
/// // Then continue to do stuff on the original window:
50+
/// driver.FindElement(By.Id("anElementOnTheOriginalWindow").Click();
51+
/// </code>
52+
/// </example>
53+
public void Do(Action action)
54+
{
55+
SwitchToNewWindow();
56+
try
57+
{
58+
action();
59+
}
60+
finally
61+
{
62+
SwitchToOriginalWindow();
63+
}
64+
}
65+
66+
/// <summary>
67+
/// Switches to the new window.
68+
/// </summary>
69+
/// <remarks>
70+
/// The following example shows how to use <see cref="SwitchToNewWindow"/> and <see cref="SwitchToOriginalWindow"/>
71+
/// to switch back and forth between the two windows:
72+
/// <example>
73+
/// <code>
74+
/// var switcher = driver.WithWindowOpenedBy(() => driver.FindElement(By.Id("openWindowId")).Click());
75+
/// // Do some stuff on the original window:
76+
/// driver.FindElement(By.Id("aButtonOnTheOriginalWindow")).Click();
77+
///
78+
/// // Do some stuff on the new window:
79+
/// switcher.SwitchToNewWindow();
80+
/// driver.FindElement(By.Id("anInputOnTheNewWindow")).SendKeys("Hi");
81+
///
82+
/// // Do some more stuff on the original window:
83+
/// switcher.SwitchToOriginalWindow();
84+
/// driver.FindElement(By.Id("aButtonOnTheOriginalWindow")).Click();
85+
///
86+
/// // And you can continue switching between the windows as you need...
87+
/// </code>
88+
/// </example>
89+
/// </remarks>
90+
public void SwitchToNewWindow()
91+
{
92+
driver.SwitchTo().Window(newWindowHandle);
93+
}
94+
95+
/// <summary>
96+
/// Switches to the original window.
97+
/// </summary>
98+
/// <inheritdoc cref="SwitchToNewWindow" path="/remarks" />
99+
public void SwitchToOriginalWindow()
100+
{
101+
driver.SwitchTo().Window(originalHandle);
102+
}
103+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using NUnit.Framework;
2+
using OpenQA.Selenium.Environment;
3+
using System.Threading.Tasks;
4+
using OpenQA.Selenium;
5+
using OpenQA.Selenium.Support.Extensions;
6+
7+
namespace Selenium.WebDriver.Support.Tests.UI;
8+
9+
internal class WindowSwitcherTest : DriverTestFixture
10+
{
11+
[OneTimeSetUp]
12+
public async Task RunBeforeAnyTestAsync()
13+
{
14+
await EnvironmentManager.Instance.WebServer.StartAsync();
15+
}
16+
17+
[OneTimeTearDown]
18+
public async Task RunAfterAnyTestsAsync()
19+
{
20+
EnvironmentManager.Instance.CloseCurrentDriver();
21+
await EnvironmentManager.Instance.WebServer.StopAsync();
22+
}
23+
24+
[Test]
25+
public void SwitchesToNewWindowAndBack()
26+
{
27+
driver.Url = xhtmlTestPage;
28+
var originalWindowHandle = driver.CurrentWindowHandle;
29+
var delegateCalled = false;
30+
var openWindowLink = driver.FindElement(By.LinkText("Open new window"));
31+
driver.WithWindowOpenedBy(() => openWindowLink.Click())
32+
.Do(() =>
33+
{
34+
delegateCalled = true;
35+
Assert.Multiple(() =>
36+
{
37+
Assert.That(driver.CurrentWindowHandle, Is.Not.EqualTo(originalWindowHandle));
38+
Assert.That(driver.Title, Is.EqualTo("We Arrive Here"));
39+
});
40+
});
41+
42+
Assert.Multiple(() =>
43+
{
44+
Assert.That(delegateCalled);
45+
Assert.That(driver.CurrentWindowHandle, Is.EqualTo(originalWindowHandle));
46+
Assert.That(driver.Title, Is.EqualTo("XHTML Test Page"));
47+
});
48+
}
49+
}

0 commit comments

Comments
 (0)