From fbc596e29bab5d40699940356bffe45a6eec2f5e Mon Sep 17 00:00:00 2001 From: zc2 Date: Fri, 28 Jan 2022 16:10:37 -0500 Subject: [PATCH 01/16] Added a missed property to get the Window's handle. --- Selenium/ComInterfaces/_Window.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Selenium/ComInterfaces/_Window.cs b/Selenium/ComInterfaces/_Window.cs index 211c0d1..81658a7 100644 --- a/Selenium/ComInterfaces/_Window.cs +++ b/Selenium/ComInterfaces/_Window.cs @@ -34,6 +34,9 @@ public interface _Window { [DispId(550), Description("Get the title of the window")] string Title { get; } + [DispId(551), Description("Get the handle of the window")] + string Handle { get; } + [DispId(554), Description("Gets the position of the browser window relative to the upper-left corner of the screen.")] Point Position(); From 59d273a3bad62f499d6966cbe2f8d1bb4e80a5e8 Mon Sep 17 00:00:00 2001 From: zc2 Date: Fri, 28 Jan 2022 17:09:05 -0500 Subject: [PATCH 02/16] Made the handle unique to let the SB be used from different accounts at the same time --- Selenium/Core/SysWaiter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Selenium/Core/SysWaiter.cs b/Selenium/Core/SysWaiter.cs index dd36753..cde293e 100644 --- a/Selenium/Core/SysWaiter.cs +++ b/Selenium/Core/SysWaiter.cs @@ -38,7 +38,7 @@ static SysWaiter() { bool createdNew; _signal_interrupt = new EventWaitHandle(false, EventResetMode.ManualReset, - @"Global\InterruptKey", + @"Global\InterruptKey"+ Environment.UserName, out createdNew, security); From a140b544c36a5c79702ffee614e4cc40ae26b80e Mon Sep 17 00:00:00 2001 From: zc2 Date: Fri, 28 Jan 2022 17:11:14 -0500 Subject: [PATCH 03/16] Fixed the problem of closing the window and making the window cache in not sync --- Selenium/Common/Window.cs | 4 +--- Selenium/Core/WindowContext.cs | 35 ++++++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Selenium/Common/Window.cs b/Selenium/Common/Window.cs index 297c40c..2d69d87 100644 --- a/Selenium/Common/Window.cs +++ b/Selenium/Common/Window.cs @@ -191,9 +191,7 @@ public void FullScreen() { /// Closes the current window. /// public void Close() { - if (this != _windows.CurrentWindow) - this.Activate(); - _session.Send(RequestMethod.DELETE, "/window"); + _windows.Close( this ); } } diff --git a/Selenium/Core/WindowContext.cs b/Selenium/Core/WindowContext.cs index cad8fbf..7237e45 100644 --- a/Selenium/Core/WindowContext.cs +++ b/Selenium/Core/WindowContext.cs @@ -37,6 +37,31 @@ public WindowContext(RemoteSession session) _cachedWindows.Add(_currentWindow); } + /// + /// Removes the specific window from all references and closes it + /// + public void Close(Window w) + { + if( w == null ) return; + _cachedWindows.Remove(w); + bool not_current = w != _currentWindow; + if( not_current ) { + ActivateWindow(_session, w.Handle); + } + _session.Send(RequestMethod.DELETE, "/window"); + if( not_current ) { + ActivateWindow(_session, _currentWindow.Handle); + if( _previousWindow == w ) + _previousWindow = _currentWindow; + } else if( _previousWindow != _currentWindow && _previousWindow != null ) { + ActivateWindow(_session, _previousWindow.Handle); + _currentWindow = _previousWindow; + } else { + _currentWindow = null; + _previousWindow = null; + } + } + public void ListWindows(out List windows, out List handles) { handles = WindowContext.GetWindowsHandles(_session); windows = new List(handles.Count); @@ -72,7 +97,7 @@ public Window SwitchToWindowByTitle(string title, int timeout = -1) { List windows, handles; this.ListWindows(out windows, out handles); foreach (Window win in windows) { - if (win.Handle == _currentWindow.Handle) + if (_currentWindow != null && win.Handle == _currentWindow.Handle) continue; WindowContext.ActivateWindow(_session, win.Handle); string winTitle = GetCurrentTitle(_session); @@ -107,7 +132,7 @@ public Window SwitchToWindowByName(string name, int timeout = -1) { return _currentWindow; } } - _currentWindow = new Window(_session, this, null); + _currentWindow = new Window(_session, this, handle); _cachedWindows.Add(_currentWindow); return _currentWindow; } catch (Errors.NoSuchWindowError) { } @@ -126,8 +151,8 @@ public Window SwitchToWindowByName(string name, int timeout = -1) { /// public Window SwitchToNextWindow(int timeout = -1) { DateTime endTime = _session.GetEndTime(timeout); - string currentHandle = _currentWindow.Handle; - string previousHandle = _previousWindow.Handle; + string currentHandle = _currentWindow?.Handle; + string previousHandle = _previousWindow?.Handle; while (true) { List windows, handles; @@ -162,6 +187,8 @@ public Window SwitchToNextWindow(int timeout = -1) { /// /// public Window SwitchToPreviousWindow() { + if( _previousWindow == null ) + return null; return _previousWindow.Activate(); } From e0a75782841025e1702453e76f296bb584731409 Mon Sep 17 00:00:00 2001 From: zc2 Date: Fri, 28 Jan 2022 17:18:45 -0500 Subject: [PATCH 04/16] Fixed a bug of deleting the last item in a list --- Selenium/Struct/List.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Selenium/Struct/List.cs b/Selenium/Struct/List.cs index ad7672e..a1675c7 100644 --- a/Selenium/Struct/List.cs +++ b/Selenium/Struct/List.cs @@ -309,7 +309,8 @@ public void Remove(object value) { public void RemoveAt(int index) { if (index < 0 || index >= _count) throw new IndexOutOfRangeException(); - Array.Copy(_items, index + 1, _items, index, _count - index); + if( index < _count - 1 ) + Array.Copy(_items, index + 1, _items, index, _count - index); _items[--_count] = null; } From f08997dfedb6ef40d36043acbf5851be2bfd2de0 Mon Sep 17 00:00:00 2001 From: zc2 Date: Fri, 28 Jan 2022 17:55:47 -0500 Subject: [PATCH 05/16] Project upgraded to VS2019. Also switched the framework to .NET v4 and the architecture to 64bit --- .gitignore | 1 + Selenium/Selenium.csproj | 516 +++++++++++++++++++++------------------ SeleniumBasic.sln | 102 +++++--- 3 files changed, 343 insertions(+), 276 deletions(-) diff --git a/.gitignore b/.gitignore index 4e4028e..3eff3b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.vs References bin obj diff --git a/Selenium/Selenium.csproj b/Selenium/Selenium.csproj index 53020c5..dbcfdb6 100644 --- a/Selenium/Selenium.csproj +++ b/Selenium/Selenium.csproj @@ -1,249 +1,291 @@ - - - - - Debug - AnyCPU - {B30CE36F-3653-4610-9519-FAA61E8FC12B} - Library - Properties - Selenium - Selenium - v3.5 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 2 - true - false - x86 - true - - - pdbonly - false - bin\Release\ - TRACE - prompt - 4 - true - bin\Release\Selenium.XML - false - AnyCPU - false - - - - - - true - ..\..\..\AppData\Local\SeleniumBasic\ - TRACE;DEBUG;Test - true - full - x86 - false - prompt - MinimumRecommendedRules.ruleset - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Form - - - ExceptionDialog.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + AnyCPU + {B30CE36F-3653-4610-9519-FAA61E8FC12B} + Library + Properties + Selenium + Selenium + v4.0 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 2 + true + false + AnyCPU + true + + + pdbonly + false + bin\Release\ + TRACE + prompt + 4 + true + bin\Release\Selenium.XML + false + AnyCPU + false + + + + + + true + ..\..\..\AppData\Local\SeleniumBasic\ + TRACE;DEBUG;Test + true + full + AnyCPU + false + prompt + MinimumRecommendedRules.ruleset + false + + + true + + + key.snk + + + true + bin\x64\Debug\ + DEBUG;TRACE + true + 2 + true + full + x64 + false + 7.3 + prompt + + + bin\x64\Release\ + TRACE + true + bin\Release\Selenium.XML + pdbonly + x64 + false + 7.3 + prompt + + + true + bin\x64\Test\ + TRACE;DEBUG;Test + true + full + x64 + false + 7.3 + prompt + MinimumRecommendedRules.ruleset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + ExceptionDialog.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + xcopy /D /Y "$(ProjectDir)\..\References\firefoxdriver.xpi" . xcopy /D /Y "$(ProjectDir)\..\References\chromedriver.exe" . xcopy /D /Y "$(ProjectDir)\..\References\operadriver.exe" . xcopy /D /Y "$(ProjectDir)\..\References\iedriver.exe" . xcopy /D /Y "$(ProjectDir)\..\References\iedriver64.exe" . -xcopy /D /Y "$(ProjectDir)\..\References\phantomjs.exe" . - +xcopy /D /Y "$(ProjectDir)\..\References\phantomjs.exe" . + + --> \ No newline at end of file diff --git a/SeleniumBasic.sln b/SeleniumBasic.sln index 8e10ad3..96baf81 100644 --- a/SeleniumBasic.sln +++ b/SeleniumBasic.sln @@ -1,39 +1,63 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Selenium", "Selenium\Selenium.csproj", "{B30CE36F-3653-4610-9519-FAA61E8FC12B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Selenium.Tests", "Selenium.Tests\Selenium.Tests.csproj", "{09D6B13B-4B98-43E8-AB8E-8BE14C515E37}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VbsConsole", "VbsConsole\VbsConsole.csproj", "{FB9F9DAE-3024-49AF-B017-8AECFDECF844}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - Test|Any CPU = Test|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Release|Any CPU.Build.0 = Release|Any CPU - {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Test|Any CPU.ActiveCfg = Test|Any CPU - {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Test|Any CPU.Build.0 = Test|Any CPU - {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Release|Any CPU.ActiveCfg = Release|Any CPU - {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Release|Any CPU.Build.0 = Release|Any CPU - {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Test|Any CPU.ActiveCfg = Test|Any CPU - {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Test|Any CPU.Build.0 = Test|Any CPU - {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Release|Any CPU.Build.0 = Release|Any CPU - {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Test|Any CPU.ActiveCfg = Release|Any CPU - {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Test|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31911.196 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Selenium", "Selenium\Selenium.csproj", "{B30CE36F-3653-4610-9519-FAA61E8FC12B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Selenium.Tests", "Selenium.Tests\Selenium.Tests.csproj", "{09D6B13B-4B98-43E8-AB8E-8BE14C515E37}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VbsConsole", "VbsConsole\VbsConsole.csproj", "{FB9F9DAE-3024-49AF-B017-8AECFDECF844}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Test|Any CPU = Test|Any CPU + Test|x64 = Test|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Debug|x64.ActiveCfg = Debug|x64 + {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Debug|x64.Build.0 = Debug|x64 + {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Release|Any CPU.Build.0 = Release|Any CPU + {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Release|x64.ActiveCfg = Release|x64 + {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Release|x64.Build.0 = Release|x64 + {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Test|Any CPU.ActiveCfg = Test|Any CPU + {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Test|Any CPU.Build.0 = Test|Any CPU + {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Test|x64.ActiveCfg = Test|x64 + {B30CE36F-3653-4610-9519-FAA61E8FC12B}.Test|x64.Build.0 = Test|x64 + {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Debug|x64.ActiveCfg = Debug|x64 + {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Debug|x64.Build.0 = Debug|x64 + {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Release|Any CPU.ActiveCfg = Release|Any CPU + {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Release|Any CPU.Build.0 = Release|Any CPU + {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Release|x64.ActiveCfg = Release|x64 + {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Release|x64.Build.0 = Release|x64 + {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Test|Any CPU.ActiveCfg = Test|Any CPU + {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Test|Any CPU.Build.0 = Test|Any CPU + {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Test|x64.ActiveCfg = Test|x64 + {09D6B13B-4B98-43E8-AB8E-8BE14C515E37}.Test|x64.Build.0 = Test|x64 + {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Debug|x64.ActiveCfg = Debug|x64 + {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Debug|x64.Build.0 = Debug|x64 + {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Release|Any CPU.Build.0 = Release|Any CPU + {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Release|x64.ActiveCfg = Release|x64 + {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Release|x64.Build.0 = Release|x64 + {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Test|Any CPU.ActiveCfg = Release|Any CPU + {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Test|Any CPU.Build.0 = Release|Any CPU + {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Test|x64.ActiveCfg = Debug|x64 + {FB9F9DAE-3024-49AF-B017-8AECFDECF844}.Test|x64.Build.0 = Debug|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8A2A18B5-A406-4666-B498-0328C4295FDF} + EndGlobalSection +EndGlobal From 678c5a878fdd0cb20d6abf38d63e92f3adf9ad8b Mon Sep 17 00:00:00 2001 From: zc2 Date: Mon, 31 Jan 2022 13:59:40 -0500 Subject: [PATCH 06/16] Chenged the interrupt key from Esc to Alt+Esc --- Selenium/Core/SysWaiter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Selenium/Core/SysWaiter.cs b/Selenium/Core/SysWaiter.cs index cde293e..c6a5ceb 100644 --- a/Selenium/Core/SysWaiter.cs +++ b/Selenium/Core/SysWaiter.cs @@ -6,7 +6,7 @@ namespace Selenium.Core { /// - /// Provides waits methods that can be interrupted with a regitered hot key + /// Provides waits methods that can be interrupted with a registered hot key /// class SysWaiter { @@ -42,7 +42,7 @@ static SysWaiter() { out createdNew, security); - HotKeyGlobal.DefineHotKey(MOD_NONE, VK_ESCAPE, ProcInterrupt); + HotKeyGlobal.DefineHotKey(MOD_ALT, VK_ESCAPE, ProcInterrupt); } public static Action OnInterrupt { From 05a172749b2cd46ffcdf6e593b792783c1ccdbeb Mon Sep 17 00:00:00 2001 From: zc2 Date: Tue, 1 Feb 2022 17:46:49 -0500 Subject: [PATCH 07/16] Fixed a typo --- Selenium/Internal/IOExt.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Selenium/Internal/IOExt.cs b/Selenium/Internal/IOExt.cs index c7a0ab7..55c5ba7 100644 --- a/Selenium/Internal/IOExt.cs +++ b/Selenium/Internal/IOExt.cs @@ -25,7 +25,7 @@ public static string GetRandomName() { /// /// Returns the directory that serves as a common repository for application-specific - // data for the current roaming user. + /// data for the current roaming user. /// public static string AppDataFolder { get { From 7e1fdb0367122beac8af5baacb73a0e9ab7583de Mon Sep 17 00:00:00 2001 From: zc2 Date: Fri, 25 Feb 2022 09:16:34 -0500 Subject: [PATCH 08/16] Many changes made to support the latest W3C driver API and GeckoDriver --- Selenium.Tests/Internals/BaseBrowsers.cs | 50 +- Selenium.Tests/Internals/WebServer.cs | 2 +- Selenium.Tests/Program.cs | 2 + Selenium.Tests/Selenium.Tests.csproj | 264 ++-- Selenium.Tests/TS_Actions.cs | 103 +- Selenium.Tests/TS_Alert.cs | 11 +- Selenium.Tests/TS_By.cs | 5 +- Selenium.Tests/TS_Capture.cs | 6 +- Selenium.Tests/TS_Element.cs | 14 +- Selenium.Tests/TS_Excel.cs | 2 +- Selenium.Tests/TS_ExecuteScript.cs | 6 +- Selenium.Tests/TS_Frame.cs | 8 +- Selenium.Tests/TS_Keyboard.cs | 71 +- Selenium.Tests/TS_Manage.cs | 14 +- Selenium.Tests/TS_Mouse.cs | 35 +- Selenium.Tests/TS_Scraping.cs | 6 +- Selenium.Tests/TS_SearchContext.cs | 6 +- Selenium.Tests/TS_Select.cs | 6 +- Selenium.Tests/TS_Verify.cs | 4 +- Selenium.Tests/TS_Window.cs | 6 +- Selenium.Tests/TS_Windows.cs | 8 +- Selenium.Tests/TS_zip.cs | 1 + Selenium.Tests/packages.config | 5 + Selenium/ComInterfaces/_Verify.cs | 14 +- Selenium/Common/Actions.cs | 344 ++++- Selenium/Common/Alert.cs | 20 +- Selenium/Common/Keyboard.cs | 55 +- Selenium/Common/Mouse.cs | 64 +- Selenium/Common/SearchContext.cs | 45 +- Selenium/Common/SelectElement.cs | 3 +- Selenium/Common/WebElement.cs | 76 +- Selenium/Common/Window.cs | 23 +- Selenium/Core/FrameContext.cs | 3 +- Selenium/Core/JavascriptContext.cs | 12 +- Selenium/Core/RemoteServer.cs | 59 +- Selenium/Core/RemoteSession.cs | 10 +- Selenium/Core/WindowContext.cs | 56 +- Selenium/Drivers/GeckoDriver.cs | 83 ++ Selenium/Properties/AssemblyInfo.cs | 4 +- Selenium/Selenium.csproj | 14 +- Selenium/SeleniumException.cs | 6 +- Selenium/Serializer/JSON.cs | 16 +- Selenium/Struct/List.cs | 2 +- Selenium/WebDriver.cs | 23 + Selenium/Zip/ZipFile.cs | 2 +- SeleniumBasic.sln | 24 +- Setup/Setup.vdproj | 1625 ++++++++++++++++++++++ VbsConsole/VbsConsole.csproj | 172 +-- VbsConsole/app.config | 3 + 49 files changed, 2952 insertions(+), 441 deletions(-) create mode 100755 Selenium.Tests/packages.config create mode 100755 Selenium/Drivers/GeckoDriver.cs create mode 100755 Setup/Setup.vdproj create mode 100755 VbsConsole/app.config diff --git a/Selenium.Tests/Internals/BaseBrowsers.cs b/Selenium.Tests/Internals/BaseBrowsers.cs index 572dc02..a08a737 100644 --- a/Selenium.Tests/Internals/BaseBrowsers.cs +++ b/Selenium.Tests/Internals/BaseBrowsers.cs @@ -1,23 +1,28 @@ using NUnit.Framework; using System; - +using System.Diagnostics; + namespace Selenium.Tests.Internals { public enum Browser { - Firefox, Chrome, Opera, IE, PhantomJS + Firefox, Chrome, Opera, IE, PhantomJS, Gecko, Edge } abstract class BaseBrowsers { - + public static bool remote_driver = false; public readonly object Fixture; - + private Process driver_process; protected WebDriver driver; protected Keys Keys = new Keys(); protected By By = new By(); public BaseBrowsers(Browser browser) { WebServer.StartServer(@"..\..\Pages"); - this.driver = GetBrowserInstance(browser); + if( remote_driver ) { + this.driver = GetBrowserWithDriverStartedRemotely(browser); + } else { + this.driver = GetBrowserInstance(browser); + } this.driver.BaseUrl = WebServer.BaseUri; this.Fixture = browser; } @@ -25,11 +30,16 @@ public BaseBrowsers(Browser browser) { [TestFixtureTearDown] public void TestFixtureTearDown() { driver.Quit(); + if( driver_process != null && !driver_process.HasExited ) { + driver_process.Kill(); + } } private string GetBrowserTypeLib(Browser browser) { switch (browser) { + case Browser.Gecko: return "Selenium.GeckoDriver"; case Browser.Firefox: return "Selenium.FirefoxDriver"; + case Browser.Edge: return "Selenium.EdgeDriver"; case Browser.Chrome: return "Selenium.ChromeDriver"; case Browser.Opera: return "Selenium.OperaDriver"; case Browser.IE: return "Selenium.IEDriver"; @@ -40,7 +50,12 @@ private string GetBrowserTypeLib(Browser browser) { private WebDriver GetBrowserInstance(Browser browser) { switch (browser) { - case Browser.Firefox: return new Selenium.FirefoxDriver(); + case Browser.Gecko: return new Selenium.GeckoDriver(); + case Browser.Firefox: + Selenium.FirefoxDriver ffd = new Selenium.FirefoxDriver(); + ffd.SetBinary( @"C:\Utils\Firefox46\firefox.exe" ); + return ffd; + case Browser.Edge: return new Selenium.EdgeDriver(); case Browser.Chrome: return new Selenium.ChromeDriver(); case Browser.Opera: return new Selenium.OperaDriver(); case Browser.IE: return new Selenium.IEDriver(); @@ -49,6 +64,27 @@ private WebDriver GetBrowserInstance(Browser browser) { throw new Exception("Browser not supported: " + browser.ToString()); } + private WebDriver GetBrowserWithDriverStartedRemotely(Browser browser) { + ProcessStartInfo p_info = new ProcessStartInfo(); + p_info.UseShellExecute = true; + p_info.CreateNoWindow = false; + p_info.WindowStyle = ProcessWindowStyle.Normal; + switch (browser) { + case Browser.Gecko: + p_info.FileName = "geckodriver.exe"; + p_info.Arguments = "-vv"; + break; + case Browser.Chrome: + p_info.FileName = "chromedriver.exe"; + p_info.Arguments = "--port=4444 --verbose"; + break; + default: throw new Exception("Browser not supported: " + browser.ToString()); + } + driver_process = Process.Start(p_info); + NUnit.Framework.Assert.False( driver_process.HasExited, "Driver process cannot start: " + browser.ToString() ); + WebDriver s = new Selenium.WebDriver(); + s.StartRemotely( "http://localhost:4444/", browser.ToString().ToLower() ); + return s; + } } - } diff --git a/Selenium.Tests/Internals/WebServer.cs b/Selenium.Tests/Internals/WebServer.cs index b41a7d1..b43feb2 100644 --- a/Selenium.Tests/Internals/WebServer.cs +++ b/Selenium.Tests/Internals/WebServer.cs @@ -10,7 +10,7 @@ namespace Selenium.Tests.Internals { public class WebServer { - public static string BaseUri = @"http://localhost:9328"; + public static string BaseUri = @"http://localhost:19328"; static WebServer _server = null; diff --git a/Selenium.Tests/Program.cs b/Selenium.Tests/Program.cs index 3e4f8ea..ac10f43 100644 --- a/Selenium.Tests/Program.cs +++ b/Selenium.Tests/Program.cs @@ -8,6 +8,8 @@ static void Main(string[] args) { string[] my_args = { Assembly.GetExecutingAssembly().Location, "/run:Selenium.Tests" + // to test an individual fixture, uncomment the following line and add ', Category = "InFocus"' to the fixture +// ,"/include:InFocus" }; int returnCode = NUnit.ConsoleRunner.Runner.Main(my_args); diff --git a/Selenium.Tests/Selenium.Tests.csproj b/Selenium.Tests/Selenium.Tests.csproj index 0e7e233..d97473d 100644 --- a/Selenium.Tests/Selenium.Tests.csproj +++ b/Selenium.Tests/Selenium.Tests.csproj @@ -1,138 +1,140 @@ - - - - - Debug - AnyCPU - {09D6B13B-4B98-43E8-AB8E-8BE14C515E37} - Library - Properties - Selenium.Tests - Selenium.Tests - v3.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - x86 - false - - - pdbonly - false - bin\Release\ - DEBUG;TRACE - prompt - 2 - x86 - - - - - - - true - bin\Test\ - TRACE;DEBUG - full - x86 - false - prompt - MinimumRecommendedRules.ruleset - - - - ..\References\nunit\nunit-console-runner.dll - - - False - ..\References\nunit\nunit.framework.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - - - - - {b30ce36f-3653-4610-9519-faa61e8fc12b} - Selenium - - - - - xcopy /D /Y "$(ProjectDir)\..\References\firefoxdriver.xpi" . -xcopy /D /Y "$(ProjectDir)\..\References\chromedriver.exe" . -xcopy /D /Y "$(ProjectDir)\..\References\operadriver.exe" . -xcopy /D /Y "$(ProjectDir)\..\References\iedriver.exe" . -xcopy /D /Y "$(ProjectDir)\..\References\phantomjs.exe" . - + + + + + Debug + AnyCPU + {09D6B13B-4B98-43E8-AB8E-8BE14C515E37} + Exe + Properties + Selenium.Tests + Selenium.Tests + v4.0 + 512 + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + x64 + false + + + pdbonly + false + bin\Release\ + DEBUG;TRACE + prompt + 2 + x86 + + + + + + + true + bin\Test\ + TRACE;DEBUG + full + x86 + false + prompt + MinimumRecommendedRules.ruleset + + + + ..\packages\NUnit.Runners.2.6.4\tools\lib\nunit-console-runner.dll + + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + + + + {b30ce36f-3653-4610-9519-faa61e8fc12b} + Selenium + + + + + + + + xcopy /D /Y "$(ProjectDir)\..\References\chromedriver.exe" . +xcopy /D /Y "$(ProjectDir)\..\References\geckodriver.exe" . + + --> \ No newline at end of file diff --git a/Selenium.Tests/TS_Actions.cs b/Selenium.Tests/TS_Actions.cs index 7f61b9f..ce337a3 100644 --- a/Selenium.Tests/TS_Actions.cs +++ b/Selenium.Tests/TS_Actions.cs @@ -1,28 +1,33 @@ using Selenium.Tests.Internals; +using System.ComponentModel; using SetUp = NUnit.Framework.SetUpAttribute; using TestCase = NUnit.Framework.TestCaseAttribute; using TestFixture = NUnit.Framework.TestFixtureAttribute; namespace Selenium.Tests { - [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] [TestFixture(Browser.PhantomJS)] +*/ class TS_Actions : BaseBrowsers { - public TS_Actions(Browser browser) : base(browser) { } [SetUp] public void SetUp() { - driver.Get("/input.html"); } [TestCase] - public void ShouldPerformActionsWithoutFailures() { - var ele = driver.FindElementById("input__search"); + public void LocalShouldNotFail() { + this.driver.BaseUrl = WebServer.BaseUri; + driver.Get("/input.html"); + WebElement ele = driver.FindElementById("input__search"); + Assert.NotEquals( ele, null, "Failed to find element by Id" ); driver.Actions .Click(ele) .Click() @@ -32,8 +37,6 @@ public void ShouldPerformActionsWithoutFailures() { .Release() .ClickDouble() .ClickDouble(ele) - .DragAndDrop(ele, ele) - .DragAndDropByOffset(ele, 10, 10) .KeyDown(Keys.Control, ele) .KeyUp(Keys.Control) .MoveByOffset(30, 87) @@ -41,8 +44,92 @@ public void ShouldPerformActionsWithoutFailures() { .SendKeys("abcd") .Wait(0) .ClickContext() + .Wait(1000) .ClickContext(ele) + .Wait(1000) + .Perform(); + } + + [TestCase] + public void DemoQAButtons() { + driver.Get("https://demoqa.com/buttons"); + WebElement ele; + ele = driver.FindElementByXPath("//button[@type='button' and .='Click Me']"); + Assert.NotEquals( ele, null, "Left click button" ); + driver.Actions + .Click(ele) + .Perform(); + WebElement e_scm = driver.FindElementById("dynamicClickMessage"); + Assert.NotEquals( e_scm, null ); + + ele = driver.FindElementById("rightClickBtn"); + Assert.NotEquals( ele, null, "Right click button" ); + driver.Actions + .ClickContext(ele) + .Perform(); + WebElement e_rcm = driver.FindElementById("rightClickMessage"); + Assert.NotEquals( e_rcm, null ); + + ele = driver.FindElementById("doubleClickBtn"); + Assert.NotEquals( ele, null, "Right click button" ); + driver.Actions + .ClickDouble(ele) + .Perform(); + WebElement e_dcm = driver.FindElementById("doubleClickMessage"); + Assert.NotEquals( e_dcm, null ); + } + + [TestCase] + public void DemoQASlider() { + driver.Get("https://demoqa.com/slider"); + WebElement ele; + ele = driver.FindElementByXPath("//input[@type='range']"); + Assert.NotEquals( ele, null ); + driver.Actions + .ClickAndHold(ele) + .MoveByOffset( 20, 0 ) + .Release() + .Wait( 2000 ) + .ClickAndHold() + .MoveByOffset( -40, 0 ) + .Release() + .Wait( 1000 ) + .Perform(); + } + + [TestCase] + public void DemoQAHover() { + driver.Get("https://demoqa.com/tool-tips"); + WebElement ele; + ele = driver.FindElementById("toolTipButton"); + Assert.NotEquals( ele, null ); + driver.Actions + .MoveToElement(ele) + .Wait( 1000 ) + .Perform(); + Assert.NotEquals( driver.FindElementById("buttonToolTip"), null ); + } + + [TestCase] + public void DemoQADragDrop() { + driver.Get("https://demoqa.com/dragabble"); + WebElement ele = driver.FindElementById("dragBox"); + Assert.NotEquals( ele, null ); + driver.Actions + .DragAndDropByOffset(ele, 100, 50) + .Wait( 1000 ) + .Perform(); + driver.Wait( 1000 ); + driver.Get("https://demoqa.com/droppable"); + WebElement ele1 = driver.FindElementById("draggable"); + WebElement ele2 = driver.FindElementById("droppable"); + Assert.NotEquals( ele1, null ); + Assert.NotEquals( ele1, null ); + driver.Actions + .DragAndDrop(ele1, ele2) + .Wait( 1000 ) .Perform(); + Assert.Equals( ele2.Text(), "Dropped!" ); } } diff --git a/Selenium.Tests/TS_Alert.cs b/Selenium.Tests/TS_Alert.cs index f46955a..d74c63e 100644 --- a/Selenium.Tests/TS_Alert.cs +++ b/Selenium.Tests/TS_Alert.cs @@ -5,12 +5,15 @@ using ExpectedException = NUnit.Framework.ExpectedExceptionAttribute; namespace Selenium.Tests { - [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] - //[TestFixture(Browser.PhantomJS)] + [TestFixture(Browser.PhantomJS)] +*/ class TS_Alert : BaseBrowsers { public TS_Alert(Browser browser) @@ -72,10 +75,10 @@ public void ShouldThrowMissingAlert() { public void ShouldThrowUnexpectedAlert() { driver.Get("/input.html"); driver.ExecuteScript("window.setTimeout(function(){window.alert('na');}, 0);"); + driver.Wait( 1000 ); var ele = driver.FindElementById("input__search"); ele.Clear(); } - } } diff --git a/Selenium.Tests/TS_By.cs b/Selenium.Tests/TS_By.cs index 6dd1e63..310da07 100644 --- a/Selenium.Tests/TS_By.cs +++ b/Selenium.Tests/TS_By.cs @@ -8,6 +8,9 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] + [TestFixture(Browser.Gecko)] + [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] class TS_By : BaseBrowsers { _By by = new By(); @@ -74,8 +77,6 @@ public void ShouldFindElementByAny() { A.AreEqual("Div4", ele.Text()); } - - [TestCase] public void ShouldFindElementsById() { var elts = driver.FindElements(by.Id("id-4")); diff --git a/Selenium.Tests/TS_Capture.cs b/Selenium.Tests/TS_Capture.cs index 1ce5ef2..3fe84ae 100644 --- a/Selenium.Tests/TS_Capture.cs +++ b/Selenium.Tests/TS_Capture.cs @@ -8,10 +8,14 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] [TestFixture(Browser.PhantomJS)] +*/ class TS_Capture : BaseBrowsers { public TS_Capture(Browser browser) diff --git a/Selenium.Tests/TS_Element.cs b/Selenium.Tests/TS_Element.cs index 4405bf6..2ac7fd7 100644 --- a/Selenium.Tests/TS_Element.cs +++ b/Selenium.Tests/TS_Element.cs @@ -8,10 +8,14 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] [TestFixture(Browser.PhantomJS)] +*/ class TS_Element : BaseBrowsers { public TS_Element(Browser browser) @@ -21,7 +25,7 @@ public TS_Element(Browser browser) public void SetUp() { driver.Get("/element.html"); } - +/* [TestCase] public void ShouldReturnDisplayState() { var ele1 = driver.FindElementById("input__search"); @@ -97,7 +101,7 @@ public void ShouldReturnSize() { A.AreEqual(308, size.Width); A.AreEqual(58, size.Height); } - +*/ [TestCase] public void ShouldReturnActiveElement() { var ele1 = driver.FindElementById("input__search"); @@ -106,7 +110,7 @@ public void ShouldReturnActiveElement() { var ele2 = driver.ActiveElement(); A.True(ele1.Equals(ele2)); } - +/* [TestCase] public void ShouldClearElement() { var ele1 = driver.FindElementById("input__search"); @@ -126,7 +130,7 @@ public void ShouldClickElement() { ele2.Click(); A.AreEqual("", ele1.Attribute("value")); } - +*/ } } diff --git a/Selenium.Tests/TS_Excel.cs b/Selenium.Tests/TS_Excel.cs index 2d90fb3..104d0fb 100644 --- a/Selenium.Tests/TS_Excel.cs +++ b/Selenium.Tests/TS_Excel.cs @@ -9,7 +9,7 @@ namespace Selenium.Tests { - [TestFixture] +// [TestFixture] class TS_Excel : BaseExcel { [Test] diff --git a/Selenium.Tests/TS_ExecuteScript.cs b/Selenium.Tests/TS_ExecuteScript.cs index efbf061..8573529 100644 --- a/Selenium.Tests/TS_ExecuteScript.cs +++ b/Selenium.Tests/TS_ExecuteScript.cs @@ -8,10 +8,14 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] [TestFixture(Browser.PhantomJS)] +*/ class TS_ExecuteScript : BaseBrowsers { public TS_ExecuteScript(Browser browser) diff --git a/Selenium.Tests/TS_Frame.cs b/Selenium.Tests/TS_Frame.cs index ceb6a3f..5b6a087 100644 --- a/Selenium.Tests/TS_Frame.cs +++ b/Selenium.Tests/TS_Frame.cs @@ -7,10 +7,14 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] [TestFixture(Browser.PhantomJS)] +*/ class TS_Frame : BaseBrowsers { public TS_Frame(Browser browser) @@ -77,7 +81,5 @@ public void ShouldSwitchToParentFrame() { driver.SwitchToParentFrame(); A.AreEqual("Frame2", driver.FindElementById("info").Text()); } - } - } diff --git a/Selenium.Tests/TS_Keyboard.cs b/Selenium.Tests/TS_Keyboard.cs index 102ebbb..4355db4 100644 --- a/Selenium.Tests/TS_Keyboard.cs +++ b/Selenium.Tests/TS_Keyboard.cs @@ -4,13 +4,27 @@ using TestCase = NUnit.Framework.TestCaseAttribute; using TestFixture = NUnit.Framework.TestFixtureAttribute; +/* + * Attention! + * Gecko does not return the input control value via the Attribute. + * Even when executed in the F12 console, the result of the following will be null: + * input__search.getAttribute("value") + * In this test all ele.Attribute("value") are replaced with ele.Value() + * which now uses the driver's endpoint property/value instead of attribute/value + * +*/ + namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] [TestFixture(Browser.PhantomJS)] +*/ class TS_Keyboard : BaseBrowsers { public TS_Keyboard(Browser browser) @@ -27,30 +41,35 @@ public void ShouldSendAndReadCharsBMP() { var ele = driver.FindElementById("input__search"); ele.Clear(); ele.SendKeys(expected); - var result = ele.Attribute("value"); + var result = ele.Value(); A.AreEqual(expected, result); + driver.Wait( 1000 ); } [TestCase] - [IgnoreFixture(Browser.Chrome, "Not supported")] + [IgnoreFixture(Browser.Edge, "MSEdgeDriver only supports characters in the BMP")] + [IgnoreFixture(Browser.Chrome, "ChromeDriver only supports characters in the BMP")] [IgnoreFixture(Browser.Opera, "Not supported")] public void ShouldSendAndReadCharsSMP() { string expected = "🌍🍈👽💔"; var ele = driver.FindElementById("input__search"); ele.Clear().SendKeys(expected); - var result = ele.Attribute("value"); + var result = ele.Value(); A.AreEqual(expected, result); + driver.Wait( 1000 ); } [TestCase] - [IgnoreFixture(Browser.Chrome, "Not supported")] + [IgnoreFixture(Browser.Edge, "MSEdgeDriver only supports characters in the BMP")] + [IgnoreFixture(Browser.Chrome, "ChromeDriver only supports characters in the BMP")] [IgnoreFixture(Browser.Opera, "Not supported")] public void ShouldSendAndReadCharsSIP() { string expected = "𠀀𠀁𠀂𠀃𤁴𤁵𤁶𫜲𫜳𫜴"; var ele = driver.FindElementById("input__search"); ele.Clear().SendKeys(expected); - var result = ele.Attribute("value"); + var result = ele.Value(); A.AreEqual(expected, result); + driver.Wait( 1000 ); } [TestCase] @@ -58,11 +77,11 @@ public void ShouldSendAndReadCharsSIP() { public void ShouldHandleModifiersWithDriver() { //Delete with driver SendKeys var ele = driver.FindElementById("input__search"); - ele.SendKeys("abcdefg"); - A.AreEqual("abcdefg", ele.Attribute("value")); + ele.SendKeys("abcdefg1"); + A.AreEqual("abcdefg1", ele.Value()); driver.SendKeys(Keys.Control, "a"); driver.SendKeys(Keys.Delete); - A.AreEqual("", ele.Attribute("value")); + A.AreEqual("", ele.Value()); } [TestCase] @@ -70,14 +89,14 @@ public void ShouldHandleModifiersWithDriver() { public void ShouldHandleModifiersWithKeyboard() { //Delete with driver SendKeys var ele = driver.FindElementById("input__search"); - ele.SendKeys("abcdefg"); - A.AreEqual("abcdefg", ele.Attribute("value")); + ele.SendKeys("abcdefg2"); + A.AreEqual("abcdefg2", ele.Value()); var kb = driver.Keyboard; kb.KeyDown(Keys.Control); kb.SendKeys("a"); kb.KeyUp(Keys.Control); kb.SendKeys(Keys.Delete); - A.AreEqual("", ele.Attribute("value")); + A.AreEqual("", ele.Value()); } [TestCase] @@ -85,10 +104,10 @@ public void ShouldHandleModifiersWithKeyboard() { public void ShouldHandleModifiersWithElement() { //Delete with element SendKeys var ele = driver.FindElementById("input__search"); - ele.SendKeys("abcdefg"); - A.AreEqual("abcdefg", ele.Attribute("value")); + ele.SendKeys("abcdefg3"); + A.AreEqual("abcdefg3", ele.Value()); ele.SendKeys(Keys.Control, "a" + Keys.Delete); - A.AreEqual("", ele.Attribute("value")); + A.AreEqual("", ele.Value()); } [TestCase] @@ -96,15 +115,29 @@ public void ShouldHandleModifiersWithElement() { public void ShouldHandleModifiersWithActions() { //Delete with action SendKeys var ele = driver.FindElementById("input__search"); - ele.SendKeys("abcdefg"); - A.AreEqual("abcdefg", ele.Attribute("value")); + ele.SendKeys("abcdefg4"); + A.AreEqual("abcdefg4", ele.Value()); driver.Actions .SendKeys(Keys.Control + "a", ele) .SendKeys(Keys.Delete) .Perform(); - A.AreEqual("", ele.Attribute("value")); + A.AreEqual("", ele.Value()); } + [TestCase] + [IgnoreFixture(Browser.IE, "Not supported")] + public void ShouldHandleExplicitModifiersWithActions() { + //Delete with action SendKeys + var ele = driver.FindElementById("input__search"); + ele.SendKeys("abcdefg5"); + A.AreEqual("abcdefg5", ele.Value()); + driver.Actions + .KeyDown(Keys.Control, ele) + .SendKeys("a", ele) + .KeyUp(Keys.Control) + .SendKeys(Keys.Delete) + .Perform(); + A.AreEqual("", ele.Value()); + } } - } diff --git a/Selenium.Tests/TS_Manage.cs b/Selenium.Tests/TS_Manage.cs index a5687f2..27426a7 100644 --- a/Selenium.Tests/TS_Manage.cs +++ b/Selenium.Tests/TS_Manage.cs @@ -1,4 +1,5 @@ using Selenium.Tests.Internals; +using System; using A = NUnit.Framework.Assert; using SetUp = NUnit.Framework.SetUpAttribute; using TestCase = NUnit.Framework.TestCaseAttribute; @@ -7,10 +8,14 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] [TestFixture(Browser.PhantomJS)] +*/ class TS_Manage : BaseBrowsers { public TS_Manage(Browser browser) @@ -27,7 +32,10 @@ public void ShouldAddAndDeleteCookies() { var manage = driver.Manage; A.AreEqual(0, manage.Cookies.Count); - manage.AddCookie("ck_name", "ck_value", null, "/", "2017-06-15T12:08:03"); + DateTime now = DateTime.Now; + DateTime exp = now.AddDays( 10 ); + string exp_s = exp.ToString("yyyy-MM-ddTHH:mm:ss"); + manage.AddCookie("ck_name", "ck_value", null, "/", exp_s); var cookies = manage.Cookies; A.AreEqual(1, manage.Cookies.Count); @@ -36,7 +44,7 @@ public void ShouldAddAndDeleteCookies() { A.AreEqual("ck_value", cookie.Value); A.AreEqual("localhost", cookie.Domain); A.AreEqual("/", cookie.Path); - A.AreEqual("2017-06-15T12:08:03", cookie.Expiry); + A.AreEqual(exp_s, cookie.Expiry); driver.Manage.DeleteAllCookies(); A.AreEqual(0, manage.Cookies.Count); diff --git a/Selenium.Tests/TS_Mouse.cs b/Selenium.Tests/TS_Mouse.cs index ec43656..9454878 100644 --- a/Selenium.Tests/TS_Mouse.cs +++ b/Selenium.Tests/TS_Mouse.cs @@ -7,10 +7,14 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] [TestFixture(Browser.PhantomJS)] +*/ class TS_Mouse : BaseBrowsers { public TS_Mouse(Browser browser) @@ -18,11 +22,12 @@ public TS_Mouse(Browser browser) [SetUp] public void SetUp() { - driver.Get("/input.html"); } [TestCase] public void ShouldDoubleClick() { + this.driver.BaseUrl = WebServer.BaseUri; + driver.Get("/input.html"); var ele = driver.FindElementById("input__search"); var mouse = driver.Mouse; @@ -32,6 +37,32 @@ public void ShouldDoubleClick() { mouse.Release(); } + [TestCase] + public void DemoQAButtons() { + driver.Get("https://demoqa.com/buttons"); + var mouse = driver.Mouse; + WebElement ele; + ele = driver.FindElementByXPath("//button[@type='button' and .='Click Me']"); + Assert.NotEquals( ele, null, "Left click button" ); + mouse.MoveTo(ele, 50); + mouse.Click(); + WebElement e_scm = driver.FindElementById("dynamicClickMessage"); + Assert.NotEquals( e_scm, null ); + + ele = driver.FindElementById("rightClickBtn"); + Assert.NotEquals( ele, null, "Right click button" ); + mouse.MoveTo(ele); + mouse.Click(MouseButton.Right); + WebElement e_rcm = driver.FindElementById("rightClickMessage"); + Assert.NotEquals( e_rcm, null ); + + ele = driver.FindElementById("doubleClickBtn"); + Assert.NotEquals( ele, null, "Double click button" ); + mouse.MoveTo(ele); + mouse.ClickDouble(); + WebElement e_dcm = driver.FindElementById("doubleClickMessage"); + Assert.NotEquals( e_dcm, null ); + } } } diff --git a/Selenium.Tests/TS_Scraping.cs b/Selenium.Tests/TS_Scraping.cs index 0cc8ac4..d68ede9 100644 --- a/Selenium.Tests/TS_Scraping.cs +++ b/Selenium.Tests/TS_Scraping.cs @@ -6,10 +6,14 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] [TestFixture(Browser.PhantomJS)] +*/ class TS_Scraping : BaseBrowsers { public TS_Scraping(Browser browser) diff --git a/Selenium.Tests/TS_SearchContext.cs b/Selenium.Tests/TS_SearchContext.cs index 58b4045..e7ad1c3 100644 --- a/Selenium.Tests/TS_SearchContext.cs +++ b/Selenium.Tests/TS_SearchContext.cs @@ -9,10 +9,14 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] [TestFixture(Browser.PhantomJS)] +*/ class TS_SearchContext : BaseBrowsers { public TS_SearchContext(Browser browser) diff --git a/Selenium.Tests/TS_Select.cs b/Selenium.Tests/TS_Select.cs index 75e074f..4f786a9 100644 --- a/Selenium.Tests/TS_Select.cs +++ b/Selenium.Tests/TS_Select.cs @@ -7,10 +7,14 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] [TestFixture(Browser.PhantomJS)] +*/ class TS_Select : BaseBrowsers { public TS_Select(Browser browser) diff --git a/Selenium.Tests/TS_Verify.cs b/Selenium.Tests/TS_Verify.cs index f80df6f..279bc4f 100644 --- a/Selenium.Tests/TS_Verify.cs +++ b/Selenium.Tests/TS_Verify.cs @@ -19,7 +19,7 @@ public void ShouldVerifyTrueOK() { [Test] public void ShouldVerifyTrueKO() { var res = verify.True(false); - A.AreEqual("KO expected=True was=False", res); + A.AreEqual("NOK expected=True was=False", res); } [Test] @@ -31,7 +31,7 @@ public void ShouldVerifyEnumEqual() { [Test] public void ShouldVerifyEnumNotEqual() { var res = verify.Equals("a;c;b", new string[] { "a", "b", "c" }, null); - A.AreEqual("KO expected=\"a;c;b\" was=[a,b,c]", res); + A.AreEqual("NOK expected=\"a;c;b\" was=[a,b,c]", res); } } diff --git a/Selenium.Tests/TS_Window.cs b/Selenium.Tests/TS_Window.cs index f127753..49410de 100644 --- a/Selenium.Tests/TS_Window.cs +++ b/Selenium.Tests/TS_Window.cs @@ -7,10 +7,14 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] [TestFixture(Browser.PhantomJS)] +*/ class TS_Window : BaseBrowsers { public TS_Window(Browser browser) diff --git a/Selenium.Tests/TS_Windows.cs b/Selenium.Tests/TS_Windows.cs index 65b943d..e323fac 100644 --- a/Selenium.Tests/TS_Windows.cs +++ b/Selenium.Tests/TS_Windows.cs @@ -8,10 +8,14 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Opera)] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] + [TestFixture(Browser.Edge)] +/* + [TestFixture(Browser.Opera)] [TestFixture(Browser.IE)] - //[TestFixture(Browser.PhantomJS)] + [TestFixture(Browser.PhantomJS)] +*/ class TS_Windows : BaseBrowsers { public TS_Windows(Browser browser) diff --git a/Selenium.Tests/TS_zip.cs b/Selenium.Tests/TS_zip.cs index 45e9c8e..d5db8a4 100644 --- a/Selenium.Tests/TS_zip.cs +++ b/Selenium.Tests/TS_zip.cs @@ -1,6 +1,7 @@ using Selenium.Zip; using System; using System.IO; +using System.ComponentModel; using A = NUnit.Framework.Assert; using Test = NUnit.Framework.TestAttribute; using TestFixture = NUnit.Framework.TestFixtureAttribute; diff --git a/Selenium.Tests/packages.config b/Selenium.Tests/packages.config new file mode 100755 index 0000000..70700fd --- /dev/null +++ b/Selenium.Tests/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Selenium/ComInterfaces/_Verify.cs b/Selenium/ComInterfaces/_Verify.cs index 32b437d..3a85030 100644 --- a/Selenium/ComInterfaces/_Verify.cs +++ b/Selenium/ComInterfaces/_Verify.cs @@ -9,25 +9,25 @@ namespace Selenium.ComInterfaces { [ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] public interface _Verify { - [DispId(564), Description("Returns if the value is true, otherwise.")] + [DispId(564), Description("Returns if the value is true, otherwise.")] string True(bool value, string failmessage = null); - [DispId(567), Description("Returns if the value is false, otherwise.")] + [DispId(567), Description("Returns if the value is false, otherwise.")] string False(bool value, string failmessage = null); - [DispId(574), Description("Returns if the objects are equal, otherwise.")] + [DispId(574), Description("Returns if the objects are equal, otherwise.")] string Equals(object expected, object input, string failmessage = null); - [DispId(579), Description("Returns if the objects are not equal, otherwise.")] + [DispId(579), Description("Returns if the objects are not equal, otherwise.")] string NotEquals(object expected, object input, string failmessage = null); - [DispId(583), Description("Returns if the text matches the pattern, otherwise.")] + [DispId(583), Description("Returns if the text matches the pattern, otherwise.")] string Matches(string pattern, string input, string failmessage = null); - [DispId(587), Description("Returns if the text does not match the pattern, otherwise.")] + [DispId(587), Description("Returns if the text does not match the pattern, otherwise.")] string NotMatches(string pattern, string input, string failmessage = null); - [DispId(589), Description("Returns if the text contains the value, otherwise.")] + [DispId(589), Description("Returns if the text contains the value, otherwise.")] string Contains(string value, string input, string failmessage = null); } diff --git a/Selenium/Common/Actions.cs b/Selenium/Common/Actions.cs index 582b85e..34d26dd 100644 --- a/Selenium/Common/Actions.cs +++ b/Selenium/Common/Actions.cs @@ -1,4 +1,5 @@ using Selenium.Core; +using Selenium.Serializer; using System; using System.Collections.Generic; using System.ComponentModel; @@ -22,6 +23,146 @@ namespace Selenium { [ComVisible(true), ClassInterface(ClassInterfaceType.None)] public class Actions : ComInterfaces._Actions { + internal enum SequenceType { + Mouse = 0, Keyboard = 1 + } + + internal class ActionSequence : IJsonObject { + private SequenceType _stype; + private List _actions; + private bool _has_real_actions; + private ActionSequence _other; + + internal ActionSequence( SequenceType type ) + { + _stype = type; + _actions = new List(); + } + + public void SetOther( ActionSequence oas ) { + _other = oas; + } + + public bool IsMouse() + { + return _stype == SequenceType.Mouse; + } + public bool IsKey() + { + return _stype == SequenceType.Keyboard; + } + public bool HasActions() + { + return _has_real_actions; + } + + public void Add( ActionBase a ) { + _actions.Add( a ); + if( !(a is ActionPause) ) { + _has_real_actions = true; + if( _other != null ) + _other.Add( new ActionPause() ); + } + } + + public virtual Dictionary SerializeJson() { + Dictionary dict = new Dictionary(); + if( IsMouse() ) { + dict.Add( "type", "pointer" ); + dict.Add( "id", "default mouse" ); + Dictionary parameters = new Dictionary(); + parameters.Add( "pointerType", "mouse" ); + dict.Add( "parameters", parameters ); + } else if( IsKey() ) { + dict.Add( "type", "key" ); + dict.Add( "id", "default keyboard" ); + } + dict.Add( "actions", _actions ); + return dict; + } + } + + internal class ActionBase : IJsonObject { + private string _type; + + internal ActionBase( string type ) + { + _type = type; + } + + public virtual Dictionary SerializeJson() { + Dictionary dict = new Dictionary(); + dict.Add( "type", _type ); + return dict; + } + } + + class ActionPause : ActionBase { + private int _duration; + public ActionPause() : this( 0 ) { } + public ActionPause( int duration ) : base( "pause" ) { + _duration = duration; + } + public override Dictionary SerializeJson() { + Dictionary dict = base.SerializeJson(); + dict.Add( "duration", _duration ); + return dict; + } + } + + internal class ActionMove : ActionBase { + private int _x, _y; + private string _elementId; + public ActionMove() : this( 0, 0 ) { } + public ActionMove( int x, int y ) : base( "pointerMove" ) { + _x = x; + _y = y; + } + public ActionMove( string id, int x = 0, int y = 0 ) : this( x, y ) { + _elementId = id; + } + public override Dictionary SerializeJson() { + Dictionary dict = base.SerializeJson(); + dict.Add( "x", _x ); + dict.Add( "y", _y ); + dict.Add( "duration", 250 ); + if( _elementId != null ) { + Dictionary origin_dict = new Dictionary(); + origin_dict.Add( WebElement.IDENTIFIER, _elementId ); + dict.Add( "origin", origin_dict ); + } else + dict.Add( "origin", "pointer" ); + return dict; + } + } + + internal class ActionPtrDownUp : ActionBase { + private MouseButton _button; + public ActionPtrDownUp( bool down ) : this( down, 0 ) { } + public ActionPtrDownUp( bool down, MouseButton button ) : base( down ? "pointerDown" : "pointerUp" ) { + _button = button; + } + public override Dictionary SerializeJson() { + Dictionary dict = base.SerializeJson(); + dict.Add( "button", (int)_button ); + return dict; + } + } + + internal class ActionKeyDownUp : ActionBase { + private char _key; + public ActionKeyDownUp( bool down, char key ) : base( down ? "keyDown" : "keyUp" ) { + _key = key; + } + public override Dictionary SerializeJson() { + Dictionary dict = base.SerializeJson(); + dict.Add( "value", _key ); + return dict; + } + } + + private List _action_sequences; + const char KEY_SHIFT = '\xE008'; const char KEY_CTRL = '\xE009'; const char KEY_ALT = '\xE00A'; @@ -42,34 +183,125 @@ internal Actions(RemoteSession session) { _mouse = session.mouse; _keyboard = session.keyboard; _actions = new List(); + if( !WebDriver.LEGACY ) { + _action_sequences = new List( 2 ); + ActionSequence m_s = new ActionSequence(SequenceType.Mouse); + ActionSequence k_s = new ActionSequence(SequenceType.Keyboard); + m_s.SetOther( k_s ); + k_s.SetOther( m_s ); + _action_sequences.Add( m_s ); + _action_sequences.Add( k_s ); + } } /// /// Performs all stored Actions. /// public void Perform() { - //perform actions - foreach (var action in _actions) - action(); - - //release the mouse if the state is down - if (_isMouseDown) - _mouse.Release(); - _isMouseDown = false; - - //release the keyboard if the modifiers keys are pressed - var modifiers = new StringBuilder(10); - if (_isKeyShiftDown) - modifiers.Append(KEY_SHIFT); - if (_isKeyCtrlDown) - modifiers.Append(KEY_CTRL); - if (_isKeyAltDown) - modifiers.Append(KEY_ALT); - if (modifiers.Length != 0) { - _keyboard.SendKeys(modifiers.ToString()); + if( WebDriver.LEGACY ) { + //perform actions + foreach (var action in _actions) + action(); + + //release the mouse if the state is down + if (_isMouseDown) + _mouse.Release(); + _isMouseDown = false; + + //release the keyboard if the modifiers keys are pressed + var modifiers = new StringBuilder(10); + if (_isKeyShiftDown) + modifiers.Append(KEY_SHIFT); + if (_isKeyCtrlDown) + modifiers.Append(KEY_CTRL); + if (_isKeyAltDown) + modifiers.Append(KEY_ALT); + if (modifiers.Length != 0) { + _keyboard.SendKeys(modifiers.ToString()); + _isKeyShiftDown = false; + _isKeyCtrlDown = false; + _isKeyAltDown = false; + } + } else { + if (_isKeyShiftDown) + GetKeySeq().Add( new ActionKeyDownUp( false, KEY_SHIFT ) ); + if (_isKeyCtrlDown) + GetKeySeq().Add( new ActionKeyDownUp( false, KEY_CTRL ) ); + if (_isKeyAltDown) + GetKeySeq().Add( new ActionKeyDownUp( false, KEY_ALT ) ); _isKeyShiftDown = false; _isKeyCtrlDown = false; _isKeyAltDown = false; + + + ActionSequence m_s = GetMouseSeq(); + ActionSequence k_s = GetKeySeq(); + if( !m_s.HasActions() ) + _action_sequences.Remove( m_s ); + if( !k_s.HasActions() ) + _action_sequences.Remove( k_s ); + + SendActions( _session, _action_sequences ); + } + } + + internal static void SendActions( RemoteSession session, ActionSequence a_s1 ) { + List a_s = new List( 1 ); + a_s.Add( a_s1 ); + Actions.SendActions( session, a_s ); + } + + internal static void SendActions( RemoteSession session, List a_s ) { + Dictionary actions_dict = new Dictionary(); + actions_dict.Add( "actions", a_s ); +#if DEBUG + JSON json = JSON.Serialize( actions_dict ); + Console.WriteLine( json.ToString() ); +#endif + session.Send(RequestMethod.POST, "/actions", actions_dict ); + } + + internal static void ReleaseActions( RemoteSession session ) { + session.Send(RequestMethod.DELETE, "/actions" ); + } + + private ActionSequence GetMouseSeq() { + return _action_sequences[ (int)SequenceType.Mouse ]; + } + + private ActionSequence GetKeySeq() { + return _action_sequences[ (int)SequenceType.Keyboard ]; + } + + private const int _DOWN = 1, _UP = 2; + + private void AddClickAction( WebElement element, MouseButton button, int what = _DOWN | _UP, int rep = 1 ) { + AddClickAction( GetMouseSeq(), element, button, what, rep ); + } + + internal static void AddClickAction( ActionSequence m_s, WebElement element, MouseButton button, int what = _DOWN | _UP, int rep = 1 ) { + if( element != null ) + m_s.Add( new ActionMove( element.Id ) ); + for( int i = 1; i <= rep; i++ ) { + if( (what & _DOWN) != 0) + m_s.Add( new ActionPtrDownUp( true, button ) ); + if( (what & _UP) != 0) + m_s.Add( new ActionPtrDownUp( false, button ) ); + } + } + + internal static void AddKeys(ActionSequence k_s, string keys, ref bool shift_down, ref bool ctrl_down, ref bool alt_down) { + foreach( char c in keys ) { + if (c == KEY_SHIFT) { + k_s.Add( new ActionKeyDownUp( ( shift_down ^= true ), c ) ); + } else if (c == KEY_CTRL) { + k_s.Add( new ActionKeyDownUp( ( ctrl_down ^= true ), c ) ); + } else if (c == KEY_ALT) { + k_s.Add( new ActionKeyDownUp( ( alt_down ^= true ), c ) ); + } else { + k_s.Add( new ActionKeyDownUp( true, c ) ); + k_s.Add( new ActionKeyDownUp( false, c ) ); + } } } @@ -79,7 +311,11 @@ public void Perform() { /// Time to wait in millisecond. /// Self public Actions Wait(int timems) { - _actions.Add(() => SysWaiter.Wait(timems)); + if( _action_sequences != null ) { + GetMouseSeq().Add( new ActionPause( timems ) ); + GetKeySeq().Add( new ActionPause() ); + } else + _actions.Add(() => SysWaiter.Wait(timems)); return this; } @@ -89,7 +325,10 @@ public Actions Wait(int timems) { /// The element to click. If None, clicks on current mouse position. /// Self public Actions Click(WebElement element = null) { - _actions.Add(() => act_click(element)); + if( _action_sequences != null ) + AddClickAction( element, MouseButton.Left, _DOWN | _UP ); + else + _actions.Add(() => act_click(element)); return this; } @@ -99,7 +338,10 @@ public Actions Click(WebElement element = null) { /// The element to mouse down. If None, clicks on current mouse position. /// Self public Actions ClickAndHold(WebElement element = null) { - _actions.Add(() => act_mouse_press(element)); + if( _action_sequences != null ) + AddClickAction( element, MouseButton.Left, _DOWN ); + else + _actions.Add(() => act_mouse_press(element)); return this; } @@ -109,7 +351,10 @@ public Actions ClickAndHold(WebElement element = null) { /// The element to context-click. If None, clicks on current mouse position. /// Self public Actions ClickContext(WebElement element = null) { - _actions.Add(() => act_click_context(element)); + if( _action_sequences != null ) + AddClickAction( element, MouseButton.Right, _DOWN | _UP ); + else + _actions.Add(() => act_click_context(element)); return this; } @@ -119,7 +364,10 @@ public Actions ClickContext(WebElement element = null) { /// The element to double-click. If None, clicks on current mouse position. /// Self public Actions ClickDouble(WebElement element = null) { - _actions.Add(() => act_click_double(element)); + if( _action_sequences != null ) + AddClickAction( element, MouseButton.Left, _DOWN | _UP, 2 ); + else + _actions.Add(() => act_click_double(element)); return this; } @@ -130,7 +378,12 @@ public Actions ClickDouble(WebElement element = null) { /// The element to mouse up. /// Self public Actions DragAndDrop(WebElement elementSource, WebElement elementTarget) { - _actions.Add(() => act_drag_drop(elementSource, elementTarget)); + if( _action_sequences != null ) { + AddClickAction( elementSource, MouseButton.Left, _DOWN ); + AddClickAction( elementTarget, MouseButton.Left, 0 ); + AddClickAction( null, MouseButton.Left, _UP ); + } else + _actions.Add(() => act_drag_drop(elementSource, elementTarget)); return this; } @@ -142,7 +395,13 @@ public Actions DragAndDrop(WebElement elementSource, WebElement elementTarget) { /// Y offset to move to. /// Self public Actions DragAndDropByOffset(WebElement element, int offset_x, int offset_y) { - _actions.Add(() => act_drag_drop_offset(element, offset_x, offset_y)); + if( _action_sequences != null ) { + AddClickAction( element, MouseButton.Left, _DOWN ); + ActionSequence m_s = GetMouseSeq(); + m_s.Add( new ActionMove( offset_x, offset_y ) ); + m_s.Add( new ActionPtrDownUp( false, MouseButton.Left ) ); + } else + _actions.Add(() => act_drag_drop_offset(element, offset_x, offset_y)); return this; } @@ -153,7 +412,10 @@ public Actions DragAndDropByOffset(WebElement element, int offset_x, int offset_ /// The element to Send keys. If None, sends a key to current focused element. /// Self public Actions KeyDown(string modifierKey, WebElement element = null) { - _actions.Add(() => act_key_down(modifierKey, element)); + if( _action_sequences != null ) + GetKeySeq().Add( new ActionKeyDownUp( true, modifierKey[0] ) ); + else + _actions.Add(() => act_key_down(modifierKey, element)); return this; } @@ -163,7 +425,10 @@ public Actions KeyDown(string modifierKey, WebElement element = null) { /// The modifier key to Send. Values are defined in Keys class. /// Self public Actions KeyUp(string modifierKey) { - _actions.Add(() => act_send_modifier_key(modifierKey)); + if( _action_sequences != null ) + GetKeySeq().Add( new ActionKeyDownUp( false, modifierKey[0] ) ); + else + _actions.Add(() => act_send_modifier_key(modifierKey)); return this; } @@ -174,7 +439,10 @@ public Actions KeyUp(string modifierKey) { /// Y offset to move to. /// Self public Actions MoveByOffset(int offset_x, int offset_y) { - _actions.Add(() => act_mouse_mouve(offset_x, offset_y)); + if( _action_sequences != null ) + GetMouseSeq().Add( new ActionMove( offset_x, offset_y ) ); + else + _actions.Add(() => act_mouse_mouve(offset_x, offset_y)); return this; } @@ -184,7 +452,10 @@ public Actions MoveByOffset(int offset_x, int offset_y) { /// The element to move to. /// Self public Actions MoveToElement(WebElement element) { - _actions.Add(() => act_mouse_mouve(element)); + if( _action_sequences != null ) + GetMouseSeq().Add( new ActionMove( element.Id ) ); + else + _actions.Add(() => act_mouse_mouve(element)); return this; } @@ -193,7 +464,10 @@ public Actions MoveToElement(WebElement element) { /// /// Self public Actions Release([MarshalAs(UnmanagedType.Struct)]WebElement element = null) { - _actions.Add(() => act_mouse_release(element)); + if( _action_sequences != null ) + AddClickAction( element, 0, _UP ); + else + _actions.Add(() => act_mouse_release(element)); return this; } @@ -204,11 +478,15 @@ public Actions Release([MarshalAs(UnmanagedType.Struct)]WebElement element = nul /// Element to Send keys. If None, Send keys to the current mouse position. /// Self public Actions SendKeys(string keys, WebElement element = null) { - _actions.Add(() => act_send_keys(keys, element)); + if( _action_sequences != null ) { + if( element != null ) + AddClickAction( element, MouseButton.Left, _DOWN | _UP ); + AddKeys(GetKeySeq(), keys, ref _isKeyShiftDown, ref _isKeyCtrlDown, ref _isKeyAltDown); + } else + _actions.Add(() => act_send_keys(keys, element)); return this; } - #region private support methods private void act_click(WebElement element) { diff --git a/Selenium/Common/Alert.cs b/Selenium/Common/Alert.cs index 101f040..3f5544e 100644 --- a/Selenium/Common/Alert.cs +++ b/Selenium/Common/Alert.cs @@ -26,7 +26,10 @@ public class Alert : ComInterfaces._Alert { internal static Alert SwitchToAlert(RemoteSession session, int timeout) { string text; try { - text = (string)session.Send(RequestMethod.GET, "/alert_text"); + if( WebDriver.LEGACY ) + text = (string)session.Send(RequestMethod.GET, "/alert_text"); + else + text = (string)session.Send(RequestMethod.GET, "/alert/text"); } catch (Errors.NoAlertPresentError) { if (timeout == 0) throw; @@ -67,14 +70,20 @@ public string Text { /// Dismisses the alert. /// public void Dismiss() { - _session.Send(RequestMethod.POST, "/dismiss_alert"); + if( WebDriver.LEGACY ) + _session.Send(RequestMethod.POST, "/dismiss_alert"); + else + _session.Send(RequestMethod.POST, "/alert/dismiss", new Dictionary()); } /// /// Accepts the alert. /// public void Accept() { - _session.Send(RequestMethod.POST, "/accept_alert"); + if( WebDriver.LEGACY ) + _session.Send(RequestMethod.POST, "/accept_alert"); + else + _session.Send(RequestMethod.POST, "/alert/accept", new Dictionary()); } /// @@ -82,7 +91,10 @@ public void Accept() { /// /// public void SendKeys(string keysToSend) { - _session.Send(RequestMethod.POST, "/alert_text", "text", keysToSend); + if( WebDriver.LEGACY ) + _session.Send(RequestMethod.POST, "/alert_text", "text", keysToSend); + else + _session.Send(RequestMethod.POST, "/alert/text", "text", keysToSend); } /// diff --git a/Selenium/Common/Keyboard.cs b/Selenium/Common/Keyboard.cs index ec735fa..555c0d5 100644 --- a/Selenium/Common/Keyboard.cs +++ b/Selenium/Common/Keyboard.cs @@ -42,10 +42,31 @@ public Keys Keys { /// Keys /// Self public Keyboard SendKeys(string keysOrModifiers, string keys = null) { - if (keys != null) - keysOrModifiers = string.Concat(keysOrModifiers, keys, keysOrModifiers); - _session.Send(RequestMethod.POST, "/keys", "value", new string[] { keysOrModifiers }); - //_session.Send(RequestMethod.POST, "/keys", "value", keysOrModifiers.ToCharArray() ); + if( WebDriver.LEGACY ) { + if (keys != null) + keysOrModifiers = string.Concat(keysOrModifiers, keys, keysOrModifiers); + _session.Send(RequestMethod.POST, "/keys", "value", new string[] { keysOrModifiers }); + //_session.Send(RequestMethod.POST, "/keys", "value", keysOrModifiers.ToCharArray() ); + } else { + bool shift_down = false, ctrl_down = false, alt_down = false; + Actions.ActionSequence k_s = new Actions.ActionSequence( Actions.SequenceType.Keyboard ); + Actions.AddKeys(k_s, keysOrModifiers, ref shift_down, ref ctrl_down, ref alt_down); + if( keys != null ) { + bool shift_down_ = false, ctrl_down_ = false, alt_down_ = false; + Actions.AddKeys(k_s, keys, ref shift_down_, ref ctrl_down_, ref alt_down_); + } +/* + if (shift_down) + k_s.Add( new Actions.ActionKeyDownUp( false, KEY_SHIFT ) ); + if (ctrl_down) + k_s.Add( new Actions.ActionKeyDownUp( false, KEY_CTRL ) ); + if (alt_down) + k_s.Add( new Actions.ActionKeyDownUp( false, KEY_ALT ) ); +*/ + Actions.SendActions( _session, k_s ); + if( shift_down || ctrl_down || alt_down ) + Actions.ReleaseActions( _session ); + } return this; } @@ -55,8 +76,17 @@ public Keyboard SendKeys(string keysOrModifiers, string keys = null) { /// The modifier key to Send. Values are defined in Keys class. /// Self public Keyboard KeyDown(string modifierKeys) { - check_keys_are_modifiers(modifierKeys); - _session.Send(RequestMethod.POST, "/keys", "value", new string[] { modifierKeys }); + if( WebDriver.LEGACY ) { + check_keys_are_modifiers(modifierKeys); + _session.Send(RequestMethod.POST, "/keys", "value", new string[] { modifierKeys }); + } else { + if( modifierKeys == null || modifierKeys.Length == 0 ) + throw new Errors.InvalideModifierKeyError(); + Actions.ActionSequence k_s = new Actions.ActionSequence( Actions.SequenceType.Keyboard ); + bool shift_down_ = false, ctrl_down_ = false, alt_down_ = false; + Actions.AddKeys(k_s, modifierKeys, ref shift_down_, ref ctrl_down_, ref alt_down_); + Actions.SendActions( _session, k_s ); + } return this; } @@ -66,8 +96,17 @@ public Keyboard KeyDown(string modifierKeys) { /// The modifier key to Send. Values are defined in Keys class. /// Self public Keyboard KeyUp(string modifierKeys) { - check_keys_are_modifiers(modifierKeys); - _session.Send(RequestMethod.POST, "/keys", "value", new string[] { modifierKeys }); + if( WebDriver.LEGACY ) { + check_keys_are_modifiers(modifierKeys); + _session.Send(RequestMethod.POST, "/keys", "value", new string[] { modifierKeys }); + } else { + if( modifierKeys == null || modifierKeys.Length == 0 ) + throw new Errors.InvalideModifierKeyError(); + Actions.ActionSequence k_s = new Actions.ActionSequence( Actions.SequenceType.Keyboard ); + bool shift_down_ = true, ctrl_down_ = true, alt_down_ = true; + Actions.AddKeys(k_s, modifierKeys, ref shift_down_, ref ctrl_down_, ref alt_down_); + Actions.SendActions( _session, k_s ); + } return this; } diff --git a/Selenium/Common/Mouse.cs b/Selenium/Common/Mouse.cs index 40fb8bd..2e2cf6f 100644 --- a/Selenium/Common/Mouse.cs +++ b/Selenium/Common/Mouse.cs @@ -2,7 +2,10 @@ using Selenium; using System.ComponentModel; using System.Runtime.InteropServices; - +using Selenium.Serializer; +using System; +using System.Collections.Generic; + namespace Selenium { /// @@ -36,15 +39,24 @@ public Mouse moveTo(WebElement element) { /// {number} X offset to move to, relative to the top-left corner of the element. If not specified, the mouse will move to the middle of the element. /// {number} Y offset to move to, relative to the top-left corner of the element. If not specified, the mouse will move to the middle of the element. public Mouse MoveTo(WebElement element, int xoffset = 0, int yoffset = 0) { - var data = new Dictionary(); - if (element != null) - data.Add("element", element.Id); + if( WebDriver.LEGACY ) { + var data = new Dictionary(); + if (element != null) + data.Add("element", element.Id); - if (xoffset != 0 || yoffset != 0) { - data.Add("xoffset", xoffset); - data.Add("yoffset", yoffset); + if (xoffset != 0 || yoffset != 0) { + data.Add("xoffset", xoffset); + data.Add("yoffset", yoffset); + } + _session.Send(RequestMethod.POST, "/moveto", data); + } else { + Actions.ActionSequence m_s = new Actions.ActionSequence( Actions.SequenceType.Mouse ); + if (element != null) + m_s.Add( new Actions.ActionMove( element.Id, 0, 0 ) ); + else if (xoffset != 0 || yoffset != 0) + m_s.Add( new Actions.ActionMove( xoffset, yoffset ) ); + Actions.SendActions( _session, m_s ); } - _session.Send(RequestMethod.POST, "/moveto", data); return this; } @@ -53,7 +65,14 @@ public Mouse MoveTo(WebElement element, int xoffset = 0, int yoffset = 0) { /// /// {number} Which button, enum: {LEFT = 0, MIDDLE = 1 , RIGHT = 2}. Defaults to the left mouse button if not specified. public Mouse Click(MouseButton button = MouseButton.Left) { - _session.Send(RequestMethod.POST, "/click", "button", (int)button); + if( WebDriver.LEGACY ) { + _session.Send(RequestMethod.POST, "/click", "button", (int)button); + } else { + Actions.ActionSequence m_s = new Actions.ActionSequence( Actions.SequenceType.Mouse ); + m_s.Add( new Actions.ActionPtrDownUp( true, button ) ); + m_s.Add( new Actions.ActionPtrDownUp( false, button ) ); + Actions.SendActions( _session, m_s ); + } return this; } @@ -62,7 +81,13 @@ public Mouse Click(MouseButton button = MouseButton.Left) { /// /// {number} Which button, enum: {LEFT = 0, MIDDLE = 1 , RIGHT = 2}. Defaults to the left mouse button if not specified. public Mouse ClickAndHold(MouseButton button = MouseButton.Left) { - _session.Send(RequestMethod.POST, "/buttondown", "button", (int)button); + if( WebDriver.LEGACY ) { + _session.Send(RequestMethod.POST, "/buttondown", "button", (int)button); + } else { + Actions.ActionSequence m_s = new Actions.ActionSequence( Actions.SequenceType.Mouse ); + m_s.Add( new Actions.ActionPtrDownUp( true, button ) ); + Actions.SendActions( _session, m_s ); + } return this; } @@ -71,7 +96,13 @@ public Mouse ClickAndHold(MouseButton button = MouseButton.Left) { /// /// {number} Which button, enum: {LEFT = 0, MIDDLE = 1 , RIGHT = 2}. Defaults to the left mouse button if not specified. public Mouse Release(MouseButton button = MouseButton.Left) { - _session.Send(RequestMethod.POST, "/buttonup", "button", (int)button); + if( WebDriver.LEGACY ) { + _session.Send(RequestMethod.POST, "/buttonup", "button", (int)button); + } else { + Actions.ActionSequence m_s = new Actions.ActionSequence( Actions.SequenceType.Mouse ); + m_s.Add( new Actions.ActionPtrDownUp( false, button ) ); + Actions.SendActions( _session, m_s ); + } return this; } @@ -79,7 +110,16 @@ public Mouse Release(MouseButton button = MouseButton.Left) { /// Double-clicks at the current mouse coordinates (set by moveto). /// public Mouse ClickDouble() { - _session.Send(RequestMethod.POST, "/doubleclick"); + if( WebDriver.LEGACY ) { + _session.Send(RequestMethod.POST, "/doubleclick"); + } else { + Actions.ActionSequence m_s = new Actions.ActionSequence( Actions.SequenceType.Mouse ); + m_s.Add( new Actions.ActionPtrDownUp( true, MouseButton.Left ) ); + m_s.Add( new Actions.ActionPtrDownUp( false, MouseButton.Left ) ); + m_s.Add( new Actions.ActionPtrDownUp( true, MouseButton.Left ) ); + m_s.Add( new Actions.ActionPtrDownUp( false, MouseButton.Left ) ); + Actions.SendActions( _session, m_s ); + } return this; } diff --git a/Selenium/Common/SearchContext.cs b/Selenium/Common/SearchContext.cs index fd4d793..12efaf3 100644 --- a/Selenium/Common/SearchContext.cs +++ b/Selenium/Common/SearchContext.cs @@ -112,7 +112,11 @@ public WebElement FindElementByXPath(string xpath, int timeout = -1, bool raise /// Optional - Raise an exception after the timeout when true /// or null public WebElement FindElementById(string id, int timeout = -1, bool raise = true) { - return this.FindElementBy(Strategy.Id, id, timeout, raise); + if( WebDriver.LEGACY ) + return this.FindElementBy(Strategy.Id, id, timeout, raise); + else { + return this.FindElementBy(Strategy.Css, "#" + id, timeout, raise); + } } /// @@ -297,6 +301,8 @@ private WebElement FindFirstElement(Strategy strategy, string value, int timeout string relativeUri = this.uri + "/element"; Dictionary element; try { + if( !WebDriver.LEGACY ) + TranslateStrategy( ref strategy, ref value ); string method = By.FormatStrategy(strategy); element = (Dictionary)session.Send(RequestMethod.POST, relativeUri, "using", method, "value", value); } catch (Errors.NoSuchElementError) { @@ -317,6 +323,24 @@ private WebElement FindFirstElement(Strategy strategy, string value, int timeout return new WebElement(session, element); } + private bool TranslateStrategy( ref Strategy strategy, ref string value ) { + switch( strategy ) { + case Strategy.Id: + strategy = Strategy.Css; + value = "#" + value; + return true; + case Strategy.Class: + strategy = Strategy.Css; + value = "." + value; + return true; + case Strategy.Name: + strategy = Strategy.XPath; + value = ( this is WebElement ? "." : "" ) + "//*[@name='" + value + "']"; + return true; + } + return false; + } + private WebElement FindAnyElement(By byAny, int timeout) { RemoteSession session = this.session; string relativeUri = this.uri + "/element"; @@ -327,8 +351,11 @@ private WebElement FindAnyElement(By byAny, int timeout) { if (by == null) break; try { - string method = By.FormatStrategy(by.Strategy); + Strategy strategy = by.Strategy; string value = by.Value.ToString(); + if( !WebDriver.LEGACY ) + TranslateStrategy( ref strategy, ref value ); + string method = By.FormatStrategy(strategy); element = (Dictionary)session.Send(RequestMethod.POST, relativeUri, "using", method, "value", value); return new WebElement(session, element); } catch (Errors.NoSuchElementError) { } @@ -346,6 +373,8 @@ private WebElements FindAllElements(Strategy strategy, string value, int minimum RemoteSession session = this.session; string uri = this.uri + "/elements"; try { + if( !WebDriver.LEGACY ) + TranslateStrategy( ref strategy, ref value ); var method = By.FormatStrategy(strategy); List elements = session.SendUntil(timeout, () => (List)session.Send(RequestMethod.POST, uri, "using", method, "value", value), @@ -366,8 +395,11 @@ private WebElements FindAnyElements(By byAny, int minimum, int timeout) { foreach (By by in (By[])byAny.Value) { if (by == null) break; - var method = By.FormatStrategy(by.Strategy); + Strategy strategy = by.Strategy; var value = (string)by.Value; + if( !WebDriver.LEGACY ) + TranslateStrategy( ref strategy, ref value ); + var method = By.FormatStrategy(strategy); List elements = (List)session.Send(RequestMethod.POST, uri, "using", method, "value", value); webelements.Add(session, elements); } @@ -385,6 +417,8 @@ private WebElements FindAnyElements(By byAny, int minimum, int timeout) { private void WaitElementNotPresent(Strategy strategy, string value, int timeout) { RemoteSession session = this.session; string uri = this.uri + "/element"; + if( !WebDriver.LEGACY ) + TranslateStrategy( ref strategy, ref value ); string method = By.FormatStrategy(strategy); DateTime endTime = session.GetEndTime(timeout); try { @@ -409,8 +443,11 @@ private void WaitAnyElementNotPresent(By byAny, int timeout) { if (by == null) break; try { - string method = By.FormatStrategy(by.Strategy); + Strategy strategy = by.Strategy; string value = by.Value.ToString(); + if( !WebDriver.LEGACY ) + TranslateStrategy( ref strategy, ref value ); + string method = By.FormatStrategy(strategy); session.Send(RequestMethod.POST, uri, "using", method, "value", value); while (true) { if (DateTime.UtcNow > endTime) diff --git a/Selenium/Common/SelectElement.cs b/Selenium/Common/SelectElement.cs index 82958c3..192fe1e 100644 --- a/Selenium/Common/SelectElement.cs +++ b/Selenium/Common/SelectElement.cs @@ -155,7 +155,8 @@ public void SelectByIndex(int index) { string match = index.ToString(CultureInfo.InvariantCulture); bool matched = false; foreach (WebElement option in this.Options) { - if (match.Equals(option.Attribute("index"))) { + var value = WebDriver.LEGACY ? option.Attribute("index") : option.Property("index"); + if (value != null && match.Equals( value.ToString() )) { SetSelected(option); if (!this.IsMultiple) return; diff --git a/Selenium/Common/WebElement.cs b/Selenium/Common/WebElement.cs index 7f6a483..29c699e 100644 --- a/Selenium/Common/WebElement.cs +++ b/Selenium/Common/WebElement.cs @@ -18,22 +18,24 @@ namespace Selenium { [Description("Defines the interface through which the user controls elements on the page.")] [ComVisible(true), ClassInterface(ClassInterfaceType.None)] public class WebElement : SearchContext, ComInterfaces._WebElement, IJsonObject { + public const string ELEMENT = "ELEMENT"; + public const string IDENTIFIER = "element-6066-11e4-a52e-4f735466cecf"; /// /// Returns the element with focus, or BODY if nothing has focus. /// internal static WebElement GetActiveWebElement(RemoteSession session) { - var result = (Dictionary)session.Send(RequestMethod.POST, "/element/active"); + RequestMethod method = WebDriver.LEGACY ? RequestMethod.POST : RequestMethod.GET; + var result = (Dictionary)session.Send(method, "/element/active"); return new WebElement(session, result); } internal static bool TryParse(Dictionary dict, out string id) { - return dict.TryGetValue("ELEMENT", out id); - //TODO: Change the key once implemented on the server side - //return dict.TryGetValue("ELEMENT-6066-11e4-a52e-4f735466cecf", out id); + if( WebDriver.LEGACY && dict.TryGetValue(ELEMENT, out id) ) + return true; + return dict.TryGetValue(IDENTIFIER, out id); } - internal readonly RemoteSession _session; internal readonly string Id; @@ -65,9 +67,9 @@ internal override string uri { /// public Dictionary SerializeJson() { var dict = new Dictionary(); - dict.Add("ELEMENT", this.Id); - //TODO: Change the key once implemented on the server side - //dict.Add("ELEMENT-6066-11e4-a52e-4f735466cecf", id); + if( !WebDriver.LEGACY ) + dict.Add(IDENTIFIER, this.Id); // Selenium.NET sends both. Let's do the same. + dict.Add( ELEMENT, this.Id); return dict; } @@ -125,8 +127,13 @@ public bool IsSelected { /// /// Point public Point Location() { - var dict = (Dictionary)Send(RequestMethod.GET, "/location"); - return new Point(dict); + if( WebDriver.LEGACY ) { + var dict = (Dictionary)Send(RequestMethod.GET, "/location"); + return new Point(dict); + } else { + var dict = (Dictionary)Send(RequestMethod.GET, "/rect"); + return new Point(dict); + } } /// @@ -141,8 +148,13 @@ public Point LocationInView() { /// Returns the size of the element /// public Size Size() { - var dict = (Dictionary)Send(RequestMethod.GET, "/size"); - return new Size(dict); + if( WebDriver.LEGACY ) { + var dict = (Dictionary)Send(RequestMethod.GET, "/size"); + return new Size(dict); + } else { + var dict = (Dictionary)Send(RequestMethod.GET, "/rect"); + return new Size(dict); + } } /// @@ -188,6 +200,16 @@ public object Attribute(string attribute) { return value; } + /// + /// Gets the property value. + /// + /// Property name + /// Property + public object Property(string property) { + var value = Send(RequestMethod.GET, "/property/" + property); + return value; + } + /// /// Returns the value of a CSS property /// @@ -203,8 +225,11 @@ public object CssValue(string property) { /// /// public object Value() { - var value = Send(RequestMethod.GET, "/attribute/value"); - return value; + if( WebDriver.LEGACY ) { + var value = Send(RequestMethod.GET, "/attribute/value"); + return value; + } + return Send(RequestMethod.GET, "/property/value"); } /// @@ -259,7 +284,7 @@ public bool Equals(WebElement other) { /// Clears the text if it’s a text entry element. /// public WebElement Clear() { - Send(RequestMethod.POST, "/clear"); + Send(RequestMethod.POST, "/clear", "id", Id); return this; } @@ -371,7 +396,7 @@ public WebElement SendKeys(string keysOrModifier, string keys = null) { if (!_session.IsLocal && text.IndexOf(":/") != -1 && File.Exists(text)) { text = _session.UploadFile(text); } - Send(RequestMethod.POST, "/value", "value", new string[] { text }); + Send(RequestMethod.POST, "/value", "value", new string[] { text }, "text", text); return this; } @@ -387,11 +412,22 @@ public void Submit() { /// /// Optional - Modifier Keys to press public void Click(string keys = null) { - if (keys != null) - _session.keyboard.SendKeys(keys); - Send(RequestMethod.POST, "/click"); - if (keys != null) + if (keys != null) { + if( WebDriver.LEGACY ) + _session.keyboard.SendKeys(keys); + else { + new Actions( _session ) + .KeyDown(keys, this) + .Click(this) + .KeyUp(keys) + .Perform(); + return; + } + } + Send(RequestMethod.POST, "/click", "id", Id ); + if (keys != null) { _session.keyboard.SendKeys(keys); + } } /// diff --git a/Selenium/Common/Window.cs b/Selenium/Common/Window.cs index 2d69d87..2c7164d 100644 --- a/Selenium/Common/Window.cs +++ b/Selenium/Common/Window.cs @@ -23,7 +23,10 @@ namespace Selenium { public class Window : ComInterfaces._Window { internal static string GetWindowHandle(RemoteSession session) { - return (string)session.Send(RequestMethod.GET, "/window_handle"); + if( WebDriver.LEGACY ) + return (string)session.Send(RequestMethod.GET, "/window_handle"); + else + return (string)session.Send(RequestMethod.GET, "/window"); } private readonly RemoteSession _session; @@ -142,7 +145,8 @@ public string Title { /// /// When setting this property, it should act as the JavaScript window.moveTo() method. public Point Position() { - var result = (Dictionary)_session.Send(RequestMethod.GET, uri() + "/position"); + string endpoint = WebDriver.LEGACY ? uri() + "/position" : "/window/rect"; + var result = (Dictionary)_session.Send(RequestMethod.GET, endpoint); return new Point(result); } @@ -152,7 +156,8 @@ public Point Position() { /// X /// Y public void SetPosition(int x, int y) { - _session.Send(RequestMethod.POST, uri() + "/position", "x", x, "y", y); + string endpoint = WebDriver.LEGACY ? uri() + "/position" : "/window/rect"; + _session.Send(RequestMethod.POST, endpoint, "x", x, "y", y); } /// @@ -160,7 +165,8 @@ public void SetPosition(int x, int y) { /// /// When setting this property, it should act as the JavaScript window.resizeTo() method. public Size Size() { - var dict = (Dictionary)_session.Send(RequestMethod.GET, uri() + "/size"); + string endpoint = WebDriver.LEGACY ? uri() + "/size" : "/window/rect"; + var dict = (Dictionary)_session.Send(RequestMethod.GET, endpoint); return new Size(dict); } @@ -170,21 +176,24 @@ public Size Size() { /// /// public void SetSize(int width, int height) { - _session.Send(RequestMethod.POST, uri() + "/size", "width", width, "height", height); + string endpoint = WebDriver.LEGACY ? uri() + "/size" : "/window/rect"; + _session.Send(RequestMethod.POST, endpoint, "width", width, "height", height); } /// /// Maximizes the current window if it is not already maximized. /// public void Maximize() { - _session.Send(RequestMethod.POST, uri() + "/maximize"); + string endpoint = WebDriver.LEGACY ? uri() + "/maximize" : "/window/maximize"; + _session.Send(RequestMethod.POST, endpoint, new Dictionary()); } /// /// Set the current window full screen. /// public void FullScreen() { - _session.Send(RequestMethod.POST, uri() + "/fullscreen"); + string endpoint = WebDriver.LEGACY ? uri() + "/fullscreen" : "/window/fullscreen"; + _session.Send(RequestMethod.POST, endpoint, new Dictionary()); } /// diff --git a/Selenium/Core/FrameContext.cs b/Selenium/Core/FrameContext.cs index d4da801..5ad10d6 100644 --- a/Selenium/Core/FrameContext.cs +++ b/Selenium/Core/FrameContext.cs @@ -25,7 +25,6 @@ internal FrameContext(RemoteSession session) { public void SwitchToFrame(object identifier, int timeout) { if (identifier == null) throw new Errors.ArgumentError("Invalid type for argument identifier"); - var element = identifier as WebElement; if (element != null) { identifier = element.SerializeJson(); @@ -54,7 +53,7 @@ public void SwitchToFrame(object identifier, int timeout) { /// Select the parent frame of the currently selected frame. /// public void SwitchToParentFrame() { - _session.Send(RequestMethod.POST, "/frame/parent"); + _session.Send(RequestMethod.POST, "/frame/parent", new Dictionary()); } /// diff --git a/Selenium/Core/JavascriptContext.cs b/Selenium/Core/JavascriptContext.cs index 48d94e9..e7213a4 100644 --- a/Selenium/Core/JavascriptContext.cs +++ b/Selenium/Core/JavascriptContext.cs @@ -23,7 +23,9 @@ internal JavascriptContext(RemoteSession session) { /// Optional - Converts web elements to objects /// public object Execute(string script, object arguments = null, bool unbox = true) { - object result = this.session.Send(RequestMethod.POST, "/execute", "script", script, "args", arguments); + string endpoint = WebDriver.LEGACY ? "/execute" : "/execute/sync"; + // The '/sync' suffix is required by the spec + object result = this.session.Send(RequestMethod.POST, endpoint, "script", script, "args", arguments); if (unbox) return Unbox(result); return result; @@ -43,8 +45,8 @@ public object ExecuteAsync(string script, object arguments = null, bool unbox = session.timeouts.Script = timeout; string script_ex = "var callback=arguments[--arguments.length];" + script; - - object result = this.session.Send(RequestMethod.POST, "/execute_async", "script", script_ex, "args", arguments); + string endpoint = WebDriver.LEGACY ? "/execute_async" : "/execute/async"; + object result = this.session.Send(RequestMethod.POST, endpoint, "script", script_ex, "args", arguments); if (unbox) return Unbox(result); return result; @@ -70,8 +72,8 @@ string script_ex + " if(res) callback(res); else setTimeout(tst, 60);" + "};" + "tst();"; - - object result = this.session.Send(RequestMethod.POST, "/execute_async", "script", script_ex, "args", arguments); + string endpoint = WebDriver.LEGACY ? "/execute_async" : "/execute/async"; + object result = this.session.Send(RequestMethod.POST, endpoint, "script", script_ex, "args", arguments); return Unbox(result); } diff --git a/Selenium/Core/RemoteServer.cs b/Selenium/Core/RemoteServer.cs index 47c84ea..5368bf8 100644 --- a/Selenium/Core/RemoteServer.cs +++ b/Selenium/Core/RemoteServer.cs @@ -60,7 +60,8 @@ public List GetSessions() { } public void ShutDown() { - Send(RequestMethod.GET, @"/shutdown", null); + if( WebDriver.LEGACY ) + Send(RequestMethod.GET, @"/shutdown", null); } /// @@ -127,6 +128,12 @@ protected Dictionary SendRequest(RequestMethod method, string uri, JSON data) { response = (HttpWebResponse)request.EndGetResponse(asyncResult); responseDict = GetHttpWebResponseContent(response); } catch (WebException ex) { +#if DEBUG + Console.WriteLine( ex.Message ); + Console.WriteLine( uri ); + if( data != null ) + Console.WriteLine( data.ToString() ); +#endif if (ex.Status == WebExceptionStatus.RequestCanceled) { throw new Errors.KeyboardInterruptError(); } else if (ex.Status == WebExceptionStatus.Timeout) { @@ -135,6 +142,7 @@ protected Dictionary SendRequest(RequestMethod method, string uri, JSON data) { try { responseDict = GetHttpWebResponseContent(response); } catch (Exception ex2) { + Console.WriteLine( ex2.Message ); throw new SeleniumException(ex2); } } else { @@ -148,21 +156,44 @@ protected Dictionary SendRequest(RequestMethod method, string uri, JSON data) { response.Close(); } - if (responseDict != null) { - //Evaluate the status and error - int statusCode = (int)responseDict["status"]; - if (statusCode != 0) { + if (responseDict != null ) { + if(responseDict.ContainsKey("status")) { + //Evaluate the status and error + int statusCode = (int)responseDict["status"]; + if (statusCode != 0) { + object errorObject = responseDict["value"]; + Dictionary errorAsDict = errorObject as Dictionary; + string errorMessage; + if (errorAsDict != null) { + errorMessage = errorAsDict["message"] as string; + } else { + errorMessage = errorObject as string; + } + SeleniumError error = Errors.WebRequestError.Select(statusCode, errorMessage); + error.ResponseData = responseDict; + throw error; + } + } + if( !WebDriver.LEGACY && response.StatusCode != HttpStatusCode.OK && responseDict.ContainsKey("value")) { object errorObject = responseDict["value"]; - Dictionary errorAsDict = errorObject as Dictionary; - string errorMessage; - if (errorAsDict != null) { - errorMessage = errorAsDict["message"] as string; - } else { - errorMessage = errorObject as string; + if( errorObject is Dictionary ) { + Dictionary errorAsDict = errorObject as Dictionary; + if (errorAsDict != null) { + string errorMessage = errorAsDict["message"] as string; + string errorStr = errorAsDict["error"] as string; +#if DEBUG + Console.WriteLine( "!!! Error: " + errorStr ); + Console.WriteLine( errorMessage ); +#endif + if( errorStr == "no such element" ) + throw new Errors.NoSuchElementError(errorMessage); + if( errorStr == "no such alert" ) + throw new Errors.NoAlertPresentError(errorMessage); + if( errorStr == "unexpected alert open" ) + throw new Errors.UnexpectedAlertOpenError(errorMessage); + throw new Errors.WebRequestError(errorMessage); + } } - SeleniumError error = Errors.WebRequestError.Select(statusCode, errorMessage); - error.ResponseData = responseDict; - throw error; } } return responseDict; diff --git a/Selenium/Core/RemoteSession.cs b/Selenium/Core/RemoteSession.cs index 6abd3ef..b9028e9 100644 --- a/Selenium/Core/RemoteSession.cs +++ b/Selenium/Core/RemoteSession.cs @@ -52,9 +52,15 @@ public void Start(Dictionary desired_capabilities, Dictionary requiredCapabiliti var response = (Dictionary)server.Send(RequestMethod.POST, "/session", param); try { - _id = (string)response["sessionId"]; + if( response.ContainsKey("sessionId") ) { + _id = (string)response["sessionId"]; + this.capabilities = (Dictionary)response["value"]; + } else { + Dictionary value = (Dictionary)response["value"]; + _id = (string)value["sessionId"]; + this.capabilities = (Dictionary)value["capabilities"]; + } _uri = "/session/" + _id; - this.capabilities = (Dictionary)response["value"]; } catch (Errors.KeyNotFoundError ex) { throw new DeserializeException(typeof(RemoteSession), ex); } diff --git a/Selenium/Core/WindowContext.cs b/Selenium/Core/WindowContext.cs index 7237e45..28bdb9b 100644 --- a/Selenium/Core/WindowContext.cs +++ b/Selenium/Core/WindowContext.cs @@ -8,18 +8,36 @@ class WindowContext { #region Static internal static List GetWindowsHandles(RemoteSession session) { - return (List)session.Send(RequestMethod.GET, "/window_handles"); + string endpoint = WebDriver.LEGACY ? "/window_handles" : "/window/handles"; + return (List)session.Send(RequestMethod.GET, endpoint); } internal static string GetCurrentTitle(RemoteSession session) { return (string)session.Send(RequestMethod.GET, "/title"); } - internal static string ActivateWindow(RemoteSession session, string name) { + internal static string ActivateWindow(RemoteSession session, string handle) { + string param = WebDriver.LEGACY ? "name" : "handle"; + session.Send(RequestMethod.POST, "/window", param, handle); + string endpoint = WebDriver.LEGACY ? "/window_handle" : "/window"; + return (string)session.Send(RequestMethod.GET, endpoint); + } + + internal static string ActivateWindowByName(RemoteSession session, string name) { + if( !WebDriver.LEGACY ) + throw new Errors.NotImplementedError(); session.Send(RequestMethod.POST, "/window", "name", name); return (string)session.Send(RequestMethod.GET, "/window_handle"); } + internal static string GetCurrentName(RemoteSession session) { + string script = "return window.name;"; + object result = session.javascript.Execute(script, new object[0], true); + if( result != null && result is string ) + return (string)result; + return null; + } + #endregion private readonly RemoteSession _session; @@ -124,17 +142,33 @@ public Window SwitchToWindowByName(string name, int timeout = -1) { var endTime = _session.GetEndTime(timeout); while (true) { try { - string handle = WindowContext.ActivateWindow(_session, name); - _previousWindow = _currentWindow; - foreach (Window win in _cachedWindows) { - if (win.Handle == handle) { - _currentWindow = win; - return _currentWindow; + if( WebDriver.LEGACY ) { + string handle = WindowContext.ActivateWindow(_session, name); + _previousWindow = _currentWindow; + foreach (Window win in _cachedWindows) { + if (win.Handle == handle) { + _currentWindow = win; + return _currentWindow; + } + } + _currentWindow = new Window(_session, this, handle); + _cachedWindows.Add(_currentWindow); + return _currentWindow; + } else { + List windows, handles; + this.ListWindows(out windows, out handles); + foreach (Window win in windows) { + if (_currentWindow != null && win.Handle == _currentWindow.Handle) + continue; + WindowContext.ActivateWindow(_session, win.Handle); + string winName = GetCurrentName(_session); + if (winName == name) { + _previousWindow = _currentWindow; + _currentWindow = win; + return win; + } } } - _currentWindow = new Window(_session, this, handle); - _cachedWindows.Add(_currentWindow); - return _currentWindow; } catch (Errors.NoSuchWindowError) { } if (DateTime.UtcNow > endTime) diff --git a/Selenium/Drivers/GeckoDriver.cs b/Selenium/Drivers/GeckoDriver.cs new file mode 100755 index 0000000..cfc7452 --- /dev/null +++ b/Selenium/Drivers/GeckoDriver.cs @@ -0,0 +1,83 @@ +using Microsoft.Win32; +using Selenium.Core; +using System; +using System.Net; +using System.Runtime.InteropServices; +using System.Text; + +namespace Selenium { + + /// + /// Web driver for Gecko driver + /// + /// + /// + /// + /// VBScript: + /// + /// Class Script + /// Dim driver + /// + /// Sub Class_Initialize + /// Set driver = CreateObject("Selenium.GeckoDriver") + /// driver.Get "http://www.google.com" + /// ... + /// End Sub + /// + /// Sub Class_Terminate + /// driver.Quit + /// End Sub + /// End Class + /// + /// Set s = New Script + /// + /// + /// VBA: + /// + /// Public Sub Script() + /// Dim driver As New GeckoDriver + /// driver.Get "http://www.mywebsite.com" + /// ... + /// driver.Quit + /// End Sub + /// + /// + /// + [ProgId("Selenium.GeckoDriver")] + [Guid("0277FC34-FD1B-4616-BB19-19E724A03AE2")] + [ComVisible(true), ClassInterface(ClassInterfaceType.None)] + public class GeckoDriver : WebDriver, ComInterfaces._WebDriver { + + const string BROWSER_NAME = "Gecko"; + + /// + /// + /// + public GeckoDriver() + : base(BROWSER_NAME) { + WebDriver.LEGACY = false; // assuming only one driver is used at a time + } + + internal static IDriverService StartService(WebDriver wd) { + ExtendCapabilities(wd, false); + + var svc = new DriverService(); + svc.AddArgument("--host=127.0.0.1"); + svc.AddArgument("--port=" + svc.IPEndPoint.Port.ToString()); + svc.Start("geckodriver.exe", true); + return svc; + } + + internal static void ExtendCapabilities(WebDriver wd, bool remote) { + WebDriver.LEGACY = false; + Capabilities capa = wd.Capabilities; + + Dictionary opts; + if (!capa.TryGetValue("GeckoOptions", out opts)) + capa["GeckoOptions"] = opts = new Dictionary(); + + } + + } + +} diff --git a/Selenium/Properties/AssemblyInfo.cs b/Selenium/Properties/AssemblyInfo.cs index e36a124..6489ecc 100644 --- a/Selenium/Properties/AssemblyInfo.cs +++ b/Selenium/Properties/AssemblyInfo.cs @@ -36,8 +36,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.9.0")] -[assembly: AssemblyFileVersion("2.0.9.0")] +[assembly: AssemblyVersion("3.0.0.0")] +[assembly: AssemblyFileVersion("3.0.0.0")] [assembly: TypeLibVersion(2, 0)] [assembly: NeutralResourcesLanguage("en-US")] diff --git a/Selenium/Selenium.csproj b/Selenium/Selenium.csproj index dbcfdb6..92a2421 100644 --- a/Selenium/Selenium.csproj +++ b/Selenium/Selenium.csproj @@ -96,6 +96,9 @@ prompt MinimumRecommendedRules.ruleset + + OnBuildSuccess + @@ -110,6 +113,7 @@ + @@ -274,12 +278,10 @@ - xcopy /D /Y "$(ProjectDir)\..\References\firefoxdriver.xpi" . -xcopy /D /Y "$(ProjectDir)\..\References\chromedriver.exe" . -xcopy /D /Y "$(ProjectDir)\..\References\operadriver.exe" . -xcopy /D /Y "$(ProjectDir)\..\References\iedriver.exe" . -xcopy /D /Y "$(ProjectDir)\..\References\iedriver64.exe" . -xcopy /D /Y "$(ProjectDir)\..\References\phantomjs.exe" . + xcopy /D /Y "$(ProjectDir)\..\References\firefoxdriver.xpi" . +xcopy /D /Y "$(ProjectDir)\..\References\chromedriver.exe" . +xcopy /D /Y "$(ProjectDir)\..\References\edgedriver.exe" . + + --> \ No newline at end of file diff --git a/VbsConsole/app.config b/VbsConsole/app.config new file mode 100755 index 0000000..57c0f2c --- /dev/null +++ b/VbsConsole/app.config @@ -0,0 +1,3 @@ + + + From 6719c637f605842e795650df215e9f0fcb7f067d Mon Sep 17 00:00:00 2001 From: zc2 Date: Tue, 1 Mar 2022 11:58:29 -0500 Subject: [PATCH 09/16] Allows GeckoDriver switch to a window by handle --- .gitignore | 2 ++ Selenium.Tests/Program.cs | 2 +- Selenium.Tests/TS_Windows.cs | 5 +++++ Selenium/Core/WindowContext.cs | 31 ++++++++++++++++++++++--------- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 3eff3b3..8ab9e77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ .vs References +packages bin obj Xlbin +*.msi *.exe *.log *.bak diff --git a/Selenium.Tests/Program.cs b/Selenium.Tests/Program.cs index ac10f43..4adec84 100644 --- a/Selenium.Tests/Program.cs +++ b/Selenium.Tests/Program.cs @@ -9,7 +9,7 @@ static void Main(string[] args) { Assembly.GetExecutingAssembly().Location, "/run:Selenium.Tests" // to test an individual fixture, uncomment the following line and add ', Category = "InFocus"' to the fixture -// ,"/include:InFocus" + //,"/include:InFocus" }; int returnCode = NUnit.ConsoleRunner.Runner.Main(my_args); diff --git a/Selenium.Tests/TS_Windows.cs b/Selenium.Tests/TS_Windows.cs index e323fac..316ab97 100644 --- a/Selenium.Tests/TS_Windows.cs +++ b/Selenium.Tests/TS_Windows.cs @@ -51,6 +51,8 @@ public void ShouldSwitchToWindows() { driver.Get("/win1.html"); A.AreEqual("Window1", driver.Title); + var win1_handle = driver.Window.Handle; + driver.FindElementByLinkText("Window2").Click(); driver.SwitchToWindowByTitle("Window2"); A.AreEqual("Window2", driver.Title); @@ -64,6 +66,9 @@ public void ShouldSwitchToWindows() { driver.SwitchToWindowByName("win3"); A.AreEqual("Window3", driver.Title); + + driver.SwitchToWindowByName(win1_handle); + A.AreEqual("Window1", driver.Title); } [TestCase] diff --git a/Selenium/Core/WindowContext.cs b/Selenium/Core/WindowContext.cs index 28bdb9b..14503f9 100644 --- a/Selenium/Core/WindowContext.cs +++ b/Selenium/Core/WindowContext.cs @@ -133,6 +133,16 @@ public Window SwitchToWindowByTitle(string title, int timeout = -1) { } } + private Window MayBeAddWindowToCache( string handle ) { + foreach (Window win in _cachedWindows) { + if (win.Handle == handle) + return win; + } + Window new_win = new Window(_session, this, handle); + _cachedWindows.Add(new_win); + return new_win; + } + /// /// Change focus to another window. /// @@ -142,19 +152,22 @@ public Window SwitchToWindowByName(string name, int timeout = -1) { var endTime = _session.GetEndTime(timeout); while (true) { try { + string handle = null; if( WebDriver.LEGACY ) { - string handle = WindowContext.ActivateWindow(_session, name); + handle = WindowContext.ActivateWindow(_session, name); _previousWindow = _currentWindow; - foreach (Window win in _cachedWindows) { - if (win.Handle == handle) { - _currentWindow = win; - return _currentWindow; - } - } - _currentWindow = new Window(_session, this, handle); - _cachedWindows.Add(_currentWindow); + _currentWindow = MayBeAddWindowToCache( handle ); return _currentWindow; } else { + // assume first that the name is actually the handle + try { + handle = WindowContext.ActivateWindow(_session, name); + } catch( Selenium.Errors.WebRequestError ) {} + if (handle == name) { + _previousWindow = _currentWindow; + _currentWindow = MayBeAddWindowToCache( handle ); + return _currentWindow; + } List windows, handles; this.ListWindows(out windows, out handles); foreach (Window win in windows) { From af92e1fcbc3a8c4f7a1cf21e267e9555515cb270 Mon Sep 17 00:00:00 2001 From: zc2 Date: Tue, 22 Mar 2022 17:07:47 -0400 Subject: [PATCH 10/16] Edge driver now can be started with a custom profile directory --- Selenium/Drivers/ChromeDriver.cs | 2 +- Selenium/Drivers/EdgeDriver.cs | 21 ++++++++++++++++++++- Selenium/WebDriver.cs | 3 ++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Selenium/Drivers/ChromeDriver.cs b/Selenium/Drivers/ChromeDriver.cs index 052ea37..79f34f9 100644 --- a/Selenium/Drivers/ChromeDriver.cs +++ b/Selenium/Drivers/ChromeDriver.cs @@ -72,7 +72,7 @@ internal static void ExtendCapabilities(WebDriver wd, bool remote) { capa.TryMove("debuggerAddress", opts); if (wd.Profile != null) - wd.Arguments.Add("user-data-dir=" + ExpandProfile(wd.Profile, remote)); + wd.Arguments.Add("user-data-dir=" + ExpandProfile(wd.Profile, remote)); // make sure the profile is not a symlinked path! if (wd.Arguments.Count != 0) opts["args"] = wd.Arguments; diff --git a/Selenium/Drivers/EdgeDriver.cs b/Selenium/Drivers/EdgeDriver.cs index 9d49cca..98d7bd3 100644 --- a/Selenium/Drivers/EdgeDriver.cs +++ b/Selenium/Drivers/EdgeDriver.cs @@ -1,6 +1,8 @@ using Microsoft.Win32; using Selenium.Core; +using Selenium.Internal; using System; +using System.IO; using System.Net; using System.Runtime.InteropServices; using System.Text; @@ -71,10 +73,27 @@ internal static void ExtendCapabilities(WebDriver wd, bool remote) { Dictionary opts; if (!capa.TryGetValue("edgeOptions", out opts)) - capa["edgeOptions"] = opts = new Dictionary(); + capa["ms:edgeOptions"] = opts = new Dictionary(); + + if (wd.Profile != null) + wd.Arguments.Add("--user-data-dir=" + ExpandProfile(wd.Profile, remote)); + + if (wd.Arguments.Count != 0) + opts["args"] = wd.Arguments; } + private static string ExpandProfile(string profile, bool remote) { + if (!remote) { + if (IOExt.IsPath(profile)) { + profile = IOExt.ExpandPath(profile); + } else { + profile = IOExt.AppDataFolder + @"\Microsoft\Edge\Profiles\" + profile; + } + Directory.CreateDirectory(profile); + } + return profile; + } } } diff --git a/Selenium/WebDriver.cs b/Selenium/WebDriver.cs index 8dafd7b..b858296 100644 --- a/Selenium/WebDriver.cs +++ b/Selenium/WebDriver.cs @@ -127,7 +127,8 @@ public Proxy Proxy { /// /// Profil name (Firefox only) or directory (Firefox and Chrome) /// If true, the browser will be launched without a copy the profile (Firefox only) - /// The profile directory can be copied from the user temp folder (run %temp%) before the WebDriver is stopped. It's also possible to create a new Firefox profile by launching firefox with the "-p" switch (firefox.exe -p). + /// The profile directory can be copied from the user temp folder (run %temp%) before the WebDriver is stopped. + /// It's also possible to create a new Firefox profile by launching firefox with the "-p" switch (firefox.exe -p). /// /// /// Dim driver As New Selenium.FirefoxDriver From 3d34ab01ea6a20e96398af5938e074a8c2d56db0 Mon Sep 17 00:00:00 2001 From: zc2 Date: Tue, 29 Mar 2022 19:00:40 -0400 Subject: [PATCH 11/16] Gecko driver now supports a custom profile directory --- Selenium/Drivers/EdgeDriver.cs | 2 +- Selenium/Drivers/GeckoDriver.cs | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Selenium/Drivers/EdgeDriver.cs b/Selenium/Drivers/EdgeDriver.cs index 98d7bd3..f9a51a2 100644 --- a/Selenium/Drivers/EdgeDriver.cs +++ b/Selenium/Drivers/EdgeDriver.cs @@ -72,7 +72,7 @@ internal static void ExtendCapabilities(WebDriver wd, bool remote) { Capabilities capa = wd.Capabilities; Dictionary opts; - if (!capa.TryGetValue("edgeOptions", out opts)) + if (!capa.TryGetValue("ms:edgeOptions", out opts)) capa["ms:edgeOptions"] = opts = new Dictionary(); if (wd.Profile != null) diff --git a/Selenium/Drivers/GeckoDriver.cs b/Selenium/Drivers/GeckoDriver.cs index cfc7452..7f2ea92 100755 --- a/Selenium/Drivers/GeckoDriver.cs +++ b/Selenium/Drivers/GeckoDriver.cs @@ -1,6 +1,8 @@ using Microsoft.Win32; using Selenium.Core; +using Selenium.Internal; using System; +using System.IO; using System.Net; using System.Runtime.InteropServices; using System.Text; @@ -73,9 +75,23 @@ internal static void ExtendCapabilities(WebDriver wd, bool remote) { Capabilities capa = wd.Capabilities; Dictionary opts; - if (!capa.TryGetValue("GeckoOptions", out opts)) - capa["GeckoOptions"] = opts = new Dictionary(); + if (!capa.TryGetValue("moz:firefoxOptions", out opts)) + capa["moz:firefoxOptions"] = opts = new Dictionary(); + + if (wd.Profile != null) { + if (IOExt.IsPath(wd.Profile)) { + string profile = IOExt.ExpandPath(wd.Profile); + Directory.CreateDirectory(profile); + wd.Arguments.Add("-profile"); + wd.Arguments.Add(profile); + } else { + wd.Arguments.Add("-P"); + wd.Arguments.Add(wd.Profile); + } + } + if (wd.Arguments.Count != 0) + opts["args"] = wd.Arguments; } } From c86d162df5c80e1fd236adf786154535cf750ed2 Mon Sep 17 00:00:00 2001 From: zc2 Date: Mon, 24 Oct 2022 13:00:19 -0400 Subject: [PATCH 12/16] FindElem... now repeates not every 50ms but 1/10th of a total timeout period --- Selenium.Tests/Pages/element.html | 16 +++++++- Selenium.Tests/Program.cs | 2 +- Selenium.Tests/TS_Element.cs | 21 ++++++---- Selenium.Tests/TS_SearchContext.cs | 6 +-- Selenium.Tests/TS_Window.cs | 9 +++- Selenium/Common/SearchContext.cs | 5 ++- Selenium/Core/RemoteServer.cs | 2 +- Selenium/WebDriver.cs | 15 +++++-- Setup/Setup.vdproj | 66 +++++------------------------- 9 files changed, 68 insertions(+), 74 deletions(-) diff --git a/Selenium.Tests/Pages/element.html b/Selenium.Tests/Pages/element.html index 365332a..2a88e66 100644 --- a/Selenium.Tests/Pages/element.html +++ b/Selenium.Tests/Pages/element.html @@ -19,17 +19,31 @@ margin: 4px; padding: 3px; border: 1px solid gray; + } + + #blocker { + display:none; + position: absolute; + background-color: gray; + opacity: 0.4; + width: 100%; + height: 100%; + left: 0px; + top: 0px; } +
Input box

- +

diff --git a/Selenium.Tests/Program.cs b/Selenium.Tests/Program.cs index 4adec84..111578d 100644 --- a/Selenium.Tests/Program.cs +++ b/Selenium.Tests/Program.cs @@ -9,7 +9,7 @@ static void Main(string[] args) { Assembly.GetExecutingAssembly().Location, "/run:Selenium.Tests" // to test an individual fixture, uncomment the following line and add ', Category = "InFocus"' to the fixture - //,"/include:InFocus" + ,"/include:InFocus" }; int returnCode = NUnit.ConsoleRunner.Runner.Main(my_args); diff --git a/Selenium.Tests/TS_Element.cs b/Selenium.Tests/TS_Element.cs index 2ac7fd7..c599991 100644 --- a/Selenium.Tests/TS_Element.cs +++ b/Selenium.Tests/TS_Element.cs @@ -25,7 +25,7 @@ public TS_Element(Browser browser) public void SetUp() { driver.Get("/element.html"); } -/* + [TestCase] public void ShouldReturnDisplayState() { var ele1 = driver.FindElementById("input__search"); @@ -101,7 +101,6 @@ public void ShouldReturnSize() { A.AreEqual(308, size.Width); A.AreEqual(58, size.Height); } -*/ [TestCase] public void ShouldReturnActiveElement() { var ele1 = driver.FindElementById("input__search"); @@ -110,27 +109,33 @@ public void ShouldReturnActiveElement() { var ele2 = driver.ActiveElement(); A.True(ele1.Equals(ele2)); } -/* [TestCase] public void ShouldClearElement() { var ele1 = driver.FindElementById("input__search"); ele1.SendKeys("abc"); - A.AreEqual("abc", ele1.Attribute("value")); + A.AreEqual("abc", ele1.Value()); ele1.Clear(); - A.AreEqual("", ele1.Attribute("value")); + A.AreEqual("", ele1.Value()); } [TestCase] public void ShouldClickElement() { var ele1 = driver.FindElementById("input__search"); ele1.SendKeys("abc"); - A.AreEqual("abc", ele1.Attribute("value")); + A.AreEqual("abc", ele1.Value()); var ele2 = driver.FindElementById("bt_reset"); ele2.Click(); - A.AreEqual("", ele1.Attribute("value")); + A.AreEqual("", ele1.Value()); + } + [TestCase] + public void ShouldThrowOnBlockedClick() { + var ble = driver.FindElementById("blocker"); + ble.ExecuteScript("this.style.display='block';"); + + var ele2 = driver.FindElementById("bt_reset"); + A.Catch( () => ele2.Click(), "Expected to throw when the element was blocked!" ); } -*/ } } diff --git a/Selenium.Tests/TS_SearchContext.cs b/Selenium.Tests/TS_SearchContext.cs index e7ad1c3..96f94c2 100644 --- a/Selenium.Tests/TS_SearchContext.cs +++ b/Selenium.Tests/TS_SearchContext.cs @@ -9,7 +9,7 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Gecko)] + [TestFixture(Browser.Gecko, Category = "InFocus")] [TestFixture(Browser.Chrome)] [TestFixture(Browser.Edge)] /* @@ -148,13 +148,13 @@ public void ShouldFindWithImplicitWait() { document.body.appendChild(e); }, 100); "); - var ele = driver.FindElementById("id-append", 500, true); + var ele = driver.FindElementById("id-append", 1500, true); } [TestCase] [ExpectedException(typeof(Selenium.Errors.NoSuchElementError))] public void ShouldNotFind() { - var ele = driver.FindElementById("id-append", 50, true); + var ele = driver.FindElementById("id-append", 5000, true); } [TestCase] diff --git a/Selenium.Tests/TS_Window.cs b/Selenium.Tests/TS_Window.cs index 49410de..4a4d0b4 100644 --- a/Selenium.Tests/TS_Window.cs +++ b/Selenium.Tests/TS_Window.cs @@ -22,7 +22,14 @@ public TS_Window(Browser browser) [SetUp] public void SetUp() { - driver.Get("/win1.html"); + driver.Get("/win1.html?param1=1¶m2=2"); + } + + [TestCase] + public void Refresh() { + string url = driver.Url; + driver.Refresh(); + A.AreEqual( url, driver.Url ); } [TestCase] diff --git a/Selenium/Common/SearchContext.cs b/Selenium/Common/SearchContext.cs index 12efaf3..8b949d3 100644 --- a/Selenium/Common/SearchContext.cs +++ b/Selenium/Common/SearchContext.cs @@ -308,9 +308,12 @@ private WebElement FindFirstElement(Strategy strategy, string value, int timeout } catch (Errors.NoSuchElementError) { if (timeout == 0) throw; + if( timeout == -1 ) timeout = session.timeouts.timeout_implicitwait; + int time_chunk = timeout / 10; + if( time_chunk < 50 ) time_chunk = 50; var endTime = session.GetEndTime(timeout); while (true) { - SysWaiter.Wait(); + SysWaiter.Wait( time_chunk ); try { element = (Dictionary)session.SendAgain(); break; diff --git a/Selenium/Core/RemoteServer.cs b/Selenium/Core/RemoteServer.cs index 5368bf8..2dc0cd6 100644 --- a/Selenium/Core/RemoteServer.cs +++ b/Selenium/Core/RemoteServer.cs @@ -237,7 +237,7 @@ private static Dictionary GetHttpWebResponseContent(HttpWebResponse response) { case 404: throw new Exception("Unknown Commands: " + bodyText); case 405: throw new Exception("Invalid Command Method: " + bodyText); case 501: throw new Exception("Unimplemented Commands: " + bodyText); - default: throw new Exception(response.StatusDescription); + default: throw new Exception(response.StatusCode + " " + response.StatusDescription + " " + bodyText); } } } diff --git a/Selenium/WebDriver.cs b/Selenium/WebDriver.cs index b858296..def6eaa 100644 --- a/Selenium/WebDriver.cs +++ b/Selenium/WebDriver.cs @@ -607,21 +607,30 @@ public string Title { /// Goes one step backward in the browser history. ///
public void GoBack() { - this.session.Send(RequestMethod.POST, "/back"); + if( WebDriver.LEGACY ) + this.session.Send(RequestMethod.POST, "/back"); + else + this.session.Send(RequestMethod.POST, "/back", new Dictionary()); } /// /// Goes one step forward in the browser history. /// public void GoForward() { - this.session.Send(RequestMethod.POST, "/forward"); + if( WebDriver.LEGACY ) + this.session.Send(RequestMethod.POST, "/forward"); + else + this.session.Send(RequestMethod.POST, "/forward", new Dictionary()); } /// /// Refreshes the current page. /// public void Refresh() { - this.session.Send(RequestMethod.POST, "/refresh"); + if( WebDriver.LEGACY ) + this.session.Send(RequestMethod.POST, "/refresh"); + else + this.session.Send(RequestMethod.POST, "/refresh", new Dictionary()); } #endregion diff --git a/Setup/Setup.vdproj b/Setup/Setup.vdproj index e297cc9..c703465 100755 --- a/Setup/Setup.vdproj +++ b/Setup/Setup.vdproj @@ -27,12 +27,6 @@ } "Entry" { - "MsmKey" = "8:_13929B34F4054A97B6B42147CD0DEA06" - "OwnerKey" = "8:_UNDEFINED" - "MsmSig" = "8:_UNDEFINED" - } - "Entry" - { "MsmKey" = "8:_14F710EC4AA54E03A38B3867639E1521" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -63,12 +57,6 @@ } "Entry" { - "MsmKey" = "8:_4048D36914E4495F9E48767C2A41E3E0" - "OwnerKey" = "8:_UNDEFINED" - "MsmSig" = "8:_UNDEFINED" - } - "Entry" - { "MsmKey" = "8:_47DEF41580E44064B8893B8132C5AA1B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -230,6 +218,14 @@ "PrerequisitesLocation" = "2:1" "Url" = "8:" "ComponentsUrl" = "8:" + "Items" + { + "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.7.2" + { + "Name" = "8:Microsoft .NET Framework 4.7.2 (x86 and x64)" + "ProductCode" = "8:.NETFramework,Version=v4.7.2" + } + } } } "Release" @@ -362,26 +358,6 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } - "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_13929B34F4054A97B6B42147CD0DEA06" - { - "SourcePath" = "8:..\\References\\chromedriver.exe" - "TargetName" = "8:chromedriver.exe" - "Tag" = "8:" - "Folder" = "8:_A05A497EE20B4C04AF266C77C7FB80C0" - "Condition" = "8:" - "Transitive" = "11:FALSE" - "Vital" = "11:TRUE" - "ReadOnly" = "11:FALSE" - "Hidden" = "11:FALSE" - "System" = "11:FALSE" - "Permanent" = "11:FALSE" - "SharedLegacy" = "11:FALSE" - "PackageAs" = "3:1" - "Register" = "3:1" - "Exclude" = "11:FALSE" - "IsDependency" = "11:FALSE" - "IsolateTo" = "8:" - } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_14F710EC4AA54E03A38B3867639E1521" { "SourcePath" = "8:..\\Examples\\VBScript\\ListLinks(Firefox).vbs" @@ -462,26 +438,6 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } - "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4048D36914E4495F9E48767C2A41E3E0" - { - "SourcePath" = "8:..\\References\\edgedriver.exe" - "TargetName" = "8:edgedriver.exe" - "Tag" = "8:" - "Folder" = "8:_A05A497EE20B4C04AF266C77C7FB80C0" - "Condition" = "8:" - "Transitive" = "11:FALSE" - "Vital" = "11:TRUE" - "ReadOnly" = "11:FALSE" - "Hidden" = "11:FALSE" - "System" = "11:FALSE" - "Permanent" = "11:FALSE" - "SharedLegacy" = "11:FALSE" - "PackageAs" = "3:1" - "Register" = "3:1" - "Exclude" = "11:FALSE" - "IsDependency" = "11:FALSE" - "IsolateTo" = "8:" - } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_47DEF41580E44064B8893B8132C5AA1B" { "SourcePath" = "8:..\\Examples\\VBScript\\CaptureToPdf(Firefox).vbs" @@ -1002,15 +958,15 @@ { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:SeleniumBasic" - "ProductCode" = "8:{8007ED70-30D3-4763-BD57-CFEEAD1376D2}" - "PackageCode" = "8:{6B0600B4-C2AD-4DB9-8DB0-F40052C5DFDC}" + "ProductCode" = "8:{06E3B790-33C5-4D6F-B281-43584A1ACFF8}" + "PackageCode" = "8:{C3332363-7EDD-4AF4-9108-628084F44ACF}" "UpgradeCode" = "8:{6239A1D5-ADD1-4423-AE46-488AD298074A}" "AspNetVersion" = "8:4.0.30319.0" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:FALSE" "DetectNewerInstalledVersion" = "11:TRUE" "InstallAllUsers" = "11:TRUE" - "ProductVersion" = "8:2.0.9" + "ProductVersion" = "8:3.0.0" "Manufacturer" = "8:zc2com" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:" From 2b6907207c6ab3d68375babb9b28ac515e32a34e Mon Sep 17 00:00:00 2001 From: zc2 Date: Fri, 11 Nov 2022 17:13:37 -0500 Subject: [PATCH 13/16] Added a support of element ShadowRoot and help file generation --- .gitignore | 2 + CHANGELOG.txt | 10 ++ Sandcastle/COMdescrToXML.awk | 59 +++++++++++ Sandcastle/SeleniumBasic.shfbproj | 95 +++++++++++++++++ Selenium.Tests/Internals/BaseBrowsers.cs | 4 + Selenium.Tests/Pages/findby.html | 9 ++ Selenium.Tests/TS_Actions.cs | 41 +++++--- Selenium.Tests/TS_Alert.cs | 1 + Selenium.Tests/TS_Mouse.cs | 13 +-- Selenium.Tests/TS_Scraping.cs | 1 - Selenium.Tests/TS_SearchContext.cs | 16 ++- Selenium.Tests/TS_Windows.cs | 2 +- Selenium/Assert.cs | 2 +- Selenium/ComInterfaces/_Shadow.cs | 94 +++++++++++++++++ Selenium/ComInterfaces/_Waiter.cs | 4 +- Selenium/ComInterfaces/_WebElement.cs | 3 + Selenium/Common/By.cs | 2 +- Selenium/Common/SearchContext.cs | 38 ++++--- Selenium/Common/Shadow.cs | 114 ++++++++++++++++++++ Selenium/Common/Strategy.cs | 14 ++- Selenium/Common/Timeouts.cs | 8 +- Selenium/Common/WebElement.cs | 53 +++++++--- Selenium/Common/WebElements.cs | 2 +- Selenium/Common/Window.cs | 2 +- Selenium/Core/DriverService.cs | 2 +- Selenium/Internal/EndPointExt.cs | 2 - Selenium/NamespaceDoc.cs | 33 +++--- Selenium/Properties/AssemblyInfo.cs | 8 +- Selenium/Selenium.csproj | 5 +- Selenium/Struct/Dictionary.cs | 5 +- Selenium/Waiter.cs | 45 +++----- Selenium/WebDriver.cs | 127 +++++++++++++---------- regasm.bat | 4 + 33 files changed, 643 insertions(+), 177 deletions(-) create mode 100755 Sandcastle/COMdescrToXML.awk create mode 100755 Sandcastle/SeleniumBasic.shfbproj create mode 100755 Selenium/ComInterfaces/_Shadow.cs create mode 100755 Selenium/Common/Shadow.cs create mode 100755 regasm.bat diff --git a/.gitignore b/.gitignore index 8ab9e77..753bef1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ packages bin obj Xlbin +Sandcastle/Help +Sandcastle/COMs.xml *.msi *.exe *.log diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 4d1b6d7..2a177f2 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,10 @@ Author : Florent BREHERET Website : https://github.com/florentbr/SeleniumBasic +V3+ modifications : zc2 +Website : https://github.com/zc2com/SeleniumBasic + + Third party softwares: Selenium - Software Freedom Conservancy @@ -16,6 +20,12 @@ Third party softwares: http://phantomjs.org/ +v3.0.0.1 (2022/11/11) +=============================================================================== + * Added support of Gecko webdriver and modern API endpoints + * Added partial support of the ShadowRoot + * Built in 64 bit assembly + v2.0.9.0 (2016/03/02) =============================================================================== * Updated examples diff --git a/Sandcastle/COMdescrToXML.awk b/Sandcastle/COMdescrToXML.awk new file mode 100755 index 0000000..af68e26 --- /dev/null +++ b/Sandcastle/COMdescrToXML.awk @@ -0,0 +1,59 @@ +BEGIN { + print "" + print "" + print " " + print " Selenium" + print " " + print " " +} + +{ + if( match($0,/Description\("(.+?)"\)/,d) ) { + descr = d[1] + gsub("<","\\<",descr) + gsub(">","\\>",descr) + match(FILENAME,/(_[A-Z][A-z]+)\.cs/,f) + mn = "" + getline NL + if( match(NL,/\s([A-Z][A-z]+)\((.*)\);$/,m) ) { + method = m[1] + split(m[2],pars,",") + ps = "" + for( pi in pars ) { + par = pars[pi] +#print par + gsub(/^\s+/,"",par) + gsub(/\s.+$/,"",par) + gsub(/^\[MarshalAs\(UnmanagedType.Struct\)(, In)?\]/,"",par) + gsub(/^By/, "Selenium.By", par) + gsub(/^Strategy/,"Selenium.Strategy",par) + gsub(/^WebEleme/,"Selenium.WebEleme",par) + gsub(/^string/, "System.String", par) + gsub(/^object/, "System.Object", par) + gsub(/^bool/, "System.Boolean", par) + gsub(/^double/, "System.Double", par) + gsub(/^float/, "System.Single", par) + gsub(/^int/, "System.Int32", par) + gsub(/^short/, "System.Int16", par) + ps = ps ? ps "," par : par + } + if( ps ) ps = "(" ps ")" + mn = "M:Selenium.ComInterfaces." f[1] "." method ps + } else + if( match(NL,/\s([A-Z][A-z]+)\s*{\s*[gs]et/,m) ) { + mn = "P:Selenium.ComInterfaces." f[1] "." m[1] + } + if( mn ) { + print " " + print " " + print " " descr + print " " + print " " + } + } +} + +END { + print " " + print "" +} diff --git a/Sandcastle/SeleniumBasic.shfbproj b/Sandcastle/SeleniumBasic.shfbproj new file mode 100755 index 0000000..2a1b901 --- /dev/null +++ b/Sandcastle/SeleniumBasic.shfbproj @@ -0,0 +1,95 @@ + + + + + + Debug + AnyCPU + 2.0 + {f5d9ebff-8b71-4ca9-8357-36c7acef4147} + 2017.9.26.0 + + Documentation + Documentation + Documentation + + .NET Framework 4.0 + Help\ + SeleniumBasic + en-US + HtmlHelp1, Website + Visual Basic, Visual Basic Usage + VS2013 + True + True + False + False + OnlyWarningsAndErrors + 100 + + + + + + None + SeleniumBasic + 1.0.0.0 + MemberName + AboveNamespaces + False + False + 2 + False + Blank + ExplicitInterfaceImplementations, InheritedMembers, PublicCompilerGenerated, NonBrowsable + + + + + + + + + + ..\Selenium\ + False + SeleniumBasic - A Selenium based browser automation framework for VB.Net, Visual Basic Applications and VBScript + + All public methods and properties. Some of them available only from a .NET (i.e. not via COM). To know which are available via COM refer to a corresponding COM interface name. If a method listed as a class member but not a member of a corresponding interface, the method won't be available via COM. Methods with an "S" icon are static and surely not available via COM. However, look for a similar method in the "Explicit Interface Implementations" section. +Only methods of those interfaces are available via COM (Component Object Model) from VBScript, etc. + C:\Program Files (x86)\HTML Help Workshop\ + + + + + + + + + + + + + + + + + + + + + + + + + + + cd /d $(ProjectDir) +awk -f COMdescrToXML.awk ../Selenium/ComInterfaces/*.cs >COMs.xml + + OnBuildSuccess + + \ No newline at end of file diff --git a/Selenium.Tests/Internals/BaseBrowsers.cs b/Selenium.Tests/Internals/BaseBrowsers.cs index a08a737..89e045c 100644 --- a/Selenium.Tests/Internals/BaseBrowsers.cs +++ b/Selenium.Tests/Internals/BaseBrowsers.cs @@ -78,6 +78,10 @@ private WebDriver GetBrowserWithDriverStartedRemotely(Browser browser) { p_info.FileName = "chromedriver.exe"; p_info.Arguments = "--port=4444 --verbose"; break; + case Browser.Edge: + p_info.FileName = "edgedriver.exe"; + p_info.Arguments = "--port=4444 --verbose"; + break; default: throw new Exception("Browser not supported: " + browser.ToString()); } driver_process = Process.Start(p_info); diff --git a/Selenium.Tests/Pages/findby.html b/Selenium.Tests/Pages/findby.html index 31e0fbd..6bc139c 100644 --- a/Selenium.Tests/Pages/findby.html +++ b/Selenium.Tests/Pages/findby.html @@ -13,5 +13,14 @@
Div5
+ Shadow root: +
+ \ No newline at end of file diff --git a/Selenium.Tests/TS_Actions.cs b/Selenium.Tests/TS_Actions.cs index ce337a3..ec67674 100644 --- a/Selenium.Tests/TS_Actions.cs +++ b/Selenium.Tests/TS_Actions.cs @@ -1,5 +1,7 @@ using Selenium.Tests.Internals; +using System; using System.ComponentModel; +using A = NUnit.Framework.Assert; using SetUp = NUnit.Framework.SetUpAttribute; using TestCase = NUnit.Framework.TestCaseAttribute; using TestFixture = NUnit.Framework.TestFixtureAttribute; @@ -19,6 +21,7 @@ public TS_Actions(Browser browser) : base(browser) { } [SetUp] + public void SetUp() { } @@ -27,7 +30,7 @@ public void LocalShouldNotFail() { this.driver.BaseUrl = WebServer.BaseUri; driver.Get("/input.html"); WebElement ele = driver.FindElementById("input__search"); - Assert.NotEquals( ele, null, "Failed to find element by Id" ); + A.IsNotNull( ele, "Failed to find element by Id" ); driver.Actions .Click(ele) .Click() @@ -53,38 +56,40 @@ public void LocalShouldNotFail() { [TestCase] public void DemoQAButtons() { driver.Get("https://demoqa.com/buttons"); + driver.Wait( 5000 ); // some ads appear WebElement ele; ele = driver.FindElementByXPath("//button[@type='button' and .='Click Me']"); - Assert.NotEquals( ele, null, "Left click button" ); + A.IsNotNull( ele, "Left click button" ); driver.Actions .Click(ele) .Perform(); WebElement e_scm = driver.FindElementById("dynamicClickMessage"); - Assert.NotEquals( e_scm, null ); + A.IsNotNull( e_scm, "dynamic Click Message" ); ele = driver.FindElementById("rightClickBtn"); - Assert.NotEquals( ele, null, "Right click button" ); + A.IsNotNull( ele, "Right click button" ); driver.Actions .ClickContext(ele) .Perform(); WebElement e_rcm = driver.FindElementById("rightClickMessage"); - Assert.NotEquals( e_rcm, null ); + A.IsNotNull( e_rcm, "rightClickMessage" ); ele = driver.FindElementById("doubleClickBtn"); - Assert.NotEquals( ele, null, "Right click button" ); + A.IsNotNull( ele, "Right click button" ); driver.Actions .ClickDouble(ele) .Perform(); WebElement e_dcm = driver.FindElementById("doubleClickMessage"); - Assert.NotEquals( e_dcm, null ); + A.IsNotNull( e_dcm, "doubleClickMessage" ); } [TestCase] public void DemoQASlider() { driver.Get("https://demoqa.com/slider"); + driver.Wait( 5000 ); // some ads appear WebElement ele; ele = driver.FindElementByXPath("//input[@type='range']"); - Assert.NotEquals( ele, null ); + A.IsNotNull( ele, "range input" ); driver.Actions .ClickAndHold(ele) .MoveByOffset( 20, 0 ) @@ -96,42 +101,46 @@ public void DemoQASlider() { .Wait( 1000 ) .Perform(); } - + [TestCase] public void DemoQAHover() { driver.Get("https://demoqa.com/tool-tips"); + driver.Wait( 5000 ); // some ads appear WebElement ele; ele = driver.FindElementById("toolTipButton"); - Assert.NotEquals( ele, null ); + A.IsNotNull( ele, "toolTipButton" ); driver.Actions .MoveToElement(ele) .Wait( 1000 ) .Perform(); - Assert.NotEquals( driver.FindElementById("buttonToolTip"), null ); + A.IsNotNull( driver.FindElementById("buttonToolTip"), "buttonToolTip" ); } [TestCase] public void DemoQADragDrop() { driver.Get("https://demoqa.com/dragabble"); WebElement ele = driver.FindElementById("dragBox"); - Assert.NotEquals( ele, null ); + A.IsNotNull( ele, "dragBox" ); + Point loc1 = ele.LocationInView(); driver.Actions .DragAndDropByOffset(ele, 100, 50) .Wait( 1000 ) .Perform(); + Point loc2 = ele.LocationInView(); + A.AreNotEqual( loc1.X, loc2.X ); + A.AreNotEqual( loc1.Y, loc2.Y ); driver.Wait( 1000 ); driver.Get("https://demoqa.com/droppable"); WebElement ele1 = driver.FindElementById("draggable"); WebElement ele2 = driver.FindElementById("droppable"); - Assert.NotEquals( ele1, null ); - Assert.NotEquals( ele1, null ); + A.IsNotNull( ele1 ); + A.IsNotNull( ele1 ); driver.Actions .DragAndDrop(ele1, ele2) .Wait( 1000 ) .Perform(); - Assert.Equals( ele2.Text(), "Dropped!" ); + A.AreEqual( "Dropped!", ele2.Text() ); } } - } diff --git a/Selenium.Tests/TS_Alert.cs b/Selenium.Tests/TS_Alert.cs index d74c63e..cbf0b54 100644 --- a/Selenium.Tests/TS_Alert.cs +++ b/Selenium.Tests/TS_Alert.cs @@ -44,6 +44,7 @@ public void ShouldDismissAlert() { public void ShouldAcceptAlert() { driver.Get("/input.html"); driver.ExecuteScript("window.setTimeout(function(){window.res=window.confirm('test dismiss');}, 100);"); +// driver.Wait( 2000 ); var alert = driver.SwitchToAlert(); A.AreEqual("test dismiss", alert.Text); alert.Dismiss(); diff --git a/Selenium.Tests/TS_Mouse.cs b/Selenium.Tests/TS_Mouse.cs index 9454878..09351e9 100644 --- a/Selenium.Tests/TS_Mouse.cs +++ b/Selenium.Tests/TS_Mouse.cs @@ -40,28 +40,29 @@ public void ShouldDoubleClick() { [TestCase] public void DemoQAButtons() { driver.Get("https://demoqa.com/buttons"); + driver.Wait( 5000 ); var mouse = driver.Mouse; WebElement ele; ele = driver.FindElementByXPath("//button[@type='button' and .='Click Me']"); - Assert.NotEquals( ele, null, "Left click button" ); + A.IsNotNull( ele, "Left click button" ); mouse.MoveTo(ele, 50); mouse.Click(); WebElement e_scm = driver.FindElementById("dynamicClickMessage"); - Assert.NotEquals( e_scm, null ); + A.IsNotNull( e_scm, "dynamicClickMessage" ); ele = driver.FindElementById("rightClickBtn"); - Assert.NotEquals( ele, null, "Right click button" ); + A.IsNotNull( ele, "Right click button" ); mouse.MoveTo(ele); mouse.Click(MouseButton.Right); WebElement e_rcm = driver.FindElementById("rightClickMessage"); - Assert.NotEquals( e_rcm, null ); + A.IsNotNull( e_rcm, "rightClickMessage" ); ele = driver.FindElementById("doubleClickBtn"); - Assert.NotEquals( ele, null, "Double click button" ); + A.IsNotNull( ele, "Double click button" ); mouse.MoveTo(ele); mouse.ClickDouble(); WebElement e_dcm = driver.FindElementById("doubleClickMessage"); - Assert.NotEquals( e_dcm, null ); + A.IsNotNull( e_dcm, "doubleClickMessage" ); } } diff --git a/Selenium.Tests/TS_Scraping.cs b/Selenium.Tests/TS_Scraping.cs index d68ede9..ea0d158 100644 --- a/Selenium.Tests/TS_Scraping.cs +++ b/Selenium.Tests/TS_Scraping.cs @@ -36,7 +36,6 @@ public void ShouldScrapTextFromElements() { var data = elements.Text(); A.AreEqual(data[0], "Table Heading 1"); } - } } diff --git a/Selenium.Tests/TS_SearchContext.cs b/Selenium.Tests/TS_SearchContext.cs index 96f94c2..8bebfef 100644 --- a/Selenium.Tests/TS_SearchContext.cs +++ b/Selenium.Tests/TS_SearchContext.cs @@ -9,7 +9,7 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Gecko, Category = "InFocus")] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] [TestFixture(Browser.Edge)] /* @@ -163,6 +163,20 @@ public void ShouldFindWithNoException() { A.Null(ele); } + [TestCase] + [IgnoreFixture(Browser.Firefox, "Shadow is not supported")] + public void ShouldGetThroughShadow() { + var sc = driver.FindElementByCss("div#shadow-ctr"); + var sr = sc.Shadow(); + A.IsNotNull( sr, "Element has no Shadow " ); + if( Fixture.Equals( Browser.Gecko ) ) + A.Ignore( "DNW in gecko. See https://bugzilla.mozilla.org/show_bug.cgi?id=1700097" ); + if( !( Fixture.Equals( Browser.Chrome ) || Fixture.Equals( Browser.Edge ) ) ) + A.Ignore("Old browsers are not supported"); + A.IsNotNull(sr.FindElementByCss("span")); + A.IsNotNull(sr.FindElementByTag("span")); + A.Throws( () => sr.FindElementByXPath(".//span") ); // apparently not supported + } } } diff --git a/Selenium.Tests/TS_Windows.cs b/Selenium.Tests/TS_Windows.cs index 316ab97..7af79ac 100644 --- a/Selenium.Tests/TS_Windows.cs +++ b/Selenium.Tests/TS_Windows.cs @@ -8,7 +8,7 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Gecko)] + [TestFixture(Browser.Gecko, Category = "InFocus")] [TestFixture(Browser.Chrome)] [TestFixture(Browser.Edge)] /* diff --git a/Selenium/Assert.cs b/Selenium/Assert.cs index 6ec806d..df10582 100644 --- a/Selenium/Assert.cs +++ b/Selenium/Assert.cs @@ -23,7 +23,7 @@ namespace Selenium { /// driver.stop ///
/// - /// + /// /// Private Assert As New Selenium.Assert /// /// Public Sub TestCase() diff --git a/Selenium/ComInterfaces/_Shadow.cs b/Selenium/ComInterfaces/_Shadow.cs new file mode 100755 index 0000000..c46059f --- /dev/null +++ b/Selenium/ComInterfaces/_Shadow.cs @@ -0,0 +1,94 @@ +using Selenium; +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace Selenium.ComInterfaces { +#pragma warning disable 1591 + + /// + /// Methods available to get child elements of a ShadowRoot + /// + /// + /// Not all methods are supported. Different drivers vary the degree of support. Use caution. + /// + /// + [Guid("0277FC34-FD1B-4616-BB19-939067ADF4F1")] + [ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] + public interface _Shadow { + + #region Find Elements + + [DispId(1176), Description("Find the first WebElement using the given method.")] + WebElement FindElement([MarshalAs(UnmanagedType.Struct)]By by, int timeout = -1, bool raise = true); + + [DispId(1173), Description("Find the first WebElement for a given strategy and value.")] + WebElement FindElementBy(Strategy strategy, string value, int timeout = -1, bool raise = true); + + [DispId(1178), Description("Finds the first element matching the specified CSS class.")] + WebElement FindElementByClass(string classname, int timeout = -1, bool raise = true); + + [DispId(1180), Description("Finds the first element matching the specified CSS selector.")] + WebElement FindElementByCss(string cssselector, int timeout = -1, bool raise = true); + + [DispId(1183), Description("Finds the first element matching the specified id.")] + WebElement FindElementById(string id, int timeout = -1, bool raise = true); + + [DispId(1185), Description("Finds the first element matching the specified link text.")] + WebElement FindElementByLinkText(string linktext, int timeout = -1, bool raise = true); + + [DispId(1187), Description("Finds the first element matching the specified name.")] + WebElement FindElementByName(string name, int timeout = -1, bool raise = true); + + [DispId(1189), Description("Finds the first of elements that match the part of the link text supplied")] + WebElement FindElementByPartialLinkText(string partiallinktext, int timeout = -1, bool raise = true); + + [DispId(1191), Description("Finds the first element matching the specified tag name.")] + WebElement FindElementByTag(string tagname, int timeout = -1, bool raise = true); + + [DispId(1193), Description("Finds the first element matching the specified XPath query.")] + WebElement FindElementByXPath(string xpath, int timeout = -1, bool raise = true); + + [DispId(1200), Description("Find all elements within the current context using the given mechanism.")] + WebElements FindElements([MarshalAs(UnmanagedType.Struct)]By by, int minimum = 0, int timeout = 0); + + [DispId(1210), Description("Find all elements for a given strategy and value.")] + WebElements FindElementsBy(Strategy strategy, string value, int minimum = 0, int timeout = 0); + + [DispId(1220), Description("Finds elements matching the specified class name.")] + WebElements FindElementsByClass(string classname, int minimum = 0, int timeout = 0); + + [DispId(1221), Description("Finds elements matching the specified CSS selector.")] + WebElements FindElementsByCss(string cssselector, int minimum = 0, int timeout = 0); + + [DispId(1223), Description("Finds elements matching the specified id.")] + WebElements FindElementsById(string id, int minimum = 0, int timeout = 0); + + [DispId(1225), Description("Finds elements matching the specified link text.")] + WebElements FindElementsByLinkText(string linktext, int minimum = 0, int timeout = 0); + + [DispId(1227), Description("Finds elements matching the specified name.")] + WebElements FindElementsByName(string name, int minimum = 0, int timeout = 0); + + [DispId(1229), Description("Finds the first of elements that match the part of the link text supplied")] + WebElements FindElementsByPartialLinkText(string partiallinktext, int minimum = 0, int timeout = 0); + + [DispId(1235), Description("Finds elements matching the specified tag name.")] + WebElements FindElementsByTag(string tagname, int minimum = 0, int timeout = 0); + + [DispId(1239), Description("Finds elements matching the specified XPath query.")] + WebElements FindElementsByXPath(string xpath, int minimum = 0, int timeout = 0); + + [DispId(1250), Description("Indicates whether a WebElement is present using the given method.")] + bool IsElementPresent([MarshalAs(UnmanagedType.Struct)]By by, int timeout = 0); + + [DispId(1260), Description("Waits for an element to disappear from the page")] + void WaitNotElement([MarshalAs(UnmanagedType.Struct)]By by, int timeout = -1); + + #endregion + + [DispId(899), Description("Evaluate equality")] + bool Equals(object obj); + } + +} diff --git a/Selenium/ComInterfaces/_Waiter.cs b/Selenium/ComInterfaces/_Waiter.cs index f816083..9809663 100644 --- a/Selenium/ComInterfaces/_Waiter.cs +++ b/Selenium/ComInterfaces/_Waiter.cs @@ -18,8 +18,8 @@ public interface _Waiter { [DispId(798), Description("Waits the given time in milliseconds")] void Wait(int timems); - [DispId(830), Description("Waits for the given function to return true. Ex: Waiter.Until addressOf MyFunction")] - object Until(object function, object argument = null, int timeout = -1, string timeoutMessage = null); + [DispId(830), Description("Waits for a function(a,b) to return true.")] + object Until(object function, object argument = null, int timeout = -1, string reserved = null); [DispId(876), Description("Waits for a file to be ready.")] void WaitForFile(string path, int timeout = -1); diff --git a/Selenium/ComInterfaces/_WebElement.cs b/Selenium/ComInterfaces/_WebElement.cs index d01f223..a18eb0e 100644 --- a/Selenium/ComInterfaces/_WebElement.cs +++ b/Selenium/ComInterfaces/_WebElement.cs @@ -48,6 +48,9 @@ public interface _WebElement { [DispId(965), Description("Returns the value attribute")] object Value(); + [DispId(967), Description("Returns the shadow root (if exists)")] + Shadow Shadow(); + #endregion diff --git a/Selenium/Common/By.cs b/Selenium/Common/By.cs index ffd8894..50b3ecb 100644 --- a/Selenium/Common/By.cs +++ b/Selenium/Common/By.cs @@ -303,7 +303,7 @@ By _By.Tag(string tagName) { /// /// Search by XPath /// - /// XPath locator + /// XPath /// By object By _By.XPath(string xpath) { return new By(Strategy.XPath, xpath); diff --git a/Selenium/Common/SearchContext.cs b/Selenium/Common/SearchContext.cs index 8b949d3..851abd5 100644 --- a/Selenium/Common/SearchContext.cs +++ b/Selenium/Common/SearchContext.cs @@ -4,19 +4,21 @@ namespace Selenium { /// - /// Locators methods for WebDriver and WebElement + /// Locators methods for , and /// + /// + /// If the element was not found on a first attempt, Selenium repeats attempt with the interval which is 1/10th of the specified total timeout + /// but not often than 50ms. For example, if the timeout was 30000ms, then the attempts will be repeating every 3000ms. + /// If a timeout is not specified will be used by default. + /// public abstract class SearchContext { - internal abstract RemoteSession session { get; } - internal abstract string uri { get; } - /// - /// "Verifies that the specified element is somewhere on the page." + /// Verifies that the specified element is somewhere on the page. /// - /// An element loctor. string or By object + /// An element locator. string or By object /// Optional timeout in milliseconds /// true if the element is present, false otherwise public bool IsElementPresent(By by, int timeout = 0) { @@ -96,10 +98,11 @@ public WebElement FindElementByName(string name, int timeout = -1, bool raise = /// /// Finds the first element matching the specified XPath query. /// - /// XPath + /// XPath expression /// Optional timeout in milliseconds /// Optional - Raise an exception after the timeout when true /// or null + /// When the raise param was true and no element was found or appear in the specific time frame public WebElement FindElementByXPath(string xpath, int timeout = -1, bool raise = true) { return this.FindElementBy(Strategy.XPath, xpath, timeout, raise); } @@ -219,10 +222,11 @@ public WebElements FindElementsByName(string name, int minimum = 0, int timeout /// /// Finds elements matching the specified XPath query. /// - /// XPath + /// XPath /// Minimum number of elements to wait for /// Optional timeout in milliseconds /// + /// When no desired number of specified elements exist or appear in the specific time frame public WebElements FindElementsByXPath(string xpath, int minimum = 0, int timeout = 0) { return this.FindElementsBy(Strategy.XPath, xpath, minimum, timeout); } @@ -301,7 +305,7 @@ private WebElement FindFirstElement(Strategy strategy, string value, int timeout string relativeUri = this.uri + "/element"; Dictionary element; try { - if( !WebDriver.LEGACY ) + if( !WebDriver.LEGACY || this is Shadow ) TranslateStrategy( ref strategy, ref value ); string method = By.FormatStrategy(strategy); element = (Dictionary)session.Send(RequestMethod.POST, relativeUri, "using", method, "value", value); @@ -328,6 +332,12 @@ private WebElement FindFirstElement(Strategy strategy, string value, int timeout private bool TranslateStrategy( ref Strategy strategy, ref string value ) { switch( strategy ) { + case Strategy.Tag: + if( !(this is Shadow) ) + return false; + strategy = Strategy.Css; + value = value; + return true; case Strategy.Id: strategy = Strategy.Css; value = "#" + value; @@ -356,7 +366,7 @@ private WebElement FindAnyElement(By byAny, int timeout) { try { Strategy strategy = by.Strategy; string value = by.Value.ToString(); - if( !WebDriver.LEGACY ) + if( !WebDriver.LEGACY || this is Shadow ) TranslateStrategy( ref strategy, ref value ); string method = By.FormatStrategy(strategy); element = (Dictionary)session.Send(RequestMethod.POST, relativeUri, "using", method, "value", value); @@ -376,7 +386,7 @@ private WebElements FindAllElements(Strategy strategy, string value, int minimum RemoteSession session = this.session; string uri = this.uri + "/elements"; try { - if( !WebDriver.LEGACY ) + if( !WebDriver.LEGACY || this is Shadow ) TranslateStrategy( ref strategy, ref value ); var method = By.FormatStrategy(strategy); List elements = session.SendUntil(timeout, @@ -400,7 +410,7 @@ private WebElements FindAnyElements(By byAny, int minimum, int timeout) { break; Strategy strategy = by.Strategy; var value = (string)by.Value; - if( !WebDriver.LEGACY ) + if( !WebDriver.LEGACY || this is Shadow ) TranslateStrategy( ref strategy, ref value ); var method = By.FormatStrategy(strategy); List elements = (List)session.Send(RequestMethod.POST, uri, "using", method, "value", value); @@ -420,7 +430,7 @@ private WebElements FindAnyElements(By byAny, int minimum, int timeout) { private void WaitElementNotPresent(Strategy strategy, string value, int timeout) { RemoteSession session = this.session; string uri = this.uri + "/element"; - if( !WebDriver.LEGACY ) + if( !WebDriver.LEGACY || this is Shadow ) TranslateStrategy( ref strategy, ref value ); string method = By.FormatStrategy(strategy); DateTime endTime = session.GetEndTime(timeout); @@ -448,7 +458,7 @@ private void WaitAnyElementNotPresent(By byAny, int timeout) { try { Strategy strategy = by.Strategy; string value = by.Value.ToString(); - if( !WebDriver.LEGACY ) + if( !WebDriver.LEGACY || this is Shadow ) TranslateStrategy( ref strategy, ref value ); string method = By.FormatStrategy(strategy); session.Send(RequestMethod.POST, uri, "using", method, "value", value); diff --git a/Selenium/Common/Shadow.cs b/Selenium/Common/Shadow.cs new file mode 100755 index 0000000..75dd89d --- /dev/null +++ b/Selenium/Common/Shadow.cs @@ -0,0 +1,114 @@ +using Selenium.Core; +using Selenium.Internal; +using Selenium.Serializer; +using System; +using System.Collections; +using System.ComponentModel; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace Selenium { + + /// + /// Represents an element's ShadowRoot. See + /// + [ProgId("Selenium.Shadow")] + [Guid("0277FC34-FD1B-4616-BB19-98333C96F89B")] + [Description("Interface to a shadow root.")] + [ComVisible(true), ClassInterface(ClassInterfaceType.None)] + public class Shadow : SearchContext, ComInterfaces._Shadow, IJsonObject { + internal const string SHADOW = "SHADOW"; + internal const string IDENTIFIER = "shadow-6066-11e4-a52e-4f735466cecf"; + + + internal static bool TryParse(Dictionary dict, out string id) { + return dict.TryGetValue(IDENTIFIER, out id); + } + + internal readonly RemoteSession _session; + internal readonly string Id; + + internal Shadow(RemoteSession session, string id) { + _session = session; + this.Id = id; + } + + internal Shadow(RemoteSession session, Dictionary dict) { + _session = session; + if (!Shadow.TryParse(dict, out this.Id)) + throw new SeleniumException("Failed to extact the Shadow from the dictionary. Missing key."); + } + + internal override RemoteSession session { + get { + return _session; + } + } + + internal override string uri { + get { + return "/shadow/" + this.Id; + } + } + + /// + /// + /// + public Dictionary SerializeJson() { + var dict = new Dictionary(); + if( !WebDriver.LEGACY ) + dict.Add(IDENTIFIER, this.Id); // Selenium.NET sends both. Let's do the same. + dict.Add( SHADOW, this.Id); + return dict; + } + + private object Send(RequestMethod method, string relativeUri) { + return _session.Send(method, this.uri + relativeUri); + } + + private object Send(RequestMethod method, string relativeUri, string key, object value) { + return _session.Send(method, this.uri + relativeUri, key, value); + } + + private object Send(RequestMethod method, string relativeUri, string key1, object value1, string key2, object value2) { + return _session.Send(method, this.uri + relativeUri, key1, value1, key2, value2); + } + + /// + /// Compares if two web elements are equal + /// + /// WebElement to compare against + /// A boolean if it is equal or not + public bool Equals(WebElement other) { + if (Id == other.Id) + return true; + return (bool)Send(RequestMethod.GET, "/equals/" + other.Id); + } + + #region Protected support methods + + /// + /// Determines whether the specified instances are considered equal. + /// + /// + /// True if equal, false otherwise. + public override bool Equals(object obj) { + Shadow shd = obj as Shadow; + if (shd == null) + return false; + return Id == shd.Id; + } + + /// + /// Returns the hash code for this element + /// + /// + public override int GetHashCode() { + return Id.GetHashCode(); + } + + #endregion + + } +} diff --git a/Selenium/Common/Strategy.cs b/Selenium/Common/Strategy.cs index 11600b7..484fe02 100644 --- a/Selenium/Common/Strategy.cs +++ b/Selenium/Common/Strategy.cs @@ -6,6 +6,10 @@ namespace Selenium { /// /// Mechanism by which to find elements within a document. /// + /// + /// See W3's Locator strategies. + /// Those in the list below without string constant are not supported by the modern webdriver API + /// [Guid("0277FC34-FD1B-4616-BB19-300DAA508541")] [ComVisible(true)] public enum Strategy : short { @@ -13,19 +17,19 @@ public enum Strategy : short { None = 0, /// Class = 1, - /// + /// "css selector" Css = 2, /// Id = 3, /// Name = 4, - /// + /// "link text" LinkText = 5, - /// + /// "partial link text" PartialLinkText = 6, - /// + /// "tag name" Tag = 7, - /// + /// "xpath" XPath = 8, /// Any = 9 diff --git a/Selenium/Common/Timeouts.cs b/Selenium/Common/Timeouts.cs index decdcc4..23cd647 100644 --- a/Selenium/Common/Timeouts.cs +++ b/Selenium/Common/Timeouts.cs @@ -40,8 +40,9 @@ private static void SendTimeoutImplicit(RemoteSession session, int timeout) { internal int timeout_script = 15000; // 15 seconds /// - /// Amount of time that Selenium will wait for waiting commands to complete + /// Amount of time that Selenium will wait for commands to complete. Default is 3000ms /// + /// Default is 3000ms public int ImplicitWait { get { return timeout_implicitwait; @@ -55,6 +56,7 @@ public int ImplicitWait { /// /// Amount of time the driver should wait while loading a page before throwing an exception. /// + /// Default is 60000ms public int PageLoad { get { return timeout_pageload; @@ -70,6 +72,7 @@ public int PageLoad { /// /// Amount of time the driver should wait while executing an asynchronous script before throwing an error. /// + /// Default is 15000ms public int Script { get { return timeout_script; @@ -83,8 +86,9 @@ public int Script { } /// - /// Maximum amount of time the driver should wait while waiting for a response from the server. + /// Maximum amount of time the driver should wait while waiting for a response from the server. /// + /// Default is 90000ms public int Server { get { return timeout_server; diff --git a/Selenium/Common/WebElement.cs b/Selenium/Common/WebElement.cs index 29c699e..3f20d31 100644 --- a/Selenium/Common/WebElement.cs +++ b/Selenium/Common/WebElement.cs @@ -18,8 +18,8 @@ namespace Selenium { [Description("Defines the interface through which the user controls elements on the page.")] [ComVisible(true), ClassInterface(ClassInterfaceType.None)] public class WebElement : SearchContext, ComInterfaces._WebElement, IJsonObject { - public const string ELEMENT = "ELEMENT"; - public const string IDENTIFIER = "element-6066-11e4-a52e-4f735466cecf"; + internal const string ELEMENT = "ELEMENT"; + internal const string IDENTIFIER = "element-6066-11e4-a52e-4f735466cecf"; /// /// Returns the element with focus, or BODY if nothing has focus. @@ -125,7 +125,7 @@ public bool IsSelected { /// /// Returns the location of the element in the renderable canvas /// - /// Point + /// Point object public Point Location() { if( WebDriver.LEGACY ) { var dict = (Dictionary)Send(RequestMethod.GET, "/location"); @@ -139,9 +139,15 @@ public Point Location() { /// /// Gets the location of an element relative to the origin of the view port. /// + /// In the modern mode (not legacy), same as Location() public Point LocationInView() { - var dict = (Dictionary)Send(RequestMethod.GET, "/location_in_view"); - return new Point(dict); + if( WebDriver.LEGACY ) { + var dict = (Dictionary)Send(RequestMethod.GET, "/location_in_view"); + return new Point(dict); + } else { + var dict = (Dictionary)Send(RequestMethod.GET, "/rect"); + return new Point(dict); + } } /// @@ -170,6 +176,10 @@ public object Rect { /// /// Whether the element would be visible to a user /// + /// False if the element is is scrolled out of view, obscured and not visible to the user by any other reasons + /// + /// The result does not relate to the visibility or display style properties! + /// public bool IsDisplayed { get { return (bool)Send(RequestMethod.GET, "/displayed"); @@ -232,6 +242,17 @@ public object Value() { return Send(RequestMethod.GET, "/property/value"); } + /// + /// Returns the attached shadow root (if exists) + /// Note: Searching elements from a shadow root is not supported in gecko. + /// Also, the XPath strategy cannot be used. + /// + /// Shadow instance + public Shadow Shadow() { + var result = (Dictionary)Send(RequestMethod.GET, "/shadow"); + return new Shadow(session, result); + } + /// /// Gets the screenshot of the current element /// @@ -411,6 +432,7 @@ public void Submit() { /// Clicks the element. /// /// Optional - Modifier Keys to press + /// Throws if the element is blocked by another element public void Click(string keys = null) { if (keys != null) { if( WebDriver.LEGACY ) @@ -433,6 +455,12 @@ public void Click(string keys = null) { /// /// Scrolls the current element into the visible area of the browser window. /// + /// Optional - desired position of the element + /// This method just executes the element's JavaScript method scrollIntoView(alignTop) + /// Sometimes, calling this won't make the element visible (or clickable) + /// because of other absolute positioned headers or footers. + /// To scroll into the center, execute a script like "this.scrollIntoView({block: "center"});" + /// public WebElement ScrollIntoView(bool alignTop = false) { string script = "arguments[0].scrollIntoView(" + (alignTop ? "true);" : "false);"); @@ -497,12 +525,13 @@ public T Until(Func func, int timeout = -1) { } /// - /// Waits for a procesult to set result to true. VBScript: Function WaitEx(webdriver), VBA: Function WaitEx(webdriver As WebDriver) As Boolean + /// Waits until a function(element, argument) returns a true-castable result. /// - /// Function reference. Sub Procedure(element, result) waitFor AddressOf Procedure - /// Optional - Argument: Sub Procedure(element, argument, result) waitFor AddressOf Procedure, "argument" + /// Function reference. In VBScript use GetRef() + /// Optional - the function's second argument /// Optional - timeout in milliseconds - /// Current WebDriver + /// function's actual result + /// Throws when time out has reached object ComInterfaces._WebElement.Until(object procedure, object argument, int timeout) { if (timeout == -1) timeout = _session.timeouts.timeout_implicitwait; @@ -664,14 +693,14 @@ public void WaitRemoval(int timeout = -1) { #region Casting properties /// - /// Cast the WebElement to a Select element + /// Cast the WebElement to a /// public SelectElement AsSelect() { return new SelectElement(this); } /// - /// Cast the WebElement to a Select element + /// Cast the WebElement to a /// public TableElement AsTable() { return new TableElement(_session, this); @@ -707,7 +736,7 @@ public object ExecuteScript(string script, object arguments = null) { /// The JavaScript code to execute. /// Optional arguments for the script. /// Optional timeout in milliseconds. - /// The first argument of the callback function. + /// The first argument of the called function callback() . /// /// /// href = ele.ExecuteAsyncScript("callback(this.href);");" diff --git a/Selenium/Common/WebElements.cs b/Selenium/Common/WebElements.cs index 646ab1d..8e7964f 100644 --- a/Selenium/Common/WebElements.cs +++ b/Selenium/Common/WebElements.cs @@ -137,7 +137,7 @@ string script_ex /// Execute an asynchronous piece of JavaScript against each web element and returns all the results; /// /// Javascript script - /// Null elements are skiped + /// Timeout in millisecond /// List /// /// diff --git a/Selenium/Common/Window.cs b/Selenium/Common/Window.cs index 2c7164d..b4a3266 100644 --- a/Selenium/Common/Window.cs +++ b/Selenium/Common/Window.cs @@ -97,7 +97,7 @@ public Window SwitchToWindowByTitle(string title, int timeout = -1) { /// /// Change focus to another window. /// - /// Name of the window + /// Name or handle of the desired window /// Optional timeout in milliseconds public Window SwitchToWindowByName(string name, int timeout = -1) { return _windows.SwitchToWindowByName(name, timeout); diff --git a/Selenium/Core/DriverService.cs b/Selenium/Core/DriverService.cs index 7b19e9c..817aeaf 100644 --- a/Selenium/Core/DriverService.cs +++ b/Selenium/Core/DriverService.cs @@ -63,7 +63,7 @@ public void Quit(RemoteServer server) { public virtual string Uri { get { - return "http://localhost:" + _endpoint.IPEndPoint.Port.ToString(); + return "http://127.0.0.1:" + _endpoint.IPEndPoint.Port.ToString(); } } diff --git a/Selenium/Internal/EndPointExt.cs b/Selenium/Internal/EndPointExt.cs index 97b3578..5af12c4 100644 --- a/Selenium/Internal/EndPointExt.cs +++ b/Selenium/Internal/EndPointExt.cs @@ -62,7 +62,6 @@ public override string ToString() { /// /// Returns true if a given host:port is connectable, false otherwise /// - /// Endpoint holding the host ip and port /// Timeout in millisecond /// Delay to retry in millisecond /// True if succeed, false otherwise @@ -96,7 +95,6 @@ public bool WaitForConnectable(int timeout, int delay) { /// /// Waits for a local port to be listening on the Loopback or Any address. /// - /// Port number /// Timeout in milliseconds /// Time to wait in milliseconds to wait before checking again /// diff --git a/Selenium/NamespaceDoc.cs b/Selenium/NamespaceDoc.cs index 96b2d79..cbbd54f 100644 --- a/Selenium/NamespaceDoc.cs +++ b/Selenium/NamespaceDoc.cs @@ -1,37 +1,32 @@ using System.Runtime.CompilerServices; namespace Selenium { - + /// \mainpage /// - /// These are the namespace comments. + /// Selenium namespace of classes. /// + /// + /// There are two modes of operation: modern and legacy. + /// gecko driver works only in the modern mode since it only supports the modern endpoints. + /// chrome and edge drivers should also support the modern mode, but since they also support the legacy endpoints + /// they operate in the lagacy mode for compatibility reasons. + /// /// - /// VBA: - /// + /// /// Public Sub Script() - /// Dim driver As New FirefoxDriver + /// Dim driver As New ChromeDriver /// driver.Get "http://www.google.com" /// ... /// driver.Quit /// End Sub /// /// - /// VBScript: /// - /// Class Script /// Dim driver - /// - /// Sub Class_Initialize - /// Set driver = CreateObject("Selenium.FirefoxDriver") - /// driver.Get "http://www.google.com" - /// End Sub - /// - /// Sub Class_Terminate - /// driver.Quit - /// End Sub - /// End Class - /// - /// Set s = New Script + /// Set driver = CreateObject("Selenium.GeckoDriver") + /// driver.Get "http://www.google.com" + /// .......... + /// driver.Quit /// /// [CompilerGeneratedAttribute()] diff --git a/Selenium/Properties/AssemblyInfo.cs b/Selenium/Properties/AssemblyInfo.cs index 6489ecc..9ed8035 100644 --- a/Selenium/Properties/AssemblyInfo.cs +++ b/Selenium/Properties/AssemblyInfo.cs @@ -11,10 +11,10 @@ [assembly: AssemblyTitle("Selenium .Net/COM binding")] [assembly: AssemblyDescription("Selenium Type Library")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Florent BREHERET")] +[assembly: AssemblyCompany("SiMX corp.")] [assembly: AssemblyProduct("SeleniumBasic")] [assembly: AssemblyCopyright("")] -[assembly: AssemblyURL(@"https://github.com/florentbr/SeleniumBasic")] +[assembly: AssemblyURL(@"https://github.com/zc2com/SeleniumBasic")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -36,8 +36,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.0.0.0")] -[assembly: AssemblyFileVersion("3.0.0.0")] +[assembly: AssemblyVersion("3.0.0.1")] +[assembly: AssemblyFileVersion("3.0.0.1")] [assembly: TypeLibVersion(2, 0)] [assembly: NeutralResourcesLanguage("en-US")] diff --git a/Selenium/Selenium.csproj b/Selenium/Selenium.csproj index 92a2421..7fb8323 100644 --- a/Selenium/Selenium.csproj +++ b/Selenium/Selenium.csproj @@ -72,12 +72,13 @@ false 7.3 prompt + bin\x64\Debug\Selenium.xml bin\x64\Release\ TRACE true - bin\Release\Selenium.XML + bin\x64\Release\Selenium.XML pdbonly x64 false @@ -111,6 +112,8 @@ + + diff --git a/Selenium/Struct/Dictionary.cs b/Selenium/Struct/Dictionary.cs index 75320a1..d77a0a7 100644 --- a/Selenium/Struct/Dictionary.cs +++ b/Selenium/Struct/Dictionary.cs @@ -297,7 +297,10 @@ public DictionaryItem[] Items { return items; } } - + /// + /// see the Values property + /// + /// object[] ComInterfaces._Dictionary.Values() { return this.Values; } diff --git a/Selenium/Waiter.cs b/Selenium/Waiter.cs index aea6a51..a5e1ea0 100644 --- a/Selenium/Waiter.cs +++ b/Selenium/Waiter.cs @@ -1,5 +1,6 @@ using Selenium.Core; using Selenium.Internal; +using Selenium.ComInterfaces; using System; using System.ComponentModel; using System.IO; @@ -14,7 +15,7 @@ namespace Selenium { [Guid("0277FC34-FD1B-4616-BB19-7D30CBC3F6BB")] [ComVisible(true), ClassInterface(ClassInterfaceType.None)] [Description("Waiting functions to keep the visual basic editor from not responding")] - public class Waiter : ComInterfaces._Waiter { + public class Waiter : _Waiter { private DateTime? _endtime; private int _timeout = 5000; @@ -47,7 +48,7 @@ public int RetryDelay { /// Waits the given time in milliseconds /// /// Time to wait in milliseconde - void ComInterfaces._Waiter.Wait(int timems) { + void _Waiter.Wait(int timems) { SysWaiter.Wait(timems); } @@ -106,53 +107,35 @@ public T Until(Func func, int timeout = -1) { } /// - /// Waits for a procedure to set the result argument to true. - /// Procedure: + /// Waits for a function(a,b) to return a true-castable result. /// - /// Procedure - /// Argument + /// Function's reference. In VBScript use GetRef() + /// Function's first argument /// Timeout in ms - /// Timeout message - /// + /// function's actual result + /// Throws when time out has reached /// - /// VBA: /// - /// Public Sub WaitForTitle(driver, arg) - /// WaitForTitle = driver.Title = arg - /// End Sub - /// - /// Public Sub Script() - /// Dim Waiter As New Waiter - /// Dim driver As New FirefoxDriver - /// driver.Get "http://www.google.com/" - /// Waiter.Until AddressOf WaitForTitle, driver, "Google" - /// ... - /// End Sub - /// + /// Dim driver ' the called function has access to global variables /// - /// VBScript: - /// - /// Public Sub WaitForTitle(driver, arg) - /// WaitForTitle = driver.Title = arg - /// End Sub + /// Private Function WaitForTitle(expected_title, always_null) + /// WaitForTitle = driver.Title = expected_title + /// End Function /// /// Public Sub Script() /// Set Waiter = CreateObject("Selenium.Waiter") /// Set driver = CreateObject("Selenium.FirefoxDriver") /// driver.Get "http://www.google.com/" - /// Waiter.Until GetRef("WaitForTitle"), 5000, driver, "Google" - /// ... + /// Waiter.Until GetRef("WaitForTitle"), "Google", 5000 /// End Sub /// /// - object ComInterfaces._Waiter.Until(object procedure, object argument, int timeout, string timeoutMessage) { + object _Waiter.Until(object procedure, object argument, int timeout, string reserved) { if (timeout == -1) timeout = _timeout; return COMExt.WaitUntilProc(procedure, argument, null, timeout); } - - /// /// Waits the given time in milliseconds /// diff --git a/Selenium/WebDriver.cs b/Selenium/WebDriver.cs index def6eaa..79e712a 100644 --- a/Selenium/WebDriver.cs +++ b/Selenium/WebDriver.cs @@ -1,6 +1,7 @@ using Selenium.Core; using Selenium.Internal; using Selenium.Serializer; +using Selenium.ComInterfaces; using System; using System.Collections; using System.ComponentModel; @@ -15,32 +16,20 @@ namespace Selenium { /// /// VBScript: /// - /// Class Script - /// Dim driver - /// - /// Sub Class_Initialize - /// Set driver = CreateObject("Selenium.WebDriver") - /// driver.Start "firefox", "http://www.google.com" - /// driver.Get "/" - /// End Sub - /// - /// Sub Class_Terminate - /// driver.Quit - /// End Sub - /// End Class - /// - /// Set s = New Script + /// Dim driver + /// Set driver = CreateObject("Selenium.WebDriver") + /// driver.Start "firefox", "http://www.google.com" + /// driver.Get "/" + /// ... + /// driver.Quit /// /// - /// VBA: - /// - /// Public Sub Script() + /// /// Dim driver As New WebDriver /// driver.Start "firefox", "http://www.google.com" /// driver.Get "/" /// ... /// driver.Quit - /// End Sub /// /// [ProgId("Selenium.WebDriver")] @@ -51,7 +40,10 @@ public class WebDriver : SearchContext, ComInterfaces._WebDriver, IDisposable { const string RUNNING_OBJECT_NAME = "Selenium.WebDriver"; - public static bool LEGACY = true; // but Gecko driver does not support many endpoints and payloads previously used here + // New API https://w3c.github.io/webdriver/#endpoints are used with + // Gecko driver which does not support many endpoints and payloads used in previous implementation + // Other drivers still used in the legacy mode + internal static bool LEGACY = true; internal Capabilities Capabilities = new Capabilities(); internal Dictionary Preferences = new Dictionary(); @@ -123,22 +115,23 @@ public Proxy Proxy { /// - /// Set a specific profile for the firefox webdriver + /// Set a specific profile directory or a named profile for the firefox webdriver /// - /// Profil name (Firefox only) or directory (Firefox and Chrome) + /// Profile name (Firefox only) or directory (Edge, Chrome) /// If true, the browser will be launched without a copy the profile (Firefox only) - /// The profile directory can be copied from the user temp folder (run %temp%) before the WebDriver is stopped. - /// It's also possible to create a new Firefox profile by launching firefox with the "-p" switch (firefox.exe -p). + /// + /// The profile directory cannot be a symlink or an UNC path. It has to be a real physical local directory. + /// It's possible to create a new Firefox profile by launching firefox with the "-p" switch (firefox.exe -p). /// - /// + /// /// Dim driver As New Selenium.FirefoxDriver - /// driver.SetProfile "Selenium" 'Firefox only. Profile created by running "..\firefox.exe -p" + /// driver.SetProfile "C:\MyProfil" ' the directory of a persistant profile /// driver.Get "http://www.google.com" /// ... /// - /// + /// /// Dim driver As New Selenium.FirefoxDriver - /// driver.SetProfile "C:\MyProfil" 'For Chrome and Firefox only + /// driver.SetProfile "Selenium" 'Firefox only. Profile created by running "..\firefox.exe -p" /// driver.Get "http://www.google.com" /// ... /// @@ -214,7 +207,7 @@ internal override string uri { /// /// Starts a new Selenium testing session /// - /// Name of the browser : firefox, ie, chrome, phantomjs + /// Name of the browser: chrome, edge, gecko, firefox, ie, phantomjs, opera /// The base URL /// /// @@ -223,6 +216,14 @@ internal override string uri { /// driver.Get "/" /// /// + /// + /// firefox is an old browser version driver which works via the plugin firefoxdriver.xpi + /// gecko is for a modern FireFox browser. + /// + /// edge and chrome require the driver version to be exactly matched the current browser version + /// + /// ie, phantomjs, opera drivers were not tested in this release and thus could be considered as not supported + /// public void Start(string browser = null, string baseUrl = null) { try { browser = ExpendBrowserName(browser); @@ -269,12 +270,12 @@ public void Start(string browser = null, string baseUrl = null) { } /// - /// Starts remotely a new Selenium testing session + /// Starts a new Selenium session attached to a remotely started driver process /// /// Remote executor address (ex : "http://localhost:4444/wd/hub") /// Name of the browser : firefox, ie, chrome, phantomjs, htmlunit, htmlunitwithjavascript, android, ipad, opera - /// Browser version - /// Platform: WINDOWS, LINUX, MAC, ANDROID... + /// Optional Browser version + /// Optional Platform: WINDOWS, LINUX, MAC, ANDROID... /// /// /// Dim driver As New WebDriver() @@ -282,6 +283,13 @@ public void Start(string browser = null, string baseUrl = null) { /// driver.Get "/" /// /// + /// + /// This could be useful for debugging. Start the driver manually in the verbose mode, like: + /// geckodriver.exe -vv + /// or + /// chromedriver.exe --verbose + /// A custom connection port could be specified as a driver command line parameter + /// public void StartRemotely(string executorUri, string browser = null, string version = null, string platform = null) { try { browser = ExpendBrowserName(browser); @@ -382,7 +390,7 @@ private string ExpendBrowserName(string browser) { } /// - /// Sends a customized command + /// Sends a custom command to the driver process /// /// POST, GET or DELETE /// Relative URI. Ex: "/screenshot" @@ -397,8 +405,14 @@ private string ExpendBrowserName(string browser) { /// Result /// /// + /// ' collect all links of the page /// Set links = driver.Send("POST", "/elements", "using", "css selector", "value", "a") /// + /// + /// ' "print" the page and get a binary as a result (works only in gecko) + /// dim base64 + /// base64 = driver.Send( "POST", "/print", "shrinkToFit", true ) + /// /// public object Send(string method, string relativeUri, string param1 = null, object value1 = null, @@ -440,7 +454,7 @@ public object Send(string method, string relativeUri, #region Interfaces /// - /// Manage the browser settings. Need to be defined before the browser is launched + /// Manage the . Need to be defined before the browser is launched /// public Timeouts Timeouts { get { @@ -449,7 +463,7 @@ public Timeouts Timeouts { } /// - /// Instructs the driver to change its settings. + /// Instructs the driver to its settings. /// public Manage Manage { get { @@ -458,7 +472,7 @@ public Manage Manage { } /// - /// Get the actions class + /// Get the class /// /// /// @@ -475,7 +489,7 @@ public Actions Actions { } /// - /// TouchActions + /// Get the class /// public TouchActions TouchActions { get { @@ -485,7 +499,7 @@ public TouchActions TouchActions { } /// - /// Keyboard + /// Keyboard /// public Keyboard Keyboard { get { @@ -494,7 +508,7 @@ public Keyboard Keyboard { } /// - /// Mouse + /// /// public Mouse Mouse { get { @@ -503,7 +517,7 @@ public Mouse Mouse { } /// - /// TouchScreen + /// /// public TouchScreen TouchScreen { get { @@ -512,7 +526,7 @@ public TouchScreen TouchScreen { } /// - /// Keys + /// /// public Keys Keys { get { @@ -538,12 +552,13 @@ public string BaseUrl { } /// - /// Loads a web page in the current browser session. Same as Open method. + /// Performs a GET request to load a web page in the current browser session. /// - /// URL - /// Optional timeout in milliseconds. Infinite=-1 + /// URL (could be relative, see ) + /// Optional timeout in milliseconds. see /// Optional - Raise an exception after the timeout when true - /// Return true if the url was openned within the timeout, false otherwise + /// Thrown when the timeout has reached + /// Return true if the url was openned within the timeout, false otherwise (when the raise param was false) public bool Get(string url, int timeout = -1, bool raise = true) { if (_session == null) this.Start(); @@ -650,6 +665,7 @@ public Window Window { /// /// Gets the window handles of open browser windows. /// + /// Try to not overuse, since the windows enumeration is a heavy process. /// public List Windows { get { @@ -715,9 +731,9 @@ public Image TakeScreenshot(int delay = 0) { /// /// Execute a piece of JavaScript in the context of the currently selected frame or window /// - /// The JavaScript code to execute. - /// The arguments to the script. - /// The value specified by the return statement. + /// The JavaScript code to execute in the page context. + /// The arguments array available to the script. + /// The value specified by the script's return statement. /// /// /// txt = driver.ExecuteScript("return 'xyz' + arguments[0];", "123") @@ -732,10 +748,10 @@ public object ExecuteScript(string script, object arguments = null) { /// /// Execute an asynchronous piece of JavaScript in the context of the current frame or window. /// - /// Piece of JavaScript code to execute. - /// Optional arguments for the script. + /// JavaScript code which involves asynchronous operations such as promises, etc. + /// Optional arguments array available for the script. /// Optional timeout in milliseconds. - /// The first argument of the callback function. + /// The first argument of the function callback() the script should call to return the result. /// /// /// txt = driver.ExecuteAsyncScript("callback('xyz')");" @@ -754,6 +770,9 @@ public object ExecuteAsyncScript(string script, object arguments = null, int tim /// Optional arguments for the script. /// Optional timeout in milliseconds. /// Value not null + /// + /// driver.WaitForScript "document.readyState == 'complete';" + /// public object WaitForScript(string script, object arguments, int timeout = -1) { object args_ex = FormatArguments(arguments); object result = session.javascript.WaitFor(script, args_ex, timeout); @@ -859,7 +878,7 @@ public Window SwitchToWindowByName(string name, int timeout = -1, bool raise = t /// The title of the window to activate /// Optional timeout in milliseconds /// Optional - Raise an exception after the timeout when true - /// Current web driver + /// public Window SwitchToWindowByTitle(string title, int timeout = -1, bool raise = true) { try { return session.windows.SwitchToWindowByTitle(title, timeout); @@ -875,7 +894,7 @@ public Window SwitchToWindowByTitle(string title, int timeout = -1, bool raise = /// /// Optional timeout in milliseconds /// Optional - Raise an exception after the timeout when true. Default is true. - /// Window + /// public Window SwitchToNextWindow(int timeout = -1, bool raise = true) { try { return session.windows.SwitchToNextWindow(timeout); @@ -1010,7 +1029,7 @@ public T Until(Func func, int timeout = -1) { /// End Sub /// /// - object ComInterfaces._WebDriver.Until(object procedure, object argument, int timeout) { + object _WebDriver.Until(object procedure, object argument, int timeout) { if (timeout == -1) timeout = session.timeouts.timeout_implicitwait; return COMExt.WaitUntilProc(procedure, this, argument, timeout); diff --git a/regasm.bat b/regasm.bat new file mode 100755 index 0000000..a9261d5 --- /dev/null +++ b/regasm.bat @@ -0,0 +1,4 @@ +@echo OFF +if "%1"=="" echo Need a parameter - the directory where the Selenium.dll is resided & exit /B +%SystemRoot%\Microsoft.NET\Framework64\v4.0.30319\regasm.exe %1\Selenium.dll /codebase + From ea9345ea93a33325c8bafd843016668d23608bf2 Mon Sep 17 00:00:00 2001 From: zc2 Date: Tue, 22 Nov 2022 10:02:06 -0500 Subject: [PATCH 14/16] Added access to driver's Implicit timer and changed gecko's capabilities schema from depricated to modern --- Selenium.Tests/Internals/BaseBrowsers.cs | 5 ++ Selenium.Tests/TS_Scraping.cs | 19 ++++++- Selenium.Tests/TS_SearchContext.cs | 43 +++++++++++---- Selenium.Tests/TS_Windows.cs | 2 +- Selenium/ComInterfaces/_Timeouts.cs | 5 +- Selenium/Common/Capabilities.cs | 21 +++++--- Selenium/Common/SearchContext.cs | 5 +- Selenium/Common/Timeouts.cs | 61 +++++++++++++++++---- Selenium/Core/DriverService.cs | 8 ++- Selenium/Core/RemoteSession.cs | 19 ++++--- Selenium/Core/SysWaiter.cs | 7 +++ Selenium/Drivers/EdgeDriver.cs | 23 +++----- Selenium/Drivers/GeckoDriver.cs | 32 +++++------ Selenium/Internal/EndPointExt.cs | 4 +- Selenium/Waiter.cs | 1 + Selenium/WebDriver.cs | 68 ++++++++++++++---------- Setup/Setup.vdproj | 60 +++++++++++++++------ 17 files changed, 257 insertions(+), 126 deletions(-) diff --git a/Selenium.Tests/Internals/BaseBrowsers.cs b/Selenium.Tests/Internals/BaseBrowsers.cs index 89e045c..2418ae7 100644 --- a/Selenium.Tests/Internals/BaseBrowsers.cs +++ b/Selenium.Tests/Internals/BaseBrowsers.cs @@ -84,11 +84,16 @@ private WebDriver GetBrowserWithDriverStartedRemotely(Browser browser) { break; default: throw new Exception("Browser not supported: " + browser.ToString()); } + try { driver_process = Process.Start(p_info); NUnit.Framework.Assert.False( driver_process.HasExited, "Driver process cannot start: " + browser.ToString() ); WebDriver s = new Selenium.WebDriver(); s.StartRemotely( "http://localhost:4444/", browser.ToString().ToLower() ); return s; + } catch( Exception e ) { + NUnit.Framework.Assert.Fail( "Thrown exception: " + e.Message ); + } + return null; } } } diff --git a/Selenium.Tests/TS_Scraping.cs b/Selenium.Tests/TS_Scraping.cs index ea0d158..da86ac4 100644 --- a/Selenium.Tests/TS_Scraping.cs +++ b/Selenium.Tests/TS_Scraping.cs @@ -6,7 +6,7 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Gecko)] + [TestFixture(Browser.Gecko, Category = "InFocus")] [TestFixture(Browser.Chrome)] [TestFixture(Browser.Edge)] /* @@ -36,6 +36,23 @@ public void ShouldScrapTextFromElements() { var data = elements.Text(); A.AreEqual(data[0], "Table Heading 1"); } + + [TestCase] +// [IgnoreFixture(Browser.Gecko, "Not supported")] + public void ShouldGetBrowserLog() { + driver.Get("/notexisting.html"); + Logs l = driver.Manage.Logs; + List sl = l.Browser; + A.IsNotNull( sl ); + } + [TestCase] + [IgnoreFixture(Browser.Gecko, "Not supported")] + public void ShouldGetDriverLog() { + driver.Get("/notexisting.html"); + Logs l = driver.Manage.Logs; + List sl = l.Driver; + A.IsNotNull( sl ); + } } } diff --git a/Selenium.Tests/TS_SearchContext.cs b/Selenium.Tests/TS_SearchContext.cs index 8bebfef..16d1b7c 100644 --- a/Selenium.Tests/TS_SearchContext.cs +++ b/Selenium.Tests/TS_SearchContext.cs @@ -138,23 +138,45 @@ public void ShouldFindElementsByAny() { A.AreEqual(3, elts.Count); } + private void AddNewElementAfter( int delay ) { + driver.ExecuteScript(@"setTimeout(function(){ + var e = document.createElement('div'); + e.id = 'id-append'; + e.innerHTML = 'DivAppend'; + document.body.appendChild(e); + }, " + delay + ");"); + } + [TestCase] - public void ShouldFindWithImplicitWait() { - driver.ExecuteScript(@" -setTimeout(function(){ - var e = document.createElement('div'); - e.id = 'id-append'; - e.innerHTML = 'DivAppend'; - document.body.appendChild(e); -}, 100); - "); + public void ShouldFindWithExplicitWait() { + AddNewElementAfter( 200 ); var ele = driver.FindElementById("id-append", 1500, true); } + [TestCase] + public void ShouldFindWithImplicitWait() { + AddNewElementAfter( 200 ); + var ele = driver.FindElementById("id-append"); + A.IsNotNull(ele); + } + + [TestCase] + [IgnoreFixture(Browser.Gecko, "Implicit timeout is always 0 and cannot be changed")] + public void ShouldFindWithDriverImplicit() { + const int implicit_timeout = 500; + driver.Timeouts.Implicit = implicit_timeout; + if( Fixture.Equals( Browser.Firefox ) ) + A.Ignore( "Timeout can be only set but not get" ); + else + A.AreEqual( implicit_timeout, driver.Timeouts.Implicit, "Cannot set the implicit timeout to the driver" ); + AddNewElementAfter( 200 ); + var ele = driver.FindElementById("id-append", 1, true); + } + [TestCase] [ExpectedException(typeof(Selenium.Errors.NoSuchElementError))] public void ShouldNotFind() { - var ele = driver.FindElementById("id-append", 5000, true); + var ele = driver.FindElementById("id-missing", 5000, true); } [TestCase] @@ -178,5 +200,4 @@ public void ShouldGetThroughShadow() { A.Throws( () => sr.FindElementByXPath(".//span") ); // apparently not supported } } - } diff --git a/Selenium.Tests/TS_Windows.cs b/Selenium.Tests/TS_Windows.cs index 7af79ac..316ab97 100644 --- a/Selenium.Tests/TS_Windows.cs +++ b/Selenium.Tests/TS_Windows.cs @@ -8,7 +8,7 @@ namespace Selenium.Tests { [TestFixture(Browser.Firefox)] - [TestFixture(Browser.Gecko, Category = "InFocus")] + [TestFixture(Browser.Gecko)] [TestFixture(Browser.Chrome)] [TestFixture(Browser.Edge)] /* diff --git a/Selenium/ComInterfaces/_Timeouts.cs b/Selenium/ComInterfaces/_Timeouts.cs index b6d8122..65e1a8e 100644 --- a/Selenium/ComInterfaces/_Timeouts.cs +++ b/Selenium/ComInterfaces/_Timeouts.cs @@ -9,9 +9,12 @@ namespace Selenium.ComInterfaces { [ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] public interface _Timeouts { - [DispId(476), Description("Amount of time that Selenium will wait for waiting commands to complete")] + [DispId(476), Description("Default amount of time that Selenium waits for search and wait operations.")] int ImplicitWait { get; set; } + [DispId(477), Description("Amount of time the driver waits when locating an element.")] + int Implicit { get; set; } + [DispId(459), Description("Amount of time the driver should wait while loading a page before throwing an exception.")] int PageLoad { get; set; } diff --git a/Selenium/Common/Capabilities.cs b/Selenium/Common/Capabilities.cs index eb72069..8e9def1 100644 --- a/Selenium/Common/Capabilities.cs +++ b/Selenium/Common/Capabilities.cs @@ -11,14 +11,15 @@ public class Capabilities : Dictionary, ComInterfaces._Dictionary { const string KEY_BROWSER_NAME = "browserName"; const string KEY_BROWSER_VERSION = "browserVersion"; const string KEY_PLATFORM = "platform"; + const string KEY_PLATFORM_NAME = "platformName"; const string KEY_NATIVE_EVENTS = "nativeEvents"; - const string KEY_ACCEPT_SSL_CERTIFICATES = "acceptSslCerts"; + const string KEY_ACCEPT_SSL_CERTS = "acceptSslCerts"; + const string KEY_ACCEPT_INSECURE_CERTS = "acceptInsecureCerts"; const string KEY_ALERT_BEHAVIOUR = "unexpectedAlertBehaviour"; const string KEY_PAGE_LOAD_STRATEGY = "pageLoadStrategy"; public Capabilities(){ base.Add(KEY_PAGE_LOAD_STRATEGY, "normal"); - base.Add(KEY_ALERT_BEHAVIOUR, "ignore"); } public new object this[string key] { @@ -64,10 +65,13 @@ public string BrowserVersion { /// public string Platform { get { - return base.GetValue(KEY_PLATFORM, "ANY"); + return base.GetValue(WebDriver.LEGACY ? KEY_PLATFORM : KEY_PLATFORM_NAME, "ANY"); } set { - base[KEY_PLATFORM] = string.IsNullOrEmpty(value) ? "ANY" : value; + if( WebDriver.LEGACY ) + base[KEY_PLATFORM] = string.IsNullOrEmpty(value) ? "ANY" : value; + else + base[KEY_PLATFORM_NAME] = string.IsNullOrEmpty(value) ? "windows" : value; } } @@ -88,22 +92,23 @@ public bool EnableNativeEvents { /// public bool AcceptUntrustedCertificates { get { - return base.GetValue(KEY_ACCEPT_SSL_CERTIFICATES, false); + return base.GetValue(WebDriver.LEGACY ? KEY_ACCEPT_SSL_CERTS : KEY_ACCEPT_INSECURE_CERTS, false); } set { - base[KEY_ACCEPT_SSL_CERTIFICATES] = value; + base[WebDriver.LEGACY ? KEY_ACCEPT_SSL_CERTS : KEY_ACCEPT_INSECURE_CERTS] = value; } } /// - /// Unexpected alert behaviour + /// Unexpected alert behaviour. Legacy only /// public string UnexpectedAlertBehaviour { get { return base.GetValue(KEY_ALERT_BEHAVIOUR, string.Empty); } set { - base[KEY_ALERT_BEHAVIOUR] = value; + if( WebDriver.LEGACY ) + base[KEY_ALERT_BEHAVIOUR] = value; } } diff --git a/Selenium/Common/SearchContext.cs b/Selenium/Common/SearchContext.cs index 851abd5..52fece7 100644 --- a/Selenium/Common/SearchContext.cs +++ b/Selenium/Common/SearchContext.cs @@ -43,7 +43,7 @@ public void WaitNotElement(By by, int timeout = -1) { /// /// The mechanism by which to find the elements. /// The value to use to search for the elements. - /// Optional timeout in milliseconds + /// Optional timeout in milliseconds. When omitted the Timeouts.ImplicitWait value will be used /// /// WebElement public WebElement FindElementBy(Strategy strategy, string value, int timeout = -1, bool raise = true) { @@ -313,8 +313,7 @@ private WebElement FindFirstElement(Strategy strategy, string value, int timeout if (timeout == 0) throw; if( timeout == -1 ) timeout = session.timeouts.timeout_implicitwait; - int time_chunk = timeout / 10; - if( time_chunk < 50 ) time_chunk = 50; + int time_chunk = SysWaiter.GetTimeChunk( timeout ); var endTime = session.GetEndTime(timeout); while (true) { SysWaiter.Wait( time_chunk ); diff --git a/Selenium/Common/Timeouts.cs b/Selenium/Common/Timeouts.cs index 23cd647..d0efdf0 100644 --- a/Selenium/Common/Timeouts.cs +++ b/Selenium/Common/Timeouts.cs @@ -1,34 +1,40 @@ -using Selenium.Core; +using System; +using Selenium.Core; using System.ComponentModel; using System.Runtime.InteropServices; namespace Selenium { /// - /// Timeouts object + /// Timeouts used in in waiting /// /// - /// Sets the implicit timout to 1 second + /// Sets the deault operational time out to 1 second: /// /// driver.Timeouts.ImplicitWait = 1000 /// + /// Tells the driver process to set its element location timeout to 1 second: + /// + /// driver.Timeouts.Implicit = 1000 + /// /// [ProgId("Selenium.Timeouts")] [Guid("0277FC34-FD1B-4616-BB19-44A424DB3F50")] [Description("Timeouts management")] [ComVisible(true), ClassInterface(ClassInterfaceType.None)] public class Timeouts : ComInterfaces._Timeouts { + private const String ENDPOINT = "/timeouts"; - private static void SendTimeoutScript(RemoteSession session, int timeout) { - session.Send(RequestMethod.POST, "/timeouts", "type", "script", "ms", timeout); + internal static void SendTimeoutScript(RemoteSession session, int timeout) { + session.Send(RequestMethod.POST, ENDPOINT, "type", "script", "ms", timeout); } - private static void SendTimeoutPageLoad(RemoteSession session, int timeout) { - session.Send(RequestMethod.POST, "/timeouts", "type", "page load", "ms", timeout); + internal static void SendTimeoutPageLoad(RemoteSession session, int timeout) { + session.Send(RequestMethod.POST, ENDPOINT, "type", "page load", "ms", timeout); } - private static void SendTimeoutImplicit(RemoteSession session, int timeout) { - session.Send(RequestMethod.POST, "/timeouts", "type", "implicit", "ms", timeout); + internal static void SendTimeoutImplicit(RemoteSession session, int timeout) { + session.Send(RequestMethod.POST, ENDPOINT, "type", "implicit", "ms", timeout); } @@ -42,6 +48,12 @@ private static void SendTimeoutImplicit(RemoteSession session, int timeout) { /// /// Amount of time that Selenium will wait for commands to complete. Default is 3000ms /// + /// + /// This timeout is not linked to the driver process + /// implicit timeout. + /// It just a SeleniumBasic's default time for an element search operation would continue repeating a search requests + /// until the timeout is reached. + /// /// Default is 3000ms public int ImplicitWait { get { @@ -53,12 +65,37 @@ public int ImplicitWait { } } + /// + /// Amount of time the driver process should wait to complete when locating an element. + /// + /// + /// Default is 0ms. See implicit timeout + /// + public int Implicit { + get { + try { + Dictionary dict = (Dictionary)_session.Send(RequestMethod.GET, ENDPOINT); + if( dict != null ) + return Convert.ToInt32(dict["implicit"]); + } catch { } + return -1; + } + set { + if (_session != null) + SendTimeoutImplicit(_session, value); + } + } + /// /// Amount of time the driver should wait while loading a page before throwing an exception. /// /// Default is 60000ms public int PageLoad { get { + Dictionary dict = (Dictionary)_session.Send(RequestMethod.GET, ENDPOINT); + if( dict != null ) { + timeout_pageload = Convert.ToInt32(dict["pageLoad"]); + } return timeout_pageload; } set { @@ -75,6 +112,10 @@ public int PageLoad { /// Default is 15000ms public int Script { get { + Dictionary dict = (Dictionary)_session.Send(RequestMethod.GET, ENDPOINT); + if( dict != null ) { + timeout_script = Convert.ToInt32(dict["script"]); + } return timeout_script; } set { @@ -102,7 +143,7 @@ public int Server { internal void SetSession(RemoteSession session) { _session = session; SendTimeoutPageLoad(_session, timeout_pageload); - SendTimeoutScript(_session, timeout_pageload); + SendTimeoutScript(_session, timeout_script); } } diff --git a/Selenium/Core/DriverService.cs b/Selenium/Core/DriverService.cs index 817aeaf..35c4495 100644 --- a/Selenium/Core/DriverService.cs +++ b/Selenium/Core/DriverService.cs @@ -85,9 +85,13 @@ public void Start(string filename, bool hide = true) { string servicePath = Path.Combine(_library_dir, filename); if (!File.Exists(servicePath)) throw new Errors.FileNotFoundError(servicePath); - +#if DEBUG + const bool noWindow = false; +#else + const bool noWindow = true; +#endif //Start the process - _process = ProcessExt.Start(servicePath, _arguments, null, env, true, true); + _process = ProcessExt.Start(servicePath, _arguments, null, env, noWindow, true); //Waits for the port to be listening if (!_endpoint.WaitForListening(10000, 150)) diff --git a/Selenium/Core/RemoteSession.cs b/Selenium/Core/RemoteSession.cs index b9028e9..72362ec 100644 --- a/Selenium/Core/RemoteSession.cs +++ b/Selenium/Core/RemoteSession.cs @@ -41,14 +41,21 @@ public string Id { /// /// Starts a new session. /// - /// An object describing the session's desired capabilities. - /// An object describing the session's required capabilities (Optional). - /// {object} An object describing the session's capabilities. + /// An object with the session's desired capabilities. + /// An object with the session's required capabilities (Optional). + /// When session start failed public void Start(Dictionary desired_capabilities, Dictionary requiredCapabilities = null) { var param = new Dictionary(); - param.Add("desiredCapabilities", desired_capabilities); - if (requiredCapabilities != null) - param.Add("requiredCapabilities", requiredCapabilities); + if( WebDriver.LEGACY ) { + param.Add("desiredCapabilities", desired_capabilities); + if (requiredCapabilities != null) + param.Add("requiredCapabilities", requiredCapabilities); + } else { + // see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities#Legacy + var capabilities = new Dictionary(); + capabilities.Add( "alwaysMatch", desired_capabilities ); + param.Add("capabilities", capabilities); + } var response = (Dictionary)server.Send(RequestMethod.POST, "/session", param); try { diff --git a/Selenium/Core/SysWaiter.cs b/Selenium/Core/SysWaiter.cs index c6a5ceb..e1bd2f7 100644 --- a/Selenium/Core/SysWaiter.cs +++ b/Selenium/Core/SysWaiter.cs @@ -11,6 +11,7 @@ namespace Selenium.Core { class SysWaiter { const int DEFAULT_WAIT_TIME = 50; + const int TIMEOUT_DIVIDER = 10; //Modifier keys const int MOD_NONE = 0x00; @@ -88,6 +89,12 @@ public static void Wait(int timems) { HotKeyGlobal.UnsubscribeAll(); } + internal static int GetTimeChunk( int full_timeout ) { + int time_chunk = full_timeout / SysWaiter.TIMEOUT_DIVIDER; + if( time_chunk < DEFAULT_WAIT_TIME ) time_chunk = DEFAULT_WAIT_TIME; + return time_chunk; + } + } } diff --git a/Selenium/Drivers/EdgeDriver.cs b/Selenium/Drivers/EdgeDriver.cs index f9a51a2..9e03a55 100644 --- a/Selenium/Drivers/EdgeDriver.cs +++ b/Selenium/Drivers/EdgeDriver.cs @@ -10,32 +10,21 @@ namespace Selenium { /// - /// Web driver for Microsoft Edge driver + /// Web driver client for Microsoft Edge driver process /// + /// + /// Note, the driver process image name is expected to be edgedriver.exe , not msedgedriver.exe + /// /// /// - /// - /// VBScript: /// - /// Class Script - /// Dim driver - /// - /// Sub Class_Initialize + /// Dim driver /// Set driver = CreateObject("Selenium.EdgeDriver") /// driver.Get "http://www.google.com" /// ... - /// End Sub - /// - /// Sub Class_Terminate /// driver.Quit - /// End Sub - /// End Class - /// - /// Set s = New Script /// - /// - /// VBA: - /// + /// /// Public Sub Script() /// Dim driver As New EdgeDriver /// driver.Get "http://www.mywebsite.com" diff --git a/Selenium/Drivers/GeckoDriver.cs b/Selenium/Drivers/GeckoDriver.cs index 7f2ea92..6cbb8ee 100755 --- a/Selenium/Drivers/GeckoDriver.cs +++ b/Selenium/Drivers/GeckoDriver.cs @@ -10,32 +10,19 @@ namespace Selenium { /// - /// Web driver for Gecko driver + /// Web driver client for Gecko driver process /// /// /// - /// - /// VBScript: /// - /// Class Script - /// Dim driver - /// - /// Sub Class_Initialize + /// Dim driver /// Set driver = CreateObject("Selenium.GeckoDriver") /// driver.Get "http://www.google.com" /// ... - /// End Sub - /// - /// Sub Class_Terminate /// driver.Quit - /// End Sub - /// End Class /// - /// Set s = New Script /// - /// - /// VBA: - /// + /// /// Public Sub Script() /// Dim driver As New GeckoDriver /// driver.Get "http://www.mywebsite.com" @@ -70,14 +57,16 @@ internal static IDriverService StartService(WebDriver wd) { return svc; } + // see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions internal static void ExtendCapabilities(WebDriver wd, bool remote) { WebDriver.LEGACY = false; Capabilities capa = wd.Capabilities; + capa.BrowserName = "firefox"; // instead of "gecko"! Dictionary opts; - if (!capa.TryGetValue("moz:firefoxOptions", out opts)) - capa["moz:firefoxOptions"] = opts = new Dictionary(); - + const string KEY_FF_OPTS = "moz:firefoxOptions"; + if (!capa.TryGetValue(KEY_FF_OPTS, out opts)) + capa[KEY_FF_OPTS] = opts = new Dictionary(); if (wd.Profile != null) { if (IOExt.IsPath(wd.Profile)) { @@ -92,6 +81,11 @@ internal static void ExtendCapabilities(WebDriver wd, bool remote) { } if (wd.Arguments.Count != 0) opts["args"] = wd.Arguments; +/* + Dictionary log = new Dictionary(); + log.Add( "level", "trace" ); + opts.Add( "log", log ); +*/ } } diff --git a/Selenium/Internal/EndPointExt.cs b/Selenium/Internal/EndPointExt.cs index 5af12c4..096acb7 100644 --- a/Selenium/Internal/EndPointExt.cs +++ b/Selenium/Internal/EndPointExt.cs @@ -17,7 +17,7 @@ public static EndPointExt Create(IPAddress address, bool disableInheritence) { socket.NoDelay = true; socket.ReceiveBufferSize = 0; socket.SendBufferSize = 0; - socket.Bind(new IPEndPoint(address, 0)); + socket.Bind(new IPEndPoint(address, 0)); // any available port //Disable inheritance to the child processes so the main process can close the //socket once a child process is launched. @@ -97,7 +97,7 @@ public bool WaitForConnectable(int timeout, int delay) { /// /// Timeout in milliseconds /// Time to wait in milliseconds to wait before checking again - /// + /// True on detecting the port is being listening public bool WaitForListening(int timeout, int delay) { _socket.Close(); diff --git a/Selenium/Waiter.cs b/Selenium/Waiter.cs index a5e1ea0..c0c197c 100644 --- a/Selenium/Waiter.cs +++ b/Selenium/Waiter.cs @@ -112,6 +112,7 @@ public T Until(Func func, int timeout = -1) { /// Function's reference. In VBScript use GetRef() /// Function's first argument /// Timeout in ms + /// Not in use /// function's actual result /// Throws when time out has reached /// diff --git a/Selenium/WebDriver.cs b/Selenium/WebDriver.cs index 79e712a..727c79e 100644 --- a/Selenium/WebDriver.cs +++ b/Selenium/WebDriver.cs @@ -121,11 +121,14 @@ public Proxy Proxy { /// If true, the browser will be launched without a copy the profile (Firefox only) /// /// The profile directory cannot be a symlink or an UNC path. It has to be a real physical local directory. - /// It's possible to create a new Firefox profile by launching firefox with the "-p" switch (firefox.exe -p). + /// + /// + /// It's possible to pre-create a new Firefox profile by launching firefox with the "-p" switch (firefox.exe -p). + /// /// /// /// Dim driver As New Selenium.FirefoxDriver - /// driver.SetProfile "C:\MyProfil" ' the directory of a persistant profile + /// driver.SetProfile "C:\MyProfile" ' the directory of a persistant profile /// driver.Get "http://www.google.com" /// ... /// @@ -205,18 +208,28 @@ internal override string uri { } /// - /// Starts a new Selenium testing session + /// Starts a new Selenium session /// /// Name of the browser: chrome, edge, gecko, firefox, ie, phantomjs, opera /// The base URL + /// When the session start failed /// - /// + /// /// Dim driver As New WebDriver() - /// driver.Start "firefox", "http://www.google.com" + /// driver.Start "gecko", "http://www.google.com" /// driver.Get "/" /// + /// + /// Dim driver + /// Set driver = CreateObject("Selenium.WebDriver") + /// driver.Start "gecko" + /// driver.Get "http://www.google.com/" + /// /// /// + /// This method executes the driver process. The driver executable has to be in the same folder as + /// the registered SeleniumBasic.dll assembly + /// /// firefox is an old browser version driver which works via the plugin firefoxdriver.xpi /// gecko is for a modern FireFox browser. /// @@ -252,8 +265,10 @@ public void Start(string browser = null, string baseUrl = null) { default: throw new Errors.ArgumentError("Invalid browser name: {0}", browser); } - - this.Capabilities.BrowserName = browser; + if (string.IsNullOrEmpty(this.Capabilities.BrowserName)) + this.Capabilities.BrowserName = browser; + if( LEGACY ) + this.Capabilities.UnexpectedAlertBehaviour = "ignore"; RegisterRunningObject(); @@ -273,22 +288,22 @@ public void Start(string browser = null, string baseUrl = null) { /// Starts a new Selenium session attached to a remotely started driver process /// /// Remote executor address (ex : "http://localhost:4444/wd/hub") - /// Name of the browser : firefox, ie, chrome, phantomjs, htmlunit, htmlunitwithjavascript, android, ipad, opera + /// Name of the browser: gecko, firefox, chrome, edge, ie, phantomjs, htmlunit, htmlunitwithjavascript, android, ipad, opera /// Optional Browser version /// Optional Platform: WINDOWS, LINUX, MAC, ANDROID... /// - /// + /// /// Dim driver As New WebDriver() - /// driver.StartRemotely "http://localhost:4444/wd/hub", "ie", 11 + /// driver.StartRemotely "http://localhost:4444/wd/hub" /// driver.Get "/" /// /// /// - /// This could be useful for debugging. Start the driver manually in the verbose mode, like: + /// This could be useful for debugging. Start the driver process manually in the verbose mode, like: /// geckodriver.exe -vv /// or /// chromedriver.exe --verbose - /// A custom connection port could be specified as a driver command line parameter + /// A custom connection port also could be specified as a driver's command line parameter /// public void StartRemotely(string executorUri, string browser = null, string version = null, string platform = null) { try { @@ -318,9 +333,11 @@ public void StartRemotely(string executorUri, string browser = null, string vers } this.Capabilities.Platform = platform; - this.Capabilities.BrowserName = browser; + if (string.IsNullOrEmpty(this.Capabilities.BrowserName)) + this.Capabilities.BrowserName = browser; if (!string.IsNullOrEmpty(version)) this.Capabilities.BrowserVersion = version; + this.Capabilities.UnexpectedAlertBehaviour = "ignore"; _session = new RemoteSession(executorUri, false, this.timeouts); _session.Start(this.Capabilities); @@ -566,12 +583,6 @@ public bool Get(string url, int timeout = -1, bool raise = true) { if (string.IsNullOrEmpty(url)) throw new Errors.ArgumentError("Argument 'url' cannot be null."); - - if (timeout > 0){ - session.timeouts.PageLoad = timeout; - session.Send(RequestMethod.POST, "/timeouts", "type", "page load", "ms", timeout); - } - int idx = url.IndexOf("/"); if (idx == 0) { //relative url @@ -588,6 +599,9 @@ public bool Get(string url, int timeout = -1, bool raise = true) { } } + if (timeout > 0){ + Timeouts.SendTimeoutPageLoad(session, timeout); + } try { session.Send(RequestMethod.POST, "/url", "url", url); return true; @@ -595,6 +609,10 @@ public bool Get(string url, int timeout = -1, bool raise = true) { if (raise) throw; return false; + } finally { + if (timeout > 0){ + Timeouts.SendTimeoutPageLoad(session, session.timeouts.timeout_pageload); + } } } @@ -1000,8 +1018,8 @@ public T Until(Func func, int timeout = -1) { /// Optional - Argument to send to the function /// Optional - timeout in milliseconds /// Current WebDriver - /// VBA example: - /// + /// + /// /// Sub WaitForTitle(driver, argument, result) /// result = driver.Title = argument /// End Sub @@ -1013,20 +1031,14 @@ public T Until(Func func, int timeout = -1) { /// ... /// End Sub /// - /// - /// - /// VBScript example: - /// + /// /// Function WaitForTitle(driver, argument) /// WaitForTitle = driver.Title = argument /// End Function /// - /// Sub testSimple() - /// Dim driver As New FirefoxDriver /// driver.Get "http://www.google.com" /// driver.Until GetRef("WaitForTitle"), "Google", 1000 /// ... - /// End Sub /// /// object _WebDriver.Until(object procedure, object argument, int timeout) { diff --git a/Setup/Setup.vdproj b/Setup/Setup.vdproj index c703465..61fa833 100755 --- a/Setup/Setup.vdproj +++ b/Setup/Setup.vdproj @@ -123,19 +123,19 @@ } "Entry" { - "MsmKey" = "8:_A027CBE646394A23868EB3974B245BC5" + "MsmKey" = "8:_B3E8B35F89A349DC99BF3F5289E869BE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_B3E8B35F89A349DC99BF3F5289E869BE" + "MsmKey" = "8:_BC025938A3574073BB1AC362AEAE3238" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_BC025938A3574073BB1AC362AEAE3238" + "MsmKey" = "8:_BDE970B051294D81899E7A6C53F24E6B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } @@ -165,6 +165,12 @@ } "Entry" { + "MsmKey" = "8:_DC48988C497E4BEC93DBD3F97A7AD1F2" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_E7060EDCBF3B4169B1D5F5933702ACE0" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -658,12 +664,12 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } - "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A027CBE646394A23868EB3974B245BC5" + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B3E8B35F89A349DC99BF3F5289E869BE" { - "SourcePath" = "8:..\\References\\Selenium.chm" - "TargetName" = "8:Selenium.chm" + "SourcePath" = "8:..\\Examples\\VBScript\\CustomProfileWith(Firefox).vbs" + "TargetName" = "8:CustomProfileWith(Firefox).vbs" "Tag" = "8:" - "Folder" = "8:_A05A497EE20B4C04AF266C77C7FB80C0" + "Folder" = "8:_10177DC2CCA142E190C2FEBDB9BD1192" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" @@ -678,12 +684,12 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } - "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B3E8B35F89A349DC99BF3F5289E869BE" + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BC025938A3574073BB1AC362AEAE3238" { - "SourcePath" = "8:..\\Examples\\VBScript\\CustomProfileWith(Firefox).vbs" - "TargetName" = "8:CustomProfileWith(Firefox).vbs" + "SourcePath" = "8:..\\Scripts\\QuickTest.vbs" + "TargetName" = "8:QuickTest.vbs" "Tag" = "8:" - "Folder" = "8:_10177DC2CCA142E190C2FEBDB9BD1192" + "Folder" = "8:_CBF90B9A71224EFDB89C8CCE3FBE3B84" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" @@ -698,12 +704,12 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } - "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BC025938A3574073BB1AC362AEAE3238" + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BDE970B051294D81899E7A6C53F24E6B" { - "SourcePath" = "8:..\\Scripts\\QuickTest.vbs" - "TargetName" = "8:QuickTest.vbs" + "SourcePath" = "8:..\\Sandcastle\\Help\\SeleniumBasic.chm" + "TargetName" = "8:SeleniumBasic.chm" "Tag" = "8:" - "Folder" = "8:_CBF90B9A71224EFDB89C8CCE3FBE3B84" + "Folder" = "8:_A05A497EE20B4C04AF266C77C7FB80C0" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" @@ -798,6 +804,26 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DC48988C497E4BEC93DBD3F97A7AD1F2" + { + "SourcePath" = "8:..\\References\\geckodriver.exe" + "TargetName" = "8:geckodriver.exe" + "Tag" = "8:" + "Folder" = "8:_A05A497EE20B4C04AF266C77C7FB80C0" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E7060EDCBF3B4169B1D5F5933702ACE0" { "SourcePath" = "8:..\\Scripts\\StartInternetExplorer.vbs" @@ -1087,7 +1113,7 @@ "ShowCmd" = "3:1" "IconIndex" = "3:0" "Transitive" = "11:FALSE" - "Target" = "8:_A027CBE646394A23868EB3974B245BC5" + "Target" = "8:_BDE970B051294D81899E7A6C53F24E6B" "Folder" = "8:_FB317164F39944C28CFA622A8DA92B42" "WorkingFolder" = "8:_A05A497EE20B4C04AF266C77C7FB80C0" "Icon" = "8:" @@ -1522,7 +1548,7 @@ } "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_25CD48DDBB8544D5AA94A12ADF703BFF" { - "SourcePath" = "8:..\\Selenium\\bin\\Release\\Selenium.XML" + "SourcePath" = "8:..\\Selenium\\bin\\x64\\Release\\Selenium.XML" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_A05A497EE20B4C04AF266C77C7FB80C0" From 200e1314c860bc8782165d4793eaaa8baa75f614 Mon Sep 17 00:00:00 2001 From: zc2 Date: Tue, 22 Nov 2022 10:30:47 -0500 Subject: [PATCH 15/16] Removed some not used utility files --- Selenium/Selenium.shfbproj | 93 ------ SeleniumBasicSetup.iss | 619 ------------------------------------- SeleniumBasicSetup.pas | 74 ----- build-setup.py | 218 ------------- clean-folders.py | 36 --- clean-registry.py | 70 ----- gen-registration.ipy | 100 ------ rebuild_exel_files.py | 164 ---------- smoke-tests.py | 85 ----- update-references.py | 455 --------------------------- 10 files changed, 1914 deletions(-) delete mode 100644 Selenium/Selenium.shfbproj delete mode 100644 SeleniumBasicSetup.iss delete mode 100644 SeleniumBasicSetup.pas delete mode 100644 build-setup.py delete mode 100644 clean-folders.py delete mode 100644 clean-registry.py delete mode 100644 gen-registration.ipy delete mode 100644 rebuild_exel_files.py delete mode 100644 smoke-tests.py delete mode 100644 update-references.py diff --git a/Selenium/Selenium.shfbproj b/Selenium/Selenium.shfbproj deleted file mode 100644 index 3ed78af..0000000 --- a/Selenium/Selenium.shfbproj +++ /dev/null @@ -1,93 +0,0 @@ - - - - - Debug - AnyCPU - 2.0 - {e3df862c-c0e7-4652-8e13-4c7d0ef2295a} - 1.9.9.0 - - Documentation - Documentation - Documentation - - .NET Framework 3.5 - bin\Help\ - Selenium - en-US - OnlyWarningsAndErrors - HtmlHelp1 - False - False - False - False - True - 2 - False - Visual Basic Usage - Blank - False - VS2013 - False - Guid - Selenium documentation - AboveNamespaces - Msdn - None - False - True - - - - - InheritedMembers - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bin\Release\Selenium.dll - - - - - \ No newline at end of file diff --git a/SeleniumBasicSetup.iss b/SeleniumBasicSetup.iss deleted file mode 100644 index c3a90b8..0000000 --- a/SeleniumBasicSetup.iss +++ /dev/null @@ -1,619 +0,0 @@ - -#define ASSEMBLY_PATH ".\Selenium\bin\Release\Selenium.dll" -#define ASSEMBLY_VERSION GetFileVersion(ASSEMBLY_PATH) - -#define AppName "Selenium" -#define AppID "{{0277FC34-FD1B-4616-BB19-1FDB7381B291}" -#define AppLongName "Selenium Basic" -#define AppPublisher "Florent BREHERET" -#define AppURL "https://github.com/florentbr/SeleniumBasic" -#define AppFolder "SeleniumBasic" -#define AppSetupFilename "SeleniumBasic-" + ASSEMBLY_VERSION - -[Setup] - -AppId={#AppID} -PrivilegesRequired=lowest -AppName={#AppLongName} -AppVersion={#ASSEMBLY_VERSION} -AppVerName={#AppLongName} -VersionInfoVersion={#ASSEMBLY_VERSION} -VersionInfoTextVersion={#ASSEMBLY_VERSION} -AppPublisher={#AppPublisher} -AppPublisherURL={#AppURL} -AppSupportURL={#AppURL} -AppUpdatesURL={#AppURL} -DisableDirPage=yes -DefaultDirName={code:GetRootDir} -UsePreviousAppDir=no -UsePreviousSetupType=yes -DefaultGroupName={#AppLongName} -DisableProgramGroupPage=yes -LicenseFile=.\LICENSE.txt -OutputDir="." -OutputBaseFilename={#AppSetupFilename} -;Compression=zip -Compression=lzma2 -SolidCompression=yes -DirExistsWarning=no -ArchitecturesInstallIn64BitMode=x64 - -[Languages] -Name: "english"; MessagesFile: "compiler:Default.isl" - -[CustomMessages] -MsgNETFrameworkNotInstalled=Microsoft .NET Framework 3.5 installation was Not detected. -MsgRegistryWriteFailure=Failed to register the library due to insufficient privileges. -MsgFileNotFound=File Not found: %1. -MsgCOMInvokeFailed=Installation failed. The installer was unable to call the registered library. - -[Components] -Name: "pkg_core"; Description: ".Net core libraries"; Types: full compact custom; Flags: fixed; -Name: "pkg_doc"; Description: "Templates and examples"; Types: full compact custom; -Name: "pkg_cons"; Description: "Enhanced console runner for VBScript files"; Types: full compact custom; -Name: "pkg_ff"; Description: "WebDriver for Firefox"; Types: full custom; -Name: "pkg_cr"; Description: "WebDriver for Chrome"; Types: full custom; -Name: "pkg_op"; Description: "WebDriver for Opera"; Types: full custom; -Name: "pkg_ie"; Description: "WebDriver for Internet Explorer"; Types: full custom; -Name: "pkg_edg"; Description: "WebDriver for Microsoft Edge"; Types: full custom; -Name: "pkg_pjs"; Description: "WebDriver for PhantomJS (headless browser)"; Types: full custom; -Name: "pkg_ide"; Description: "SeleniumIDE plugin for Firefox"; Types: full custom; - -[Files] -Source: "Selenium\bin\Release\Selenium.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_core; -Source: "Selenium\bin\Release\Selenium.pdb"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_core; -Source: "Selenium\bin\Release\Selenium32.tlb"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_core; -Source: "Selenium\bin\Release\Selenium64.tlb"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_core; Check: IsWin64; - -Source: "Selenium\bin\Help\Selenium.chm"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_core; - -Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion overwritereadonly ; Attribs:readonly; -Source: "CHANGELOG.txt"; DestDir: "{app}"; Flags: ignoreversion overwritereadonly ; Attribs:readonly; - -Source: "VbsConsole\bin\Release\vbsc.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_cons; - -;drivers -Source: "References\firefoxdriver.xpi"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_ff; -Source: "References\chromedriver.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_cr; -Source: "References\operadriver.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_op; -Source: "References\phantomjs.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_pjs; -Source: "References\iedriver.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_ie; -;Source: "References\iedriver64.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_ie; Check: IsWin64; -Source: "References\edgedriver.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_edg; - -;Firefox extensions -Source: "FirefoxAddons\bin\extensions.xpi"; DestDir: "{app}"; Flags: ignoreversion; Components: pkg_ide; - -;examples -Source: "Scripts\*.*" ; DestDir: "{app}\Scripts"; Flags: ignoreversion overwritereadonly; Attribs:readonly; Components: pkg_core; -Source: "Templates\*.vbs" ; DestDir: "{app}\Templates"; Flags: ignoreversion overwritereadonly; Attribs:readonly; Components: pkg_doc; -Source: "Templates\Xlbin\*.xlt"; DestDir: "{app}\Templates"; Flags: ignoreversion overwritereadonly; Attribs:readonly; Components: pkg_doc; Check: HasOldExcel -Source: "Templates\*.xltm" ; DestDir: "{app}\Templates"; Flags: ignoreversion overwritereadonly; Attribs:readonly; Components: pkg_doc; Check: HasNewExcel -Source: "Templates\*.au3" ; DestDir: "{app}\Templates"; Flags: ignoreversion overwritereadonly; Attribs:readonly; Components: pkg_doc; Check: HasAutoIt -Source: "Examples\VBScript\*.vbs"; DestDir: "{app}\Examples\VBScript"; Flags: ignoreversion overwritereadonly; Attribs:readonly; Components: pkg_doc; -Source: "Examples\JavaScript\*.js"; DestDir: "{app}\Examples\JavaScript"; Flags: ignoreversion overwritereadonly; Attribs:readonly; Components: pkg_doc; -Source: "Examples\Excel\Xlbin\*.xls"; DestDir: "{app}\Examples\Excel"; Flags: ignoreversion overwritereadonly; Attribs:readonly; Components: pkg_doc; Check: HasOldExcel -Source: "Examples\Excel\*.xlsm"; DestDir: "{app}\Examples\Excel"; Flags: ignoreversion overwritereadonly; Attribs:readonly; Components: pkg_doc; Check: HasNewExcel -Source: "Examples\AutoIt\*.au3"; DestDir: "{app}\Examples\AutoIt"; Flags: ignoreversion overwritereadonly; Attribs:readonly; Components: pkg_doc; Check: HasAutoIt - -;copy config file -Source: "References\exe.config" ; DestDir: "{sys}"; DestName: "wscript.exe.config"; Flags: ignoreversion uninsneveruninstall; Check: HasPrivileges; -Source: "References\exe.config" ; DestDir: "{sys}"; DestName: "cscript.exe.config"; Flags: ignoreversion uninsneveruninstall; Check: HasPrivileges; -Source: "References\exe.config" ; DestDir: "{syswow64}"; DestName: "wscript.exe.config"; Flags: ignoreversion uninsneveruninstall; Check: IsWin64 And HasPrivileges; -Source: "References\exe.config" ; DestDir: "{syswow64}"; DestName: "cscript.exe.config"; Flags: ignoreversion uninsneveruninstall; Check: IsWin64 And HasPrivileges; -Source: "References\exe.config" ; DestDir: "{code:GetAppFolder|excel.exe}"; DestName: "EXCEL.EXE.CONFIG"; Flags: ignoreversion uninsneveruninstall; Check: HasPrivileges And HasApp('excel.exe'); -Source: "References\exe.config" ; DestDir: "{code:GetAppFolder|winword.exe}"; DestName: "WINWORD.EXE.CONFIG"; Flags: ignoreversion uninsneveruninstall; Check: HasPrivileges And HasApp('winword.exe'); -Source: "References\exe.config" ; DestDir: "{code:GetAppFolder|msaccess.exe}"; DestName: "MSACCESS.EXE.CONFIG"; Flags: ignoreversion uninsneveruninstall; Check: HasPrivileges And HasApp('msaccess.exe'); -Source: "References\exe.config" ; DestDir: "{code:GetAppFolder|outlook.exe}"; DestName: "OUTLOOK.EXE.CONFIG"; Flags: ignoreversion uninsneveruninstall; Check: HasPrivileges And HasApp('outlook.exe'); - -[Icons] -Name: "{group}\Project Home Page"; Filename: {#AppURL}; WorkingDir: "{app}"; -Name: "{group}\Vbs Console"; Filename: "{app}\vbsc.exe"; Components: pkg_cons; -Name: "{group}\Examples"; Filename: "{app}\Examples"; -Name: "{group}\Templates"; Filename: "{app}\Templates"; -Name: "{group}\RunCleaner"; Filename: "{app}\Scripts\RunCleaner.vbs"; -Name: "{group}\API documentation"; Filename: "{app}\Selenium.chm"; -Name: "{group}\ChangeLog"; Filename: "{app}\CHANGELOG.txt"; -Name: "{group}\Uninstall"; Filename: "{uninstallexe}" - -Name: "{group}\Start Firefox"; Filename: "{app}\Scripts\StartFirefox.vbs"; Components: pkg_ff; -Name: "{group}\Start Chrome"; Filename: "{app}\Scripts\StartChrome.vbs"; Components: pkg_cr; -Name: "{group}\Start Chrome Debug"; Filename: "{app}\Scripts\StartChromeDebug.vbs"; Components: pkg_cr; -Name: "{group}\Start IE"; Filename: "{app}\Scripts\StartInternetExplorer.vbs"; Components: pkg_ie; -Name: "{group}\Start Opera"; Filename: "{app}\Scripts\StartOpera.vbs"; Components: pkg_op; -Name: "{group}\Start PhantomJS"; Filename: "{app}\Scripts\StartPhantomJS.vbs"; Components: pkg_pjs; - - -[Registry] - -;Firefox plugins -;Root: HKCU; Subkey: "Software\Mozilla\Firefox\Extensions"; ValueName: "{{a6fd85ed-e919-4a43-a5af-8da18bda539f}"; ValueType: string; ValueData:"{app}\selenium-ide.xpi"; Flags: uninsdeletevalue; Components: pkg_ide; -;Root: HKCU; Subkey: "Software\Mozilla\Firefox\Extensions"; ValueName: "vbformatters@florent.breheret"; ValueType: string; ValueData:"{app}\vbformatters.xpi"; Flags: uninsdeletevalue; Components: pkg_ide; -;Root: HKCU; Subkey: "Software\Mozilla\Firefox\Extensions"; ValueName: "implicit-wait@florent.breheret"; ValueType: string; ValueData:"{app}\implicit-wait.xpi"; Flags: uninsdeletevalue; Components: pkg_ide; - -;IE tweaks: Force the tabs to run in the same process as the manager process -;Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Main"; ValueName: "TabProcGrowth"; ValueType: dword; ValueData: 0; Flags: uninsdeletevalue; Components: pkg_ie; - -;IE tweaks: Allow file navigation -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_LOCALMACHINE_LOCKDOWN"; ValueName: "iexplore.exe"; ValueType: dword; ValueData: 0; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_RESTRICT_FILEDOWNLOAD"; ValueName: "iexplore.exe"; ValueType: dword; ValueData: 0; Components: pkg_ie; - -;IE tweaks: Maintain a connection to the instance (for IE11) -Root: HKCU; Subkey: "SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BFCACHE"; ValueName: "iexplore.exe"; ValueType: dword; ValueData: 0; Components: pkg_ie; - -;IE tweaks: Restore default zoom level -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Zoom"; ValueName: "ZoomFactor"; Flags: dontcreatekey deletevalue; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Zoom"; ValueName: "ResetTextSizeOnStartup"; ValueType: dword; ValueData: 1 ; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Zoom"; ValueName: "ResetZoomOnStartup"; ValueType: dword; ValueData: 1 ; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Zoom"; ValueName: "ResetZoomOnStartup2"; ValueType: dword; ValueData: 1 ; Components: pkg_ie; - -;IE tweaks: Disable enhanced protected mode -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Main"; ValueName: "Isolation"; Flags: dontcreatekey deletevalue; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Main"; ValueName: "Isolation64Bit"; Flags: dontcreatekey deletevalue; Components: pkg_ie; - -;IE tweaks: Disable autocomplete -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Explorer\AutoComplete"; ValueName: "AutoSuggest"; ValueType: string; ValueData: "no"; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Main"; ValueName: "Use FormSuggest"; ValueType: string; ValueData: "no"; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Main"; ValueName: "FormSuggest Passwords"; ValueType: string; ValueData: "no"; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Main"; ValueName: "FormSuggest PW Ask"; ValueType: string; ValueData: "no"; Components: pkg_ie; - -;IE tweaks: Disable warn -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings"; ValueName: "WarnonBadCertRecving"; ValueType: dword; ValueData: 0; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings"; ValueName: "WarnonZoneCrossing"; ValueType: dword; ValueData: 0; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings"; ValueName: "WarnOnPostRedirect"; ValueType: dword; ValueData: 0; Components: pkg_ie; - -;IE tweaks: Disable Check for publisher's certificate revocation -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\WinTrust\Trust Providers\Software Publishing"; ValueName: "State"; ValueType: dword; ValueData: 146944; Components: pkg_ie; -;IE tweaks: Disable Check for server certificate revocation -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings"; ValueName: "CertificateRevocation"; ValueType: dword; ValueData: 0; Components: pkg_ie; -;IE tweaks: Disable Check for signatures on downloaded programs -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Download"; ValueName: "CheckExeSignatures"; ValueType: string; ValueData: "no"; Components: pkg_ie; - -;IE tweaks: Disable Check default browser -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Main"; ValueName: "Check_Associations"; ValueType: string; ValueData: "no"; Components: pkg_ie; - -;IE tweaks: Disable accelerator button -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Activities"; ValueName: "NoActivities"; ValueType: dword; ValueData: 1; Components: pkg_ie; - -;IE tweaks: Set the same protected mode for all zones Disable=3 Enable=0 -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Main"; ValueName: "NoProtectedModeBanner"; ValueType: dword; ValueData: 1; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\0"; ValueName: "2500"; ValueType: dword; ValueData: 3; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1"; ValueName: "2500"; ValueType: dword; ValueData: 3; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\2"; ValueName: "2500"; ValueType: dword; ValueData: 3; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3"; ValueName: "2500"; ValueType: dword; ValueData: 3; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\4"; ValueName: "2500"; ValueType: dword; ValueData: 3; Components: pkg_ie; - -;IE tweaks: Enable all cookies -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings"; ValueName: "PrivacyAdvanced"; ValueType: dword; ValueData: 1; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3"; ValueName: "1A10"; ValueType: dword; ValueData: 1; Components: pkg_ie; - -;IE tweak: Allow HTTP Basic Authentication in url -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_HTTP_USERNAME_PASSWORD_DISABLE"; ValueName: "iexplore.exe"; ValueType: dword; ValueData: 0; Components: pkg_ie; - -;IE tweak: Turn off popup blocker -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\New Windows"; ValueName: "PopupMgr"; ValueType: dword; ValueData: 0; Components: pkg_ie; - -;IE tweak: Delete browsing history on exit -Root: HKCU; Subkey: "Software\Microsoft\Internet Explorer\Privacy"; ValueName: "ClearBrowsingHistoryOnExit"; ValueType: dword; ValueData: 1; Components: pkg_ie; - -;IE tweak: Disable cache -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings\CACHE"; ValueName: "Persistent"; ValueType: dword; ValueData: 0; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings"; ValueName: "SyncMode5"; ValueType: dword; ValueData: 3; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings\Url History"; ValueName: "DaysToKeep"; ValueType: dword; ValueData: 0; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\Cache\Content"; ValueName: "CacheLimit"; ValueType: dword; ValueData: 2000; Components: pkg_ie; -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Internet Settings\Cache\Content"; ValueName: "CacheLimit"; ValueType: dword; ValueData: 2000; Components: pkg_ie; - -;File association for the console -Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\App Paths\vbsc.exe"; ValueType: string; ValueData: "{app}\vbsc.exe"; Flags: deletekey uninsdeletekey; Components: pkg_cons; - -Root: HKCU; Subkey: "Software\Classes\VBSFile\Shell\Debug"; ValueType: string; ValueData: "Debug"; Flags: deletekey uninsdeletekey; -Root: HKCU; Subkey: "Software\Classes\VBSFile\Shell\Debug\Command"; ValueType: expandsz; ValueData: """%SystemRoot%\System32\wscript.exe"" //D //X ""%1"" %*"; Components: pkg_cons; - -Root: HKCU; Subkey: "Software\Classes\VBSFile\shell\runas"; ValueType: string; ValueName: "HasLUAShield"; ValueData: ""; Flags: deletekey uninsdeletekey; -Root: HKCU; Subkey: "Software\Classes\VBSFile\shell\runas\Command"; ValueType: expandsz; ValueData: """%SystemRoot%\System32\wscript.exe"" ""%1"" %*"; - -Root: HKCU; Subkey: "Software\Classes\VBSFile\Shell\RunExt"; ValueType: string; ValueData: "Run VBScript"; Flags: deletekey uninsdeletekey; Components: pkg_cons; -Root: HKCU; Subkey: "Software\Classes\VBSFile\Shell\RunExt\Command"; ValueType: string; ValueData: """{app}\vbsc.exe"" -i ""%1"" %*"; Components: pkg_cons; - -Root: HKCU; Subkey: "Software\Classes\Directory\shell\RunExt"; ValueType: string; ValueData: "Run VBScripts"; Flags: deletekey uninsdeletekey; Components: pkg_cons; -Root: HKCU; Subkey: "Software\Classes\Directory\shell\RunExt\Command"; ValueType: string; ValueData: """{app}\vbsc.exe"" -i ""%1\*.vbs"""; Components: pkg_cons; - -;Add excel trusted location for templates and examples -Root: HKCU; Subkey: "Software\Microsoft\Office\{code:GetOfficeVersion|Excel}.0\Excel\Security\Trusted Locations\Selenium1"; ValueName: "Path"; ValueType: String; ValueData: "{app}\Templates"; Flags: uninsdeletekey; Check: HasExcel; -Root: HKCU; Subkey: "Software\Microsoft\Office\{code:GetOfficeVersion|Excel}.0\Excel\Security\Trusted Locations\Selenium2"; ValueName: "Path"; ValueType: String; ValueData: "{app}\Examples"; Flags: uninsdeletekey; Check: HasExcel; -Root: HKCU; Subkey: "Software\Microsoft\Office\{code:GetOfficeVersion|Word}.0\Word\Security\Trusted Locations\Selenium1"; ValueName: "Path"; ValueType: String; ValueData: "{app}\Templates"; Flags: uninsdeletekey; Check: HasWord; -Root: HKCU; Subkey: "Software\Microsoft\Office\{code:GetOfficeVersion|Word}.0\Word\Security\Trusted Locations\Selenium2"; ValueName: "Path"; ValueType: String; ValueData: "{app}\Examples"; Flags: uninsdeletekey; Check: HasWord; - -;Enable WScript host in case it's been disabled -Root: HKCU; Subkey: "Software\Microsoft\Windows Script Host\Settings"; ValueName: "Enabled"; ValueType: dword; ValueData: 1; - -[Run] -;Filename: "{app}\RegNet.exe"; Parameters: "-r"; WorkingDir: {app}; Flags: waituntilterminated runascurrentuser runhidden; StatusMsg: "Register for COM interoperability"; -Filename: "{code:GetAppPath|firefox.exe}"; Parameters: "-url ""{app}\extensions.xpi"""; WorkingDir: {app}; Flags: shellexec postinstall skipifsilent runascurrentuser; Components: pkg_ide; Check: HasFirefox; Description: "Install the Selenium IDE Addon for Firefox"; - -[UninstallRun] -;Filename: "{app}\RegNet.exe"; Parameters: "-u"; WorkingDir: {app}; Flags: waituntilterminated runascurrentuser runhidden; StatusMsg: "Unregister for COM interoperability"; - -[InstallDelete] -Type: filesandordirs; Name: "{localappdata}\Temp\Selenium" - -[UninstallDelete] -Type: filesandordirs; Name: "{app}" -Type: filesandordirs; Name: "{localappdata}\Temp\Selenium" - -[Code] -var - ALL_USERS : Boolean; - HK : Integer; - HK32 : Integer; - -Function HasPrivileges(): Boolean; - Begin - Result := IsAdminLoggedOn Or IsPowerUserLoggedOn - End; - -Function GetRootDir(Param: String): String; - Begin - If ALL_USERS Then - Result := ExpandConstant('{pf}\{#AppFolder}') - Else - Result := ExpandConstant('{localappdata}\{#AppFolder}'); - End; - -Procedure InitRootKeys(); - Begin - If ALL_USERS Then Begin - HK := HKLM; - HK32 := HKLM32; - End Else Begin - HK := HKCU; - HK32 := HKCU32; - End - End; - -Function GetAppPath(app: String): string; - Begin - RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\' + app, '', Result); - End; - -Function HasFirefox(): Boolean; - Begin - Result := FileExists(GetAppPath('firefox.exe')); - End; - -Function GetAppFolder(app: String): string; - Begin - Result := ExtractFileDir(GetAppPath(app)); - End; - -Function HasApp(app: String): Boolean; - Begin - Result := GetAppPath(app) <> ''; - End; - -Function GetOfficeVersion(app: String): String; - Var ver: String; i: Integer; - Begin - If RegQueryStringValue(HKCR, app + '.Application\CurVer', '', ver) Then Begin - For i := 1 To Length(ver) Do Begin - If (ver[i] >= '0') And (ver[i] <= '9') Then - Result := Result + ver[i]; - End; - End; - End; - -Function HasExcel(): Boolean; - Begin - Result := RegKeyExists(HKCR, 'Excel.Application'); - End; - -Function HasWord(): Boolean; - Begin - Result := RegKeyExists(HKCR, 'Word.Application'); - End; - -Function HasNewExcel(): Boolean; - Begin - Result := RegKeyExists(HKCR, '.xlsm'); - End; - -Function HasOldExcel(): Boolean; - Begin - Result := RegKeyExists(HKCR, '.xls') And Not RegKeyExists(HKCR, '.xlsm'); - End; - -Function HasAutoIt(): Boolean; - Begin - Result := RegKeyExists(HKCR, '.au3'); - End; - -Procedure _PatchOfficeFileVersion(Const subkey: String); - Var name, value: String; - Begin - name := 'Maximum File Version Number'; - If RegQueryStringValue(HKLM, subkey, name, value) Then Begin - RegWriteStringValue(HKLM, subkey, 'Maximum File Version', value); - RegDeleteValue(HKLM, subkey, name); - End - End; - -Procedure PatchOfficeFileVersion(); - Begin - If Not HasPrivileges() Then Exit; - //Excel 11 - _PatchOfficeFileVersion('SOFTWARE\Microsoft\.NETFramework\Policy\AppPatch\v4.0.30319.00000\excel.exe\{2CCAA9FE-6884-4AF2-99DD-5217B94115DF}'); - _PatchOfficeFileVersion('SOFTWARE\Microsoft\.NETFramework\Policy\AppPatch\v2.0.50727.00000\excel.exe\{2CCAA9FE-6884-4AF2-99DD-5217B94115DF}'); - //Word 11 - _PatchOfficeFileVersion('SOFTWARE\Microsoft\.NETFramework\Policy\AppPatch\v4.0.30319.00000\winword.exe\{2CCAA9FE-6884-4AF2-99DD-5217B94115DF}'); - _PatchOfficeFileVersion('SOFTWARE\Microsoft\.NETFramework\Policy\AppPatch\v2.0.50727.00000\winword.exe\{2CCAA9FE-6884-4AF2-99DD-5217B94115DF}'); - End; - -Procedure AssertFrameworkPresent(Const version: String); - Begin - If Not RegKeyExists(HKLM,'SOFTWARE\Microsoft\NET Framework Setup\NDP\v' + version) Then - RaiseException(ExpandConstant('{cm:MsgNETFrameworkNotInstalled}')); - End; - -Procedure AssertFilePresent(Const path: String); - Begin - If Not FileExists(path) Then - RaiseException(ExpandConstant('{cm:MsgFileNotFound,' + path +'}')); - End; - -Function UninstallPrevious(const appid: String): Boolean; - Var key, name, out_cmd: String; retcode: Integer; - Begin - key := 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + ExpandConstant(appid) + '_is1'; - name := 'UninstallString'; - If Not RegQueryStringValue(HKLM, key, name, out_cmd) Then - If Not RegQueryStringValue(HKLM32, key, name, out_cmd) Then - If Not RegQueryStringValue(HKCU, key, name, out_cmd) Then - RegQueryStringValue(HKCU32, key, name, out_cmd); - If out_cmd <> '' Then - Exec('>', out_cmd + ' /SILENT /NORESTART /SUPPRESSMSGBOXES', '', SW_SHOW, ewWaitUntilTerminated, retcode); - Result := retcode = 0; - End; - -Function BoolToStr(const value: Boolean): String; - Begin - If value Then Result := '1' Else Result := '0'; - End; - -//--------------------------------------------------------------------------------------- -// Registration -//--------------------------------------------------------------------------------------- - -type - TNetLib = record - Guid: String; - FullName: String; - Description: String; - TypeVersion: String; - Directory: String; - PathDll: String; - PathTlb32: String; - PathTlb64: String; - Runtime: String; - end; - -Procedure RegString(Const root: Integer; Const subkey, name, value: String); - Begin - //Log('REG [' + subkey + '] "' + name + '"="' + value + '"'); - If Not RegWriteStringValue(root, subkey, name, value) Then - RaiseException(ExpandConstant('{cm:MsgRegistryWriteFailure}')); - End; - -// Interface registration (32/64bits Independent) : -// -// HKCU\Software\Classes\Interface\{ interface guid } -// ""=" interface name " -// \ProxyStubClsid32 -// ""=" interface short name " -// \TypeLib -// ""="{ assembly guid }" -// \Version -// ""=" assembly type version " -// -Procedure RegInterface_(Const lib : TNetLib; Const root: Integer; Const guid, typename, proxyStub: String); - Var key: String; - Begin - key := 'Software\Classes\Interface\' + guid; - RegDeleteKeyIncludingSubkeys(root, key); - If Not IsUninstaller Then Begin - RegString(root, key , '' , typename ); - RegString(root, key + '\ProxyStubClsid32' , '' , proxyStub ); - RegString(root, key + '\TypeLib' , '' , lib.Guid ); - RegString(root, key + '\TypeLib' , 'Version' , lib.TypeVersion ); - End - End; - -Procedure RegInterface(Const lib : TNetLib; Const guid, name, proxystub: String); - Begin - RegInterface_(lib, HK, guid, name, proxystub); - If IsWin64 Then - RegInterface_(lib, HK32, guid, name, proxystub); - End; - -// Enumeration registration (32/64bits Shared) : -// -// HKCU\Software\Classes\Record\{ record guid }] -// ""="mscoree.dll" -// "Class"="Selenium.Structures.Point" -// "Assembly"="Selenium, Version=2.0.1.2, Culture=neutral, PublicKeyToken=null" -// "RuntimeVersion"="v2.0.50727" -// "CodeBase"="C:\...\SeleniumBasic\Selenium.dll" -// -Procedure RegRecord(Const lib : TNetLib; Const guid, typename: String); - Var key: String; - Begin - key := 'Software\Classes\Record\' + guid; - RegDeleteKeyIncludingSubkeys(HK, key); - If Not IsUninstaller Then Begin - RegString(HK, key, 'Class' , typename ); - RegString(HK, key, 'Assembly' , lib.FullName ); - RegString(HK, key, 'RuntimeVersion' , lib.Runtime ); - RegString(HK, key, 'CodeBase' , lib.PathDll ); - End - End; - -// CLSID registration (32/64bits Independent) : -// -// Root\Software\Classes\CLSID\{ class guid } -// ""="Selenium.WebDriver" -// \InprocServer32 -// ""="C:\Windows\System32\mscoree.dll" -// "Class"="Selenium.WebDriver" -// "Assembly"="Selenium, Version=2.0.1.2, Culture=neutral, PublicKeyToken=null" -// "RuntimeVersion"="v2.0.50727" -// "CodeBase"="C:\...\SeleniumBasic\Selenium.dll" -// "ThreadingModel"="Both" -// \Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29} -// ""="" -// \ProgId -// ""="Selenium.WebDriver" -// \VersionIndependentProgID -// ""="Selenium.WebDriver" -// -Procedure RegClsid(Const lib : TNetLib; Const root: Integer; Const guid, progid, typename, sysdir: String); - Var key, skey : String; - Begin - key := 'Software\Classes\CLSID\' + guid; - RegDeleteKeyIncludingSubkeys(root, key); - If Not IsUninstaller Then Begin - RegString(root, key, '', typename); - - skey := key + '\InprocServer32'; - RegString(root, skey, '' , ExpandConstant(sysdir) + '\mscoree.dll' ); - RegString(root, skey, 'Class' , typename ); - RegString(root, skey, 'Assembly' , lib.FullName ); - RegString(root, skey, 'RuntimeVersion' , lib.Runtime ); - RegString(root, skey, 'CodeBase' , lib.PathDll ); - RegString(root, skey, 'ThreadingModel' , 'Both' ); - - skey := key + '\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}'; - RegString(root, skey, '', ''); - - skey := key + '\ProgId'; - RegString(root, skey, '', progid); - - skey := key + '\VersionIndependentProgID'; - RegString(root, skey, '', progid); - End - End; - -// Class registration (32/64bits Shared) : -// -// HKCU\Software\Classes\[progid] -// ""=[progid] -// \CLSID -// ""=[class guid] -// -Procedure RegClass(Const lib : TNetLib; Const guid, progid, typename: String); - Var key, sysdir: String; - Begin - key := 'Software\Classes\' + progid; - RegDeleteKeyIncludingSubkeys(HK, key); - If Not IsUninstaller Then Begin - RegString(HK, key, '', progid); - RegString(HK, key + '\CLSID', '', guid); - End - - RegClsid(lib, HK, guid, progid, typename, '{sys}'); - If IsWin64 Then - RegClsid(lib, HK32, guid, progid, typename, '{syswow64}'); - End; - -// TypeLib registration (32/64bits Shared) : -// -// HKCU\Software\Classes\TypeLib\[assembly guid] -// \2.0 -// ""="App Type Library" -// \0\win32 -// ""="C:\...\App32.tlb" -// \0\win64 -// ""="C:\...\App64.tlb" -// \FLAGS -// ""="0" -// \HELPDIR -// ""="C:\..." -// -Procedure RegTypeLib(Const lib : TNetLib); - Var key, skey : String; - Begin - key := 'Software\Classes\TypeLib\' + lib.Guid; - RegDeleteKeyIncludingSubkeys(HK, key); - If Not IsUninstaller Then Begin - skey := key + '\' + lib.TypeVersion; - RegString(HK, skey, '', lib.Description); - RegString(HK, skey + '\FLAGS' , '' , '0' ); - RegString(HK, skey + '\HELPDIR' , '' , lib.Directory ); - RegString(HK, skey + '\0\win32' , '' , lib.PathTlb32 ); - If IsWin64 Then - RegString(HK, skey + '\0\win64', '' , lib.PathTlb64 ); - End - End; - -Procedure RegisterAssembly(); - Var lib : TNetLib; - Begin - { Includes the file generated by gen-registration.ipy } - #include 'SeleniumBasicSetup.pas' - End; - -//--------------------------------------------------------------------------------------- -// Workflow -//--------------------------------------------------------------------------------------- - -Function InitializeSetup() : Boolean; - Begin - ALL_USERS := HasPrivileges(); - InitRootKeys(); - //AssertFrameworkPresent('3.5'); - Result := True; - End; - -Function InitializeUninstall() : Boolean; - var p : string; - Begin - ALL_USERS := GetPreviousData('AllUsers', '') = '1'; - InitRootKeys(); - Result := True; - End; - -Procedure RegisterPreviousData(PreviousDataKey: Integer); - Begin - SetPreviousData(PreviousDataKey, 'AllUsers', BoolToStr(ALL_USERS)); - End; - -Procedure CurPageChanged(CurPageID: Integer); - var lines : TStrings; - Begin - If CurPageID = wpReady Then Begin - lines := Wizardform.ReadyMemo.Lines; - If ALL_USERS Then - lines.Insert(0, 'Install folder / All Users :') - Else - lines.Insert(0, 'Install folder / Current User :'); - - lines.Insert(1, ExpandConstant('{app}')); - lines.Insert(2, ''); - End; - End; - -Procedure CurStepChanged(CurStep: TSetupStep); - Begin - If CurStep = ssInstall Then Begin - UninstallPrevious('SeleniumWrapper'); - UninstallPrevious('{#AppId}'); - End Else If CurStep = ssPostInstall Then Begin - RegisterAssembly(); - PatchOfficeFileVersion(); - End; - End; - -Procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); - Begin - If CurUninstallStep = usUninstall Then Begin - RegisterAssembly(); //Only deletes the main keys - End; - End; diff --git a/SeleniumBasicSetup.pas b/SeleniumBasicSetup.pas deleted file mode 100644 index d543f00..0000000 --- a/SeleniumBasicSetup.pas +++ /dev/null @@ -1,74 +0,0 @@ -{ - Code generated at each build by gen-registration.ipy - This is a subset of SeleniumBasicSetup.iss -} - -lib.Guid := '{0277FC34-FD1B-4616-BB19-A9AABCAF2A70}'; -lib.FullName := 'Selenium, Version=2.0.9.0, Culture=neutral, PublicKeyToken=d499ab7f7ba4d827'; -lib.Description := 'Selenium Type Library'; -lib.TypeVersion := '2.0'; -lib.PathDll := ExpandConstant('{app}\Selenium.dll'); -lib.PathTlb32 := ExpandConstant('{app}\Selenium32.tlb'); -lib.PathTlb64 := ExpandConstant('{app}\Selenium64.tlb'); -lib.Runtime := 'v2.0.50727'; - -RegTypeLib(lib); - -RegClass(lib, '{0277FC34-FD1B-4616-BB19-0809389E78C4}', 'Selenium.PhantomJSDriver', 'Selenium.PhantomJSDriver'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-14DB1E4916D4}', 'Selenium.FirefoxDriver', 'Selenium.FirefoxDriver'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-3C406728F1A2}', 'Selenium.EdgeDriver', 'Selenium.EdgeDriver'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-44A424DB3F50}', 'Selenium.Timeouts', 'Selenium.Timeouts'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-5D556733E8C9}', 'Selenium.ChromeDriver', 'Selenium.ChromeDriver'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-5DB46A739EEA}', 'Selenium.List', 'Selenium.List'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-6AAF7EDD33D6}', 'Selenium.Assert', 'Selenium.Assert'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-7D30CBC3F6BB}', 'Selenium.Waiter', 'Selenium.Waiter'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-80B2B91F0D44}', 'Selenium.By', 'Selenium.By'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-9E7F9EF1D002}', 'Selenium.OperaDriver', 'Selenium.OperaDriver'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-A34FCBA29598}', 'Selenium.Utils', 'Selenium.Utils'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-B0C8C528C673}', 'Selenium.Verify', 'Selenium.Verify'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-B719752452AA}', 'Selenium.Table', 'Selenium.Table'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-BE75D14E7B41}', 'Selenium.Keys', 'Selenium.Keys'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-CDCD9EB97FD6}', 'Selenium.PdfFile', 'Selenium.PdfFile'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-CEA7D8FD6954}', 'Selenium.Dictionary', 'Selenium.Dictionary'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-E3CCFFAB4234}', 'Selenium.WebDriver', 'Selenium.WebDriver'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-E9AAFA695FFB}', 'Selenium.Application', 'Selenium.Application'); -RegClass(lib, '{0277FC34-FD1B-4616-BB19-EED04A1E4CD1}', 'Selenium.IEDriver', 'Selenium.IEDriver'); - -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-01D514FE0B1A}', '_Utils', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-0B61E370369D}', '_TableRow', '{00020424-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-0EA52ACB97D1}', '_Assert', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-11660D7615B7}', '_Manage', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-1456C48D8E5C}', '_Dictionary', '{00020424-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-2276E80F5CF7}', '_Cookie', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-384C7E50EFA8}', '_Waiter', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-495CC9DBFB96}', '_Verify', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-4CE442A16502}', '_SelectElement', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-54BA7C175990}', '_Image', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-61DAD6C51012}', '_Keyboard', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-637431245D48}', '_Keys', '{00020424-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-63F894CA99E9}', '_Mouse', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-6E0522EA435E}', '_Application', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-74F5D5680428}', '_Timeouts', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-7C9763568492}', '_WebElements', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-7E2EBB6C82E9}', '_Size', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-8B145197B76C}', '_WebElement', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-A398E67A519B}', '_DictionaryItem', '{00020424-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-A3DE5685A27E}', '_By', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-ACE280CD7780}', '_Point', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-B51CB7C5A694}', '_Alert', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-B825A6BF9610}', '_Table', '{00020424-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-BBE48A6D09DB}', '_Actions', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-BE15C121F199}', '_TableElement', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-C539CB44B63F}', '_List', '{00020424-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-C6F450B6EE52}', '_Storage', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-CC6284398AA5}', '_WebDriver', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-D0E30A5D0697}', '_Proxy', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-D5DE929CF018}', '_TouchActions', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-E6E7ED329824}', '_Cookies', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-F2A56C3A68D4}', '_PdfFile', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-FBDA3A91C82B}', '_Window', '{00020420-0000-0000-C000-000000000046}'); -RegInterface(lib, '{0277FC34-FD1B-4616-BB19-FFD6FAEF290A}', '_TouchScreen', '{00020420-0000-0000-C000-000000000046}'); - -RegRecord(lib, '{0277FC34-FD1B-4616-BB19-300DAA508541}', 'Selenium.Strategy'); -RegRecord(lib, '{0277FC34-FD1B-4616-BB19-B342CE81CB2A}', 'Selenium.MouseButton'); -RegRecord(lib, '{0277FC34-FD1B-4616-BB19-C724C5135B6E}', 'Selenium.CacheState'); diff --git a/build-setup.py b/build-setup.py deleted file mode 100644 index 4f4998b..0000000 --- a/build-setup.py +++ /dev/null @@ -1,218 +0,0 @@ - -"""Script to create the installation package for Windows: - Builds the Firefox add-ins - Builds the project with MSBuild.exe - Builds the 32bit and 64bit Type libraries with TlbExp.exe - Builds the documentation with Help File Builder [https://shfb.codeplex.com/] - Creates the setup installer with InnoSetup [http://www.jrsoftware.org/isinfo.php] -""" - -import os, re, time, sys, traceback, shutil, datetime, subprocess, zipfile, glob - -__dir__ = os.path.dirname(os.path.realpath(__file__)) - -APP_MSBUILD_PATH = r'c:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe' -APP_TLBEXP_PATH = r'c:\Progra~2\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\TlbExp.exe' -APP_INNOSETUP_PATH = r'c:\Progra~2\Inno Setup 5\ISCC.exe' -APP_PYTHON_PATH = r'c:\Progra~2\Python27\python.exe' -APP_IRONPYTHON_PATH = r'c:\Progra~2\IronPython 2.7\ipy.exe' -APP_SHFBROOT_DIR = r'c:\Progra~2\EWSoftware\Sandcastle Help File Builder' - -def main(): - set_working_dir(__dir__) - check_globals_exists(r'_PATH$|_DIR$') - - assemblyinfo_path = __dir__ + r'\Selenium\Properties\AssemblyInfo.cs' - last_modified_time = get_file_mtime(assemblyinfo_path, '%Y-%m-%d %H:%M:%S') - current_version = match_in_file(assemblyinfo_path, r'AssemblyFileVersion\("([.\d]+)"\)') - - print __doc__ - print 'Last compilation : ' + (last_modified_time or 'none') - print 'Current Version : ' + current_version - print '' - - new_version = get_input_version(current_version) - - print 'New version : ' + new_version + '\n' - print 'Update version number ...' - replace_in_file(assemblyinfo_path, r'Version\("[.\d]+"\)', r'Version("{}")'.format(new_version)) - - print 'Delete previous builds ...' - clear_dir(r'.\FirefoxAddons\bin') - clear_dir(r'.\Selenium\bin\Release') - clear_dir(r'.\Selenium\obj\Release') - clear_dir(r'.\Selenium\bin\Help') - clear_dir(r'.\VbsConsole\bin\Release') - clear_dir(r'.\VbsConsole\obj\Release') - - print 'Build vb-format addin ...' - execute(APP_IRONPYTHON_PATH, __dir__ + r'\FirefoxAddons\build-vb-format.py', current_version) - - print 'Build implicit-wait addin ...' - execute(APP_IRONPYTHON_PATH, __dir__ + r'\FirefoxAddons\build-implicit-wait.py', current_version) - - print 'Build extensions package ...' - with zipfile.ZipFile(__dir__ + r'\FirefoxAddons\bin\extensions.xpi', 'a') as zip: - zip.write(__dir__ + r'\FirefoxAddons\install.rdf', 'install.rdf') - zip.write(__dir__ + r'\References\selenium-ide.xpi', 'selenium-ide.xpi') - zip.write(__dir__ + r'\FirefoxAddons\bin\vb-formatters.xpi', 'vb-formatters.xpi') - zip.write(__dir__ + r'\FirefoxAddons\bin\implicit-wait.xpi', 'implicit-wait.xpi') - - print 'Build .Net library ...' - execute(APP_MSBUILD_PATH, '/t:build', '/nologo', '/v:quiet', - r'/p:Configuration=Release;TargetFrameworkVersion=v3.5', - r'/p:RegisterForComInterop=False', - r'/p:SignAssembly=true;AssemblyOriginatorKeyFile=key.snk', - r'.\Selenium\Selenium.csproj' ) - - print 'Build Type libraries 32bits ...' - execute(APP_TLBEXP_PATH, r'.\Selenium\bin\Release\Selenium.dll', - r'/win32', r'/out:.\Selenium\bin\Release\Selenium32.tlb' ) - - print 'Build Type libraries 64bits ...' - execute(APP_TLBEXP_PATH, - r'.\Selenium\bin\Release\Selenium.dll', - r'/win64', r'/out:.\Selenium\bin\Release\Selenium64.tlb' ) - - print 'Build console runner ...' - execute(APP_MSBUILD_PATH, '/v:quiet', '/t:build', '/nologo', - r'/p:Configuration=Release;TargetFrameworkVersion=v3.5', - r'.\VbsConsole\VbsConsole.csproj' ) - - print 'Build documentation ...' - os.environ['SHFBROOT'] = APP_SHFBROOT_DIR - execute(APP_MSBUILD_PATH, '/p:Configuration=Release', '/nologo', '.\Selenium\Selenium.shfbproj') - - print 'Build registration file ...' - execute(APP_IRONPYTHON_PATH, r'gen-registration.ipy', \ - r'Selenium\bin\Release\Selenium.dll', __dir__ + r'\SeleniumBasicSetup.pas') - - print 'Rebuild excel files ...' - execute(APP_PYTHON_PATH, __dir__ + r'\rebuild_exel_files.py') - - print 'Build setup package ...' - execute(APP_INNOSETUP_PATH, '/q', '/O' + __dir__, __dir__ + r'\SeleniumBasicSetup.iss') - - print 'Launch install ...' - execute(__dir__ + '\SeleniumBasic-%s.exe' % new_version) - - print '\nDone' - - - -def set_working_dir(directory): - make_dir(directory) - os.chdir(directory) - -def make_dir(directory): - if not os.path.isdir(directory): - os.makedirs(directory) - -def remove_dir(directory): - if os.path.isdir(directory): - shutil.rmtree(directory) - -def copy_file(src, dest): - shutil.copyfile(src, dest) - -def clear_dir(directory): - if os.path.isdir(directory): - shutil.rmtree(directory) - os.makedirs(directory) - -def check_globals_exists(pattern): - items = globals() - miss_items = [k for k in items.keys() if re.search(pattern, k) \ - and type(items[k]) == type('') and not os.path.exists(items[k])] - if miss_items: - raise Exception('Invalide path(s):\n ' + '\n '.join( \ - ['{}: {}'.format(k, items[k]) for k in miss_items])) - -def get_file_mtime(filepath, format=None): - if(not os.path.isfile(filepath)): - return None - dt = datetime.datetime.fromtimestamp(os.path.getmtime(filepath)) - if format: - return dt.strftime(format) - return dt - -def match_in_file(filepath, pattern): - with open(filepath, 'r') as f: - result = re.search(pattern, f.read()) - return result.group(result.re.groups) - -def replace_in_file(filepath, pattern, replacement): - with open(filepath, 'r') as f: - text = re.sub(pattern, replacement, f.read()) - with open(filepath, 'w') as f: - f.write(text) - -class CommandException(Exception): - pass - -def execute(*arguments): - cmd = ' '.join(arguments) - Logger.write('cwd> ', os.getcwd()) - Logger.write('cmd> ', cmd) - p = subprocess.Popen(arguments, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate() - txtout = stdout.decode("utf-8") - Logger.write('info> ', txtout) - if p.returncode != 0: - txterr = stderr.decode("utf-8") - raise CommandException(cmd + '\n' + txtout + '\n' + txterr) - Logger.write('', '\n') - -def get_input(message): - try: - return raw_input(message) - except NameError: - return input(message) - -def get_input_version(version): - while True: - input = get_input('Digit to increment [w.x.y.z] or version [0.0.0.0] or skip [s] ? ').strip() - if re.match(r's|w|x|y|z', input) : - idx = {'s': 99, 'w': 0, 'x': 1, 'y': 2, 'z': 3}[input] - return '.'.join([str((int(v)+(i == idx))*(i <= idx)) for i, v in enumerate(version.split('.'))]) - elif re.match(r'\d+\.\d+\.\d+\.\d+', input): - return input - -class ZipFile(zipfile.ZipFile): - - def __init__(cls, file, mode): - zipfile.ZipFile.__init__(cls, file, mode) - - def add(self, path): - for item in glob.glob(path): - if os.path.isdir(item): - self.add(item + r'\*') - else: - self.write(item) - -class Logger: - - @staticmethod - def write(header, message): - header = time.strftime('%H:%M:%S') + (' ' + header if header else '') - txt = header + message.replace('\r\n', '\n') \ - .replace('\r', '').replace('\n', '\n' + header) + '\n' - Logger.file.write(txt) - - def __init__(self): - filename = re.sub(r'\.[^.]+$', '.log', __file__) - Logger.file = open(filename, mode='w', buffering = 1) - - def __enter__(self): - return self - - def __exit__(self, e_type, e_value, e_trace): - if e_type: - Logger.write('error>', ''.join(traceback.format_exception(e_type, e_value, e_trace))) - Logger.file.close() - if e_type: - subprocess.Popen(['cmd', '/C', Logger.file.name]) #open the log file - -if __name__ == '__main__': - with Logger() as log: - main() diff --git a/clean-folders.py b/clean-folders.py deleted file mode 100644 index 6a6dc03..0000000 --- a/clean-folders.py +++ /dev/null @@ -1,36 +0,0 @@ - -"""Utility script to clean the project files and folders -""" - -DELETE_FOLDERS_PATTERNS = ['bin', 'obj'] -DELETE_FILES_PATTERNS = ['*.suo', '*.bak', '*.pyc'] - -import sys, os, fnmatch, shutil - -__dir__ = os.path.dirname(os.path.realpath(__file__)) - -def main(): - print __doc__ - print "Folders : " + ', '.join(DELETE_FOLDERS_PATTERNS) - print "Files : " + ', '.join(DELETE_FILES_PATTERNS) - print "" - print "Start cleaning ..." - - for root_dir, folders, files in os.walk(__dir__): - - for del_dir in DELETE_FOLDERS_PATTERNS: - for dirname in fnmatch.filter(folders, del_dir): - dir_path = os.path.join(root_dir, dirname) - print "rm %s" % dir_path - shutil.rmtree(dir_path) - - for del_file in DELETE_FILES_PATTERNS: - for filename in fnmatch.filter(files, del_file): - file_path = os.path.join(root_dir, filename) - print "rm %s" % file_path - os.remove(file_path) - - print "\nDone" - -if __name__ == '__main__': - main() diff --git a/clean-registry.py b/clean-registry.py deleted file mode 100644 index 3dd25fe..0000000 --- a/clean-registry.py +++ /dev/null @@ -1,70 +0,0 @@ - -"""Utility script to clean the registry """ - -# To run the script from the context menu with elevation: -# [HKEY_CLASSES_ROOT\Python.File\shell\runas\command] -# @="C:\Program Files (x86)\Python27\python.exe" -i "%1" %* runas - -# Additional modules: -# pip install pywin32 - -STARTSWITH_PROGID = "Selenium." -STARTSWITH_GUID = "{0277FC34-FD1B-4616-BB19" - -import sys, os, io, sys, win32api, win32con - -__dir__ = os.path.dirname(os.path.realpath(__file__)) - -HKCR = win32con.HKEY_CLASSES_ROOT -HKLM = win32con.HKEY_LOCAL_MACHINE -HKCU = win32con.HKEY_CURRENT_USER -HKMAP = {HKCR:'HKCR', HKLM:'HKLM', HKCU:'HKCU'} - -VIEW32 = win32con.KEY_WOW64_32KEY | win32con.KEY_ALL_ACCESS -VIEW64 = win32con.KEY_WOW64_64KEY | win32con.KEY_ALL_ACCESS -VIEWMAP = {VIEW32:'32', VIEW64:''} - -def main(): - print __doc__ - print "Start cleaning registry ..." - print "" - - for view in (VIEW64, VIEW32): - hRoot = HKCR - for subkey1 in [r'', r'TypeLib', r'CLSID', r'Interface', r'Record']: - hKey = win32api.RegOpenKeyEx(hRoot, subkey1, 0, view) - for subkey2, r, c, l in win32api.RegEnumKeyEx(hKey): - if subkey2.startswith(STARTSWITH_GUID) or subkey2.startswith(STARTSWITH_PROGID): - print '\\'.join((HKMAP[hRoot] + VIEWMAP[view], subkey1, subkey2)).replace('\\\\', '\\') - try: - win32api.RegDeleteTree(hKey, subkey2) - except Exception as ex : - print ' failed: %s' % ex.strerror - win32api.RegCloseKey(hKey) - - print "\nDone" - -def elevate(): - import ctypes, win32com.shell.shell, win32event, win32process - outpath = r'%s\%s.out' % (os.environ["TEMP"], os.path.basename(__file__)) - if ctypes.windll.shell32.IsUserAnAdmin(): - if os.path.isfile(outpath): - sys.stderr = sys.stdout = open(outpath, 'w', 0) - return - with open(outpath, 'w+', 0) as outfile: - hProc = win32com.shell.shell.ShellExecuteEx(lpFile=sys.executable, \ - lpVerb='runas', lpParameters=' '.join(sys.argv), fMask=64, nShow=0)['hProcess'] - while True: - hr = win32event.WaitForSingleObject(hProc, 40) - while True: - line = outfile.readline() - if not line: break - sys.stdout.write(line) - if hr != 0x102: break - os.remove(outpath) - sys.stderr = '' - sys.exit(win32process.GetExitCodeProcess(hProc)) - -if __name__ == '__main__': - elevate() - main() diff --git a/gen-registration.ipy b/gen-registration.ipy deleted file mode 100644 index 124f2ee..0000000 --- a/gen-registration.ipy +++ /dev/null @@ -1,100 +0,0 @@ - -"""Script to create the registration file for innosetup. -Usage: ipy gen-registration.ipy c:\\assembly.dll c:\\outfile.pas -""" - -import os, sys, re, argparse, subprocess, tempfile -import clr, System -from System.Reflection import Assembly, AssemblyDescriptionAttribute -from System.Runtime.InteropServices import GuidAttribute, TypeLibVersionAttribute \ - , ComVisibleAttribute, InterfaceTypeAttribute, ProgIdAttribute - -PROXYSTUBS = [ - '{00020424-0000-0000-C000-000000000046}', #DUAL: PSOAInterface - '{00020424-0000-0000-C000-000000000046}', #IUnknown: PSOAInterface - '{00020420-0000-0000-C000-000000000046}' #IDispatch: PSDispatch -] - -def main(args): - in_dll = os.path.abspath(args[0]) - out_file = os.path.abspath(args[1]) - - if not os.path.isfile(in_dll): - raise Exception('Assembly not found: "%s"' % in_dll) - - print "Assembly : .\%s" % os.path.relpath(in_dll, os.getcwd()) - print "OutFile : .\%s" % os.path.relpath(out_file, os.getcwd()) - print "" - print 'Parse the .Net assembly ...' - - assembly = Assembly.LoadFile(in_dll) - assembly_fullname = assembly.FullName - assembly_filename = os.path.basename(in_dll) - ass_guid = GetAttribute(assembly, GuidAttribute) - ass_vers = GetAttribute(assembly, TypeLibVersionAttribute) - ass_desc = GetAttribute(assembly, AssemblyDescriptionAttribute) - - lines_classes = [] - lines_interfaces = [] - lines_values = [] - - types = assembly.GetExportedTypes() - for type in types: - comvisible = GetAttribute(type, ComVisibleAttribute, silent = True) - if comvisible and comvisible.Value == True: - att_guid = GetAttribute(type, GuidAttribute) - guid = '{' + att_guid.Value + '}' - if type.IsClass: - hasConstructor = type.GetConstructor(System.Type.EmptyTypes) is not None - if hasConstructor: - progid = GetAttribute(type, ProgIdAttribute).Value - lines_classes.Add("RegClass(lib, '%s', '%s', '%s');" % (guid, progid, type.FullName)) - elif type.IsValueType: - lines_values.Add("RegRecord(lib, '%s', '%s');" % (guid, type.FullName)) - elif type.IsInterface: - interfaceType = GetAttribute(type, InterfaceTypeAttribute).Value - proxystub = PROXYSTUBS[int(interfaceType)] - lines_interfaces.Add("RegInterface(lib, '%s', '%s', '%s');" % (guid, type.Name, proxystub)) - - - print 'Genereate the registration file ...' - - with open(out_file, 'w') as f : - f.write("{\n") - f.write(" Code generated at each build by gen-registration.ipy\n") - f.write(" This is a subset of SeleniumBasicSetup.iss\n") - f.write("}\n\n") - - f.write("lib.Guid := '{%s}';\n" % ass_guid.Value) - f.write("lib.FullName := '%s';\n" % assembly_fullname) - f.write("lib.Description := '%s';\n" % ass_desc.Description) - f.write("lib.TypeVersion := '%s.%s';\n" % (ass_vers.MajorVersion, ass_vers.MinorVersion)) - f.write("lib.PathDll := ExpandConstant('{app}\%s');\n" % assembly_filename) - f.write("lib.PathTlb32 := ExpandConstant('{app}\%s');\n" % re.sub('\.[^.]+$', '32.tlb', assembly_filename)) - f.write("lib.PathTlb64 := ExpandConstant('{app}\%s');\n" % re.sub('\.[^.]+$', '64.tlb', assembly_filename)) - f.write("lib.Runtime := '%s';\n" % assembly.ImageRuntimeVersion) - f.write("\n") - f.write("RegTypeLib(lib);\n") - for lines in [lines_classes, lines_interfaces, lines_values]: - f.write('\n') - lines.sort() - for line in lines: - f.write(line + '\n') - - print "\nDone" - -def GetAttribute(obj, att_typename, silent = False): - type = clr.GetClrType(att_typename) - attributes = obj.GetCustomAttributes(type, False) - if attributes.Length == 0 : - if not silent: - raise Exception("Attribute {0} is missing on type {1}".format(type.Name, obj.FullName)) - return None - return attributes[0] - -if __name__ == '__main__': - if len(sys.argv) == 3 : - main(sys.argv[1:]) - else: - print __doc__ - diff --git a/rebuild_exel_files.py b/rebuild_exel_files.py deleted file mode 100644 index 1c7f423..0000000 --- a/rebuild_exel_files.py +++ /dev/null @@ -1,164 +0,0 @@ - -"""Rebuild Excel files -""" - -import sys, os, shutil, zipfile, re, glob, tempfile -from win32com import client #http://sourceforge.net/projects/pywin32/files/pywin32/ - -def main(args): - print __doc__ - - for folder in (r'\Templates', r'\Examples\Excel'): - rebuild(__dir__ + folder) - - print "\nDone" - - -def rebuild(directory): - xlbin_dir = make_dir(directory + r'\Xlbin') - - for file in glob.glob(directory + r"/*.xl?m"): - shortname = get_shortname(file) - extention = get_extention(file) - - print "Extract %s ..." % file - tmp_folder = extract_excel(file) - - print "Rebuild %s ..." % shortname - (wb, wb_file) = build_excel(tmp_folder, extention) - wb.VBProject.References.AddFromGuid("{0277FC34-FD1B-4616-BB19-A9AABCAF2A70}", 2, 0) - - if extention == ".xlsm": - print "Save %s.xlsm ..." % shortname - wb.SaveAs(directory + r'\%s.xlsm' % shortname, xlOpenXMLWorkbookMacroEnabled) - print "Save %s.xls ..." % shortname - wb.SaveAs(xlbin_dir + r'\%s.xls' % shortname, xlWorkbook8) - - elif extention == ".xltm": - print "Save %s.xltm ..." % shortname - wb.SaveAs(directory + r'\%s.xltm' % shortname, xlOpenXMLTemplateMacroEnabled) - print "Save %s.xlt ..." % shortname - wb.SaveAs(xlbin_dir + r'\%s.xlt' % shortname, xlTemplate8) - - wb.Application.Quit() - shutil.rmtree(tmp_folder) - os.remove(wb_file) - print "" - -def build_excel(folder, extension): - # Create excel file for folder - file = tempfile.NamedTemporaryFile(suffix=extension).name - build_zip(folder, file, r".*", r"^vba\\") - - # Add VBA code - xl = CreateObject("Excel.Application") - xl.EnableEvents = False - xl.DisplayAlerts = False - wb = xl.Workbooks.Open(file) - components = wb.VBProject.VBComponents - for (dirpath, dirnames, filenames) in os.walk(folder + r"\vba"): - for filename in filenames: - file_path = os.path.join(dirpath, filename) - if filename.find(".") == -1: - module = components.Item(filename).CodeModule - with open(file_path, "r") as f: - code = f.read() - module.InsertLines(1, code) - else: - components.Import(file_path) - return (wb, file) - - -def extract_excel(file): - # Create temporary folders - folder = tempfile.mkdtemp() - dir_vba = make_dir(folder + r'\vba') - - # Extract the file - extract_zip(file, folder, r".*", r".*vbaProject\.bin$") - - # Extract the VBA code - xl = CreateObject("Excel.Application") - xl.EnableEvents = False - wb = xl.Workbooks.Open(file) - try: - for item in wb.VBProject.VBComponents: - if item.Type < 4: - ext = extensions.get(item.Type, None) - item.Export(r"%s\%s.%s" % (dir_vba, item.Name, ext)) - else: - module = item.CodeModule - count = module.CountOfLines - if count: - with open(r"%s\%s" % (dir_vba, item.Name), "w") as f: - code = module.Lines(1, count) - f.write(code) - finally: - wb.Close(False) - xl.Quit() - - return folder - - -def CreateObject(progid): - return client.Dispatch(progid) - -def make_dir(directory): - if not os.path.isdir(directory): - os.makedirs(directory) - return directory - -def get_shortname(path): - start = path.rfind("\\") + 1 - end = max(0, path.rfind(".", start)) - return path[start: end or None] - -def get_extention(path): - start = path.rfind('.') - return '' if start == -1 else path[start:] - -def extract_zip(filename, folder, pattern_include, pattern_exclude): - p_inc = re.compile(pattern_include) - p_exc = re.compile(pattern_exclude) - with zipfile.ZipFile(filename, mode="r") as zip: - for name in zip.namelist(): - if p_inc.match(name) and not p_exc.match(name): - dest_file = folder + '\\' + name - dest_dir = os.path.dirname(dest_file) - if not os.path.isdir(dest_dir): - os.makedirs(dest_dir) - with open(dest_file , 'wb') as file: - file.write(zip.read(name)) - -def build_zip(folder, filename, pattern_include, pattern_exclude): - p_inc = re.compile(pattern_include) - p_exc = re.compile(pattern_exclude) - with zipfile.ZipFile(filename, mode="w", compression=8) as zip: - for (dirpath, dirnames, filenames) in os.walk(folder): - for file in filenames: - abs_path = os.path.join(dirpath, file) - rel_path = os.path.relpath(abs_path, folder) - if p_inc.match(rel_path) and not p_exc.match(rel_path): - zip.write(abs_path, rel_path) - - -xlTemplate8 = 17 -xlWorkbook8 = 56 -xlOpenXMLWorkbookMacroEnabled = 52 -xlOpenXMLTemplateMacroEnabled = 53 - -vbext_ct_StdModule = 1 -vbext_ct_ClassModule = 2 -vbext_ct_MSForm = 3 -vbext_ct_Document = 100 - -extensions = { - vbext_ct_StdModule : 'bas', - vbext_ct_ClassModule : 'cls', - vbext_ct_MSForm : 'frm' -} - -__dir__ = os.path.dirname(os.path.realpath(__file__)) - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/smoke-tests.py b/smoke-tests.py deleted file mode 100644 index 2ad3956..0000000 --- a/smoke-tests.py +++ /dev/null @@ -1,85 +0,0 @@ - -"""Basic tests to ensure all the browsers are launchable with the COM server -""" - -import sys, os, unittest, threading, time, multiprocessing, BaseHTTPServer -from win32com import client #http://sourceforge.net/projects/pywin32/files/pywin32/ - -SERVER_ADDRESS = ('127.0.0.1', 9393) -SERVER_PAGE = """ - - - Title - - - Test page - - -""" - -class Suite(unittest.TestCase): - - def test_list(self): - lst = CreateObject("Selenium.List") - for i in range(0, 10): - lst.add(i) - self.assertEqual(10, lst.Count) - - def test_firefox(self): - self.assert_browser_display_page("Selenium.FirefoxDriver") - - def test_iedriver(self): - self.assert_browser_display_page("Selenium.IEDriver") - - def test_chrome(self): - self.assert_browser_display_page("Selenium.ChromeDriver") - - def test_opera(self): - self.assert_browser_display_page("Selenium.OperaDriver") - - def test_phantomjs(self): - self.assert_browser_display_page("Selenium.PhantomJSDriver") - - def assert_browser_display_page(self, progid): - driver = CreateObject(progid) - try: - driver.get("http://%s:%s" % SERVER_ADDRESS) - txt = driver.FindElementById('link').Text - self.assertEqual("Test page", txt) - finally: - driver.quit - - - -def CreateObject(progid): - return client.Dispatch(progid) - -def RunHTTPServer(): - server = BaseHTTPServer.HTTPServer(SERVER_ADDRESS, HTTPServerHandler) - server.serve_forever() - -class HTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): - - def handle(self): - try: - return BaseHTTPServer.BaseHTTPRequestHandler.handle(self) - except: return - - def log_message(self, format, *args): - return - - def do_GET(s): - s.send_response(200) - s.send_header('Content-type', 'text/html') - s.end_headers() - s.wfile.write(SERVER_PAGE) - - -if __name__ == '__main__': - print __doc__ - print "Start tests ...\n" - server = multiprocessing.Process(target=RunHTTPServer) - server.start() - try: - unittest.main() - except SystemExit: pass \ No newline at end of file diff --git a/update-references.py b/update-references.py deleted file mode 100644 index e0a786a..0000000 --- a/update-references.py +++ /dev/null @@ -1,455 +0,0 @@ - -"""Script to download the references used by the project -""" - -import sys, os, time, types, re, traceback, threading, io, datetime, csv, json, urllib, requests, zipfile, tarfile -from win32api import GetFileVersionInfo, LOWORD, HIWORD - -__dir__ = os.path.dirname(os.path.realpath(__file__)) - -def main(): - set_working_dir(__dir__ + r'\References\\') - - print __doc__ - print 'Last update : ' + file_datetime('references.json', format='%Y-%m-%d %H:%M:%S') - print '' - - Log('Update references ...') - - #run tasks in parallel - with ConfigFile('references.json') as config: - tasks = Tasks(config) - exitcode = ParallelWorker(tasks).run(pattern='^update_') - - if exitcode: - print '\nFailed!' - sys.stderr = '' - sys.exit(1) - else: - print '\nDone' - -class Tasks(): - - def __init__(self, configs): - self.cfgs = configs - - def update_FirefoxDriver(self): - page = r"https://pypi.python.org/pypi/selenium" - pattern = 'selenium-([\d\.]+).tar.gz' - value, version = WebSource(page).findlastversion(pattern, group_value=0, group_version=1) - url = r'https://pypi.python.org/packages/source/s/selenium/' + value - cfg = self.cfgs.get('FirefoxDriver') - if cfg.get('version') != version or not file_exists('firefoxdriver.xpi'): - with WebGZip(url) as gzip: - #open the xpi file as a zip - xpi_bytes = gzip.read(r'.*\webdriver.xpi') - zip_in = zipfile.ZipFile(xpi_bytes) - zip_out = zipfile.ZipFile('firefoxdriver.xpi', mode='w', compression=0) - #copy all the files except the linux ones and remove their references from the manifest - for fileinfo in zip_in.infolist(): - filename = fileinfo.filename - if filename.endswith(r'chrome.manifest'): #edit manifest - manifest_txt = zip_in.read(filename) - manifest_txt = re.sub(r'^binary-component platform/Linux.*$\s*', \ - '', manifest_txt, flags=re.MULTILINE) - zip_out.writestr(fileinfo, manifest_txt, compress_type=0) - elif not filename.endswith('.so'): #skip linux files - bytes = zip_in.read(filename) - zip_out.writestr(fileinfo, bytes, compress_type=0) - zip_out.close() - zip_in.close() - cfg.update({'version': version, 'url': url}) - Log("Updated FirefoxDriver to version " + version) - - def update_FirefoxPrefs(self): - url = r"https://raw.githubusercontent.com/SeleniumHQ/selenium/master/javascript/firefox-driver/webdriver.json" - version = WebSource(url).getEtag() - cfg = self.cfgs.get('FirefoxPrefs') - if cfg.get('version') != version or not file_exists('firefox-prefs.js'): - source = WebSource(url).gettext().decode('utf-8') - content = json.loads(source) - with open("firefox-prefs.js", 'w') as file: - for mainkey in ["frozen", "mutable"]: - mainobj = content[mainkey] - for key in sorted(mainobj.keys()): - value = mainobj[key] - if isinstance(value, basestring): - txt = 'user_pref("' + key + '", "' + value + '");\n' - else: - txt = 'user_pref("' + key + '", ' + str(value).lower() + ');\n' - file.write(txt) - file.write('\n') - cfg.update({'version': version, 'url': url}) - Log("Updated FirefoxPrefs to version " + version) - - def skip_SeleniumLibraries(self): - page = r"http://selenium-release.storage.googleapis.com" - pattern = r'([\d\.]+/selenium-dotnet-([\d\.]+).zip)' - value, version = WebSource(page).findlastversion(pattern, group_value=1, group_version=2) - url = page + '/' + value - cfg = self.cfgs.get('.NetLibraries') - if cfg.get('version') != version or not file_exists('WebDriver.dll'): - with WebZip(url) as zip: - zip.extract(r'^net35/.') - WebFile('http://selenium.googlecode.com/git/dotnet/CHANGELOG') \ - .save('WebDriver.changelog.txt') - cfg.update({'version': version, 'url': url}) - Log("Updated Selenium .Net to version " + version) - - def update_IE32(self): - url = r"https://github.com/SeleniumHQ/selenium/raw/master/cpp/prebuilt/Win32/Release/IEDriverServer.exe" - version = WebSource(url).getEtag() - cfg = self.cfgs.get('IEDriver') - if cfg.get('version') != version or not file_exists('iedriver.exe'): - WebFile(url).save('iedriver.exe') - cfg.update({'version': version, 'url': url}) - file_version = get_version_number(r'iedriver.exe') - Log("Updated IE32 driver to version " + file_version) - - def update_IE64(self): - page = r"http://selenium-release.storage.googleapis.com/" - pattern = r'([\d\.]+/IEDriverServer_x64_([\d\.]+).zip)' - value, version = WebSource(page).findlastversion(pattern, group_value=1, group_version=2) - url = page + value - cfg = self.cfgs.get('IE64Driver') - if cfg.get('version') != version or not file_exists('iedriver64.exe'): - with WebZip(url) as zip: - zip.extract(r'IEDriverServer.exe', 'iedriver64.exe') - cfg.update({'version': version, 'url': url}) - Log("Updated IE64 driver to version " + version) - - def update_SeleniumIDE(self): - page = r'https://addons.mozilla.org/en-US/firefox/addon/selenium-ide/' - pattern = r'https://addons.mozilla.org/firefox/downloads/file/\d+/selenium_ide-(\d\.\d\.\d)[^?"]+' - url, version = WebSource(page).findlastversion(pattern, group_value=0, group_version=1) - cfg = self.cfgs.get('SeleniumIDE') - if cfg.get('version') != version or not file_exists('selenium-ide.xpi'): - WebFile(url).save('selenium-ide.xpi') - cfg.update({'version': version, 'url': url}) - Log("Updated Selenium IDE to version " + version) - - def update_ChromeDriver(self): - page = r"http://chromedriver.storage.googleapis.com/" - version = WebSource(page + r'LATEST_RELEASE').gettext().strip() - url = page + version + r'/chromedriver_win32.zip' - cfg = self.cfgs.get('ChromeDriver') - if cfg.get('version') != version or not file_exists('chromedriver.exe'): - with WebZip(url) as zip: - zip.extract(r'chromedriver.exe') - cfg.update({'version': version, 'url': url}) - Log("Updated Chrome driver to version " + version) - - def update_PhantomJS(self): - page = r'https://bitbucket.org/ariya/phantomjs/downloads/' - pattern = r'phantomjs-([\d\.]+)-windows.zip' - value, version = WebSource(page).findlastversion(pattern, group_value=0, group_version=1) - url = page + value - cfg = self.cfgs.get('PhantomJSDriver') - if cfg.get('version') != version or not file_exists('phantomjs.exe'): - with WebZip(url) as zip: - zip.extract(r'.*/phantomjs.exe') - cfg.update({'version': version, 'url': url}) - Log("Updated PhantomJS to version " + version) - - def skip_Safari(self): - page = r"http://selenium-release.storage.googleapis.com/" - pattern = r'(([\d\.]+)/SafariDriver.safariextz)' - value, version = WebSource(page).findlastversion(pattern, group_value=1, group_version=2) - url = page + value - cfg = self.cfgs.get('SafariDriver') - if cfg.get('version') != version or not file_exists('SafariDriver.safariextz'): - WebFile(url).save('SafariDriver.safariextz') - cfg.update({'version': version, 'url': url}) - Log("Updated Safari driver to version " + version) - - def update_Opera(self): - page = r'https://api.github.com/repos/operasoftware/operachromiumdriver/releases' - pattern = r'/v([\d\.]+)/operadriver_win32.zip' - value, version = WebSource(page).findlastversion(pattern, group_value=0, group_version=1) - url = r'https://github.com/operasoftware/operachromiumdriver/releases/download' + value - cfg = self.cfgs.get('OperaDriver') - if cfg.get('version') != version or not file_exists('operadriver.exe'): - with WebZip(url) as zip: - zip.extract(r'operadriver.exe') - cfg.update({'version': version, 'url': url}) - Log("Updated Opera driver to version " + version) - - def update_FirefoxWires(self): - page = r'https://api.github.com/repos/jgraham/wires/releases' - pattern = r'/([\d\.]+)/wires-[\d\.]+-windows.zip' - value, version = WebSource(page).findlastversion(pattern, group_value=0, group_version=1) - url = r'https://github.com/jgraham/wires/releases/download' + value - cfg = self.cfgs.get('FirefoxWiresDriver') - if cfg.get('version') != version or not file_exists('wires.exe'): - with WebZip(url) as zip: - zip.extract(r'wires.exe') - cfg.update({'version': version, 'url': url}) - Log("Updated Firefox Wire driver to version " + version) - - def skip_PdfSharp(self): - page1 = r'http://sourceforge.net/projects/pdfsharp/files/pdfsharp' - pattern = r'/PDFsharp%20([\d\.]+)' - value, version = WebSource(page1).findlastversion(pattern, group_value=0, group_version=1) - page2 = page1 + value - pattern = r'([^/]+/[^/]*Assemblies[^/]*\.zip)/download' - value = WebSource(page2).findfirst(pattern) - url = r'http://sunet.dl.sourceforge.net/project/pdfsharp/pdfsharp/' + value - cfg = self.cfgs.get('PDFsharp') - if cfg.get('version') != version or not file_exists( 'PdfSharp.dll'): - with WebZip(url) as zip: - zip.extract(r'.*/PdfSharp.dll') - cfg.update({'version': version, 'url': url}) - Log("Updated PDF Sharp to version " + version) - - def skip_DotNetZip(self): - page = r'https://olex-secure.openlogic.com/packages/dotnetzip' - pattern = r'https[^"]+dotnetzip-([\d.]+)[^"]+\.zip' - value, version = WebSource(page).findlastversion(pattern, group_value=0, group_version=1) - url = urllib.unquote(value) - cfg = self.cfgs.get('DotNetZip') - if cfg.get('version') != version or not file_exists('Ionic.Zip.dll'): - with WebZip(url) as zip: - zip.extract(r'.*/Release/Ionic.Zip.dll') - cfg.update({'version': version, 'url': url}) - Log("Updated DotNetZip to version " + version) - - -def set_working_dir(folder): - if not os.path.isdir(folder): - os.makedirs(folder) - os.chdir(folder) - -def file_exists(file_path): - return os.path.isfile(file_path); - -def file_datetime(filepath, format='%c', default='none'): - if not os.path.isfile(filepath) : - return default - return datetime.datetime.fromtimestamp(os.path.getmtime(filepath)).strftime(format) - -def format_ex(e_type, e_value, e_trace): - lines = [] - for filename, lineno, name, line in traceback.extract_tb(e_trace): - lines.append(' in %s() line %d in %s\n' % (name, lineno, os.path.basename(filename))) - if line: - lines.append(' ' + line.strip() + '\n') - return '\n#%s:\n%s\n\n%s' % (e_type.__name__, str(e_value), ''.join(lines)) - -def get_version_number(filename): - try: - info = GetFileVersionInfo(os.path.realpath(filename), "\\") - ms = info['FileVersionMS'] - ls = info['FileVersionLS'] - return '.'.join([str(v) for v in (HIWORD(ms), LOWORD(ms), HIWORD(ls), LOWORD(ls))]) - except: - return '0.0.0.0' - -class ConfigFile(dict): - - def __init__(self, filepath): - self.lock = threading.Lock() - self.filepath = filepath - if os.path.isfile(filepath): - with open(filepath, 'r') as file: - self.data = json.load(file) - else: - self.data = {} - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - with open(self.filepath, 'w') as file: - json.dump(self.data, file, sort_keys=False, indent=4, ensure_ascii=False) - - def get(self, key): - with self.lock: - return self.data.setdefault(key, {}) - -from Queue import Queue - -class ParallelWorker(Queue): - - def __init__(self, instance, max_workers=10): - Queue.__init__(self) - self.instance = instance - self.exitcode = 0 - self.max_workers = max_workers - - def __run__(self): - while not self.empty(): - method = self.get() - try: - method() - except Exception as ex: - self.exitcode = 1 - e_type, e_value, e_trace = sys.exc_info() - sys.stderr.write(format_ex(e_type, e_value, e_trace.tb_next)) - finally: - self.task_done() - sys.exc_clear() - - def run(self, pattern=''): - methods = [getattr(self.instance, k) for k, v in self.instance.__class__.__dict__.items() \ - if isinstance(v, types.FunctionType) and re.search(pattern, k)] - nb_workers = min(self.max_workers, len(methods)) - for method in methods: - self.put(method) - for i in range(nb_workers): - t = threading.Thread(target=self.__run__) - t.daemon = True - t.start() - self.join() - return self.exitcode - -class WebGZip: - - def __init__(self, url): - response = requests.get(url) - response.raise_for_status() - buffer = io.BytesIO(response.content) - self.tar = tarfile.open(fileobj=buffer, mode='r:gz') - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - self.tar.close() - - def extract(self, pattern, dest = '.'): - p = re.compile(pattern) - destIsdir = os.path.isdir(dest) - for tarinfo in self.tar.getmembers(): - name = tarinfo.name - if p.match(name) and not tarinfo.isdir(): - dest_file = dest + '\\' + os.path.basename(name) if destIsdir else dest - with open(dest_file , 'wb') as file: - file.write(self.tar.extractfile(tarinfo).read()) - if not destIsdir: - return - - def read(self, pattern): - p = re.compile(pattern) - for tarinfo in self.tar.getmembers(): - if p.match(tarinfo.name) and not tarinfo.isdir(): - return self.tar.extractfile(tarinfo) - -class WebZip: - - def __init__(self, url): - response = requests.get(url) - response.raise_for_status() - buffer = io.BytesIO(response.content) - self.zip = zipfile.ZipFile(buffer) - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - self.zip.close() - - def extract(self, pattern, dest='.'): - p = re.compile(pattern) - destIsdir = os.path.isdir(dest) - for name in self.zip.namelist(): - if p.match(name): - dest_file = dest + '\\' + os.path.basename(name) if destIsdir else dest - with open(dest_file , 'wb') as file: - file.write(self.zip.read(name)) - if not destIsdir: - return - -class WebFile: - - def __init__(self, url): - self.url = url - - def save(self, file): - response = requests.get(self.url, stream=True) - response.raise_for_status() - with open(file, 'wb') as handle: - for block in response.iter_content(1024): - if not block: - break - handle.write(block) - - -class WebSource: - - def __init__(self, url): - self.url = url - self.text = None - - def gettext(self): - if self.text is None: - self.text = requests.get(self.url).text - return self.text - - def findfirst(self, pattern, group=-1): - res = re.search(pattern, self.gettext()) - if not res: - raise PatternNotFound(pattern, self.url) - return res.group(res.re.groups if group == -1 else group) - - def getEtag(self, default='none'): - try: - etag = requests.head(self.url).headers['etag'] - return re.search(r'[\w-]+', etag).group(0) - except: - return default - - def findlastversion(self, pattern, group_value=1, group_version=2): - match = re.finditer(pattern, self.gettext()) - lst_versions = [(m.group(group_value), m.group(group_version), \ - map(int, m.group(group_version).split('.'))) for m in match] - if not lst_versions: - raise PatternNotFound(pattern, self.url) - lst_versions.sort(key=lambda m: m[2], reverse=True) - return (lst_versions[0][0], lst_versions[0][1]) - - def findlastdate(self, pattern, group_value=1, group_datetime=2, datetime_format='%Y-%m-%dT%H:%M:%S'): - match = re.finditer(pattern, self.gettext()) - lst_dates = [(m.group(group_value), time.strptime(m.group(group_datetime), datetime_format) \ - ) for m in match] - if not lst_dates: - raise PatternNotFound(pattern, self.url) - lst_dates.sort(key=lambda m: m[1], reverse=True) - return lst_dates[0] - -class PatternNotFound(Exception): - - def __init__(self, pattern, source): - self.__data__ = (pattern, source) - - def __str__(self): - return 'Pattern "%s" not found in %s' % self.__data__ - -def Log(message): - with Logger.lock: - print message - Logger.write(message) - -class Logger: - - @staticmethod - def write(message, header = ''): - header = time.strftime('%Y-%m-%d %H:%M:%S') + ' ' + header - txt = header + message.replace('\r\n', '\n').replace('\n', '\n' + header) + '\n' - Logger.logfile.write(txt) - - def __init__(self): - Logger.lock = threading.Lock() - filename = re.sub(r'\.[^.]+$', '.log', __file__) - Logger.logfile = open(filename, mode='a', buffering = 1) - - def __enter__(self): - return self - - def __exit__(self, e_type, e_value, e_trace): - if e_type: - Logger.write(''.join(traceback.format_exception(e_type, e_value, e_trace)), 'Err') - Logger.logfile.close() - -if __name__ == '__main__': - with Logger() as log: - main() From 5badb8dda68d58252e083b38ff9a2629e9f93c99 Mon Sep 17 00:00:00 2001 From: zc2 Date: Tue, 3 Oct 2023 15:18:40 -0400 Subject: [PATCH 16/16] many changes --- Sandcastle/COMdescrToXML.awk | 37 +++-- Sandcastle/SeleniumBasic.shfbproj | 28 ++-- Scripts/StartEdge.vbs | Bin 222 -> 274 bytes Selenium.Tests/Internals/BaseBrowsers.cs | 55 +++--- Selenium.Tests/Internals/BaseExcel.cs | 8 +- .../Internals/IgnoreFixtureAttribute.cs | 2 +- Selenium.Tests/Pages/element.html | 8 +- Selenium.Tests/Program.cs | 13 +- Selenium.Tests/Selenium.Tests.csproj | 65 +++++++- Selenium.Tests/TS_Actions.cs | 14 +- Selenium.Tests/TS_Alert.cs | 14 +- Selenium.Tests/TS_By.cs | 8 +- Selenium.Tests/TS_Capture.cs | 16 +- Selenium.Tests/TS_Element.cs | 16 +- Selenium.Tests/TS_ExecuteScript.cs | 14 +- Selenium.Tests/TS_Frame.cs | 14 +- Selenium.Tests/TS_Keyboard.cs | 14 +- Selenium.Tests/TS_Manage.cs | 30 +++- Selenium.Tests/TS_Mouse.cs | 14 +- Selenium.Tests/TS_Scraping.cs | 35 +--- Selenium.Tests/TS_SearchContext.cs | 20 +-- Selenium.Tests/TS_Select.cs | 14 +- Selenium.Tests/TS_Window.cs | 14 +- Selenium.Tests/TS_Windows.cs | 14 +- Selenium.Tests/packages.config | 0 Selenium/Application.cs | 13 +- Selenium/Assert.cs | 13 +- Selenium/ComInterfaces/_Application.cs | 10 +- Selenium/ComInterfaces/_Assert.cs | 8 +- Selenium/ComInterfaces/_Keys.cs | 5 + Selenium/ComInterfaces/_List.cs | 7 +- Selenium/ComInterfaces/_Shadow.cs | 0 Selenium/ComInterfaces/_Verify.cs | 4 + Selenium/ComInterfaces/_WebDriver.cs | 2 +- Selenium/ComInterfaces/_WebElement.cs | 6 +- Selenium/Common/Cookie.cs | 2 +- Selenium/Common/Image.cs | 4 +- Selenium/Common/Logs.cs | 25 +++ Selenium/Common/SelectElement.cs | 27 +-- Selenium/Common/Shadow.cs | 0 Selenium/Common/Timeouts.cs | 6 +- Selenium/Common/WebElement.cs | 72 ++++++-- Selenium/Common/WebElements.cs | 16 +- Selenium/Core/DriverService.cs | 6 +- Selenium/Core/RemoteServer.cs | 23 +-- Selenium/Core/RemoteSession.cs | 9 +- Selenium/Core/SysWaiter.cs | 1 + Selenium/Drivers/GeckoDriver.cs | 0 Selenium/Errors/WebRequestErrors.cs | 132 ++++++++++----- Selenium/Internal/EndPointExt.cs | 27 ++- Selenium/Internal/ObjExt.cs | 2 +- Selenium/Internal/ProcessExt.cs | 19 ++- Selenium/Selenium.csproj | 11 +- Selenium/SeleniumError.cs | 37 ++++- Selenium/SeleniumException.cs | 10 +- Selenium/Struct/List.cs | 56 +++++-- Selenium/Verify.cs | 10 +- Selenium/WebDriver.cs | 43 +++-- SeleniumBasic.sln | 31 ++-- Setup/Setup.vdproj | 157 +++++++++++++----- VbsConsole/app.config | 0 regasm.bat | 0 62 files changed, 828 insertions(+), 433 deletions(-) mode change 100755 => 100644 Sandcastle/COMdescrToXML.awk mode change 100755 => 100644 Sandcastle/SeleniumBasic.shfbproj mode change 100755 => 100644 Selenium.Tests/packages.config mode change 100755 => 100644 Selenium/ComInterfaces/_Shadow.cs mode change 100755 => 100644 Selenium/Common/Shadow.cs mode change 100755 => 100644 Selenium/Drivers/GeckoDriver.cs mode change 100755 => 100644 Setup/Setup.vdproj mode change 100755 => 100644 VbsConsole/app.config mode change 100755 => 100644 regasm.bat diff --git a/Sandcastle/COMdescrToXML.awk b/Sandcastle/COMdescrToXML.awk old mode 100755 new mode 100644 index af68e26..dd81311 --- a/Sandcastle/COMdescrToXML.awk +++ b/Sandcastle/COMdescrToXML.awk @@ -7,6 +7,24 @@ BEGIN { print " " } +function TranslateType( par ) { +#print par + gsub(/^\s+/,"",par) + gsub(/\s.+$/,"",par) + gsub(/^\[MarshalAs\(UnmanagedType.Struct\)(, In)?\]/,"",par) + gsub(/^By/, "Selenium.By", par) + gsub(/^Strategy/,"Selenium.Strategy",par) + gsub(/^WebEleme/,"Selenium.WebEleme",par) + gsub(/^string/, "System.String", par) + gsub(/^object/, "System.Object", par) + gsub(/^bool/, "System.Boolean", par) + gsub(/^double/, "System.Double", par) + gsub(/^float/, "System.Single", par) + gsub(/^int/, "System.Int32", par) + gsub(/^short/, "System.Int16", par) + return par +} + { if( match($0,/Description\("(.+?)"\)/,d) ) { descr = d[1] @@ -20,21 +38,7 @@ BEGIN { split(m[2],pars,",") ps = "" for( pi in pars ) { - par = pars[pi] -#print par - gsub(/^\s+/,"",par) - gsub(/\s.+$/,"",par) - gsub(/^\[MarshalAs\(UnmanagedType.Struct\)(, In)?\]/,"",par) - gsub(/^By/, "Selenium.By", par) - gsub(/^Strategy/,"Selenium.Strategy",par) - gsub(/^WebEleme/,"Selenium.WebEleme",par) - gsub(/^string/, "System.String", par) - gsub(/^object/, "System.Object", par) - gsub(/^bool/, "System.Boolean", par) - gsub(/^double/, "System.Double", par) - gsub(/^float/, "System.Single", par) - gsub(/^int/, "System.Int32", par) - gsub(/^short/, "System.Int16", par) + par = TranslateType( pars[pi] ) ps = ps ? ps "," par : par } if( ps ) ps = "(" ps ")" @@ -42,6 +46,9 @@ BEGIN { } else if( match(NL,/\s([A-Z][A-z]+)\s*{\s*[gs]et/,m) ) { mn = "P:Selenium.ComInterfaces." f[1] "." m[1] + } else + if( match(NL,/this\[([a-z]+) [A-z]+\]/,m) ) { + mn = "P:Selenium.ComInterfaces." f[1] ".Item(" TranslateType( m[1] ) ")" } if( mn ) { print " " diff --git a/Sandcastle/SeleniumBasic.shfbproj b/Sandcastle/SeleniumBasic.shfbproj old mode 100755 new mode 100644 index 2a1b901..64061be --- a/Sandcastle/SeleniumBasic.shfbproj +++ b/Sandcastle/SeleniumBasic.shfbproj @@ -18,7 +18,7 @@ Help\ SeleniumBasic en-US - HtmlHelp1, Website + HtmlHelp1 Visual Basic, Visual Basic Usage VS2013 True @@ -29,9 +29,10 @@ 100 - - - + + + + None SeleniumBasic 1.0.0.0 @@ -47,17 +48,21 @@ - - - - - + + + + + + + + ..\Selenium\ False SeleniumBasic - A Selenium based browser automation framework for VB.Net, Visual Basic Applications and VBScript All public methods and properties. Some of them available only from a .NET (i.e. not via COM). To know which are available via COM refer to a corresponding COM interface name. If a method listed as a class member but not a member of a corresponding interface, the method won't be available via COM. Methods with an "S" icon are static and surely not available via COM. However, look for a similar method in the "Explicit Interface Implementations" section. -Only methods of those interfaces are available via COM (Component Object Model) from VBScript, etc. + Only methods of those interfaces are available via COM (Component Object Model) from VBScript, etc. + C:\Program Files (x86)\HTML Help Workshop\