├── .gitignore ├── History.md ├── LICENSE.txt ├── README.md ├── images └── coypu.jpg └── src ├── Coypu.AcceptanceTests ├── BasicAuth.cs ├── Coypu.AcceptanceTests.csproj ├── Examples │ ├── ApiExamples.cs │ ├── CheckboxExamples.cs │ ├── ClickExamples.cs │ ├── CustomSeleniumBrowserSession.cs │ ├── EnterTextExamples.cs │ ├── FindExamples.cs │ ├── HasExamples.cs │ ├── ModalDialogExamples.cs │ ├── SelectFromExamples.cs │ ├── ShowsExamples.cs │ ├── WindowExamples.cs │ └── WithinExamples.cs ├── FindAllWithPredicateAndRetries.cs ├── InnerAndOuterHtml.cs ├── Location.cs ├── MultipleSessions.cs ├── PathHelper.cs ├── Properties │ ├── AssemblyInfo.cs │ └── ParallelizableAssemblyInfo.cs ├── Screenshots.cs ├── SnapshotElementScope.cs ├── StaleScopeExamples.cs ├── States.cs ├── TextPrecisionAndMatch.cs ├── WaitAndRetryExamples.cs ├── WebRequests.cs ├── html │ ├── states.htm │ ├── test-card.png │ └── tricky.htm └── sites │ └── SelfHostedSite.cs ├── Coypu.Drivers.Tests ├── Coypu.Drivers.Tests.csproj ├── DriverSpecs.cs ├── When_accessing_attributes.cs ├── When_checking.cs ├── When_choosing.cs ├── When_clicking.cs ├── When_executing_script.cs ├── When_finding_all_elements_by_css.cs ├── When_finding_all_elements_by_xpath.cs ├── When_finding_an_element_by_css.cs ├── When_finding_an_element_by_xpath.cs ├── When_finding_buttons.cs ├── When_finding_buttons_within_scope.cs ├── When_finding_fields_by_container_label.cs ├── When_finding_fields_by_for_attribute.cs ├── When_finding_fields_by_name.cs ├── When_finding_fields_by_placeholder.cs ├── When_finding_fields_by_value.cs ├── When_finding_fields_id.cs ├── When_finding_fields_within_scope.cs ├── When_finding_fieldsets.cs ├── When_finding_frames.cs ├── When_finding_ids.cs ├── When_finding_iframes.cs ├── When_finding_links.cs ├── When_finding_sections.cs ├── When_finding_sections_as_divs.cs ├── When_finding_windows.cs ├── When_forced_to_consider_invisible_elements.cs ├── When_getting_cookies.cs ├── When_hovering.cs ├── When_inspecting_dialog_text.cs ├── When_inspecting_html.cs ├── When_inspecting_location.cs ├── When_inspecting_title.cs ├── When_navigating.cs ├── When_refreshing_windows.cs ├── When_saving_screenshots.cs ├── When_selecting_options.cs ├── When_sending_keys_to_elements.cs ├── When_setting_fields.cs ├── When_sizing_windows.cs ├── When_uploading_files.cs ├── When_using_iframes_as_scope.cs └── html │ ├── InteractionTestsPage.htm │ ├── frame1.htm │ ├── frame2.htm │ ├── frameset.htm │ ├── iFrame1.htm │ ├── iFrame2.htm │ ├── popUp.htm │ ├── popUp2.htm │ ├── table.htm │ ├── test-card.jpg │ └── test-card.png ├── Coypu.NUnit ├── Coypu.NUnit.csproj └── Matchers │ ├── AggregateMatcher.cs │ ├── HasAllCssInOrderMatcher.cs │ ├── HasContentMatcher.cs │ ├── HasCssMatcher.cs │ ├── HasNoContentMatcher.cs │ ├── HasNoCssMatcher.cs │ ├── HasNoValueMatcher.cs │ ├── HasValueMatcher.cs │ └── Shows.cs ├── Coypu.Tests ├── Coypu.Tests.csproj ├── ProxyTests.cs ├── TestBuilders │ ├── FindsFirstWitNoDisambiguationStrategy.cs │ └── TestSessionBuilder.cs ├── TestDoubles │ ├── FakeDriver.cs │ ├── ImmediateSingleExecutionFakeTimingStrategy.cs │ ├── SpyRestrictedResourceDownloader.cs │ ├── SpyTimingStrategy.cs │ ├── StubDriver.cs │ ├── StubElement.cs │ ├── StubWebResources.cs │ └── StubWebResponse.cs ├── TestException.cs ├── When_applying_match_strategy │ ├── Any_match_strategy.cs │ ├── StubElementFinder.cs │ ├── When_using_first_exact.cs │ ├── When_using_first_prefer_exact.cs │ ├── When_using_first_substring.cs │ ├── When_using_single_exact.cs │ ├── When_using_single_prefer_exact.cs │ └── When_using_single_substring.cs ├── When_configuring_options.cs ├── When_interacting_with_the_browser │ ├── BrowserInteractionTests.cs │ ├── WhenFindingSingleElements.cs │ ├── When_building_urls.cs │ ├── When_clicking_buttons.cs │ ├── When_clicking_links.cs │ ├── When_completing_forms.cs │ ├── When_constructing_xpath.cs │ ├── When_controlling_the_window.cs │ ├── When_executing_script.cs │ ├── When_finding_all_matching_elements.cs │ ├── When_finding_state.cs │ ├── When_finding_then_checking_an_element.cs │ ├── When_finding_then_clicking_an_element.cs │ ├── When_finding_then_filling_in_any_field.cs │ ├── When_finding_then_hovering.cs │ ├── When_finding_then_unchecking_any_element.cs │ ├── When_insepcting_elements.cs │ ├── When_inspecting.cs │ ├── When_inspecting_location.cs │ ├── When_inspecting_modal_dialogs.cs │ ├── When_inspecting_the_page.cs │ ├── When_inspecting_title.cs │ ├── When_interacting_with_modal_dialogs.cs │ ├── When_querying_with_any_finder.cs │ ├── When_saving_screenshots.cs │ ├── When_scoping_interactions.cs │ └── When_visiting.cs ├── When_making_browser_interactions_robust │ ├── TestActions.cs │ ├── TestQueries.cs │ ├── When_I_want_to_manage_timing_manually.cs │ ├── When_querying_for_an_expected_result.cs │ ├── When_retrying_until_a_timeout.cs │ ├── When_trying_an_action_until_a_given_state_is_reached.cs │ └── When_waiting.cs ├── When_making_direct_web_requests │ └── When_saving_a_resource_from_the_web.cs ├── When_starting_and_ending_sessions.cs └── proxysettings.json ├── Coypu.sln ├── Coypu ├── Actions │ ├── AcceptModalDialog.cs │ ├── BrowserAction.cs │ ├── CancelModalDialog.cs │ ├── CheckAction.cs │ ├── Choose.cs │ ├── ClickAction.cs │ ├── DblClickAction.cs │ ├── DriverAction.cs │ ├── FillIn.cs │ ├── Hover.cs │ ├── LambdaDriverAction.cs │ ├── Select.cs │ ├── SendKeys.cs │ ├── Uncheck.cs │ └── WaitThenClick.cs ├── ActivatorDriverFactory.cs ├── AmbiguousException.cs ├── AsyncExtensions.cs ├── BrowserSession.cs ├── BrowserWindow.cs ├── Cookies.cs ├── Coypu.csproj ├── DriverFactory.cs ├── DriverProxy.cs ├── DriverProxyType.cs ├── DriverScope.cs ├── Drivers │ ├── Browser.cs │ ├── BrowserNotSupportedException.cs │ ├── Html.cs │ ├── NoSuchBrowserException.cs │ ├── Playwright │ │ ├── Cookies.cs │ │ ├── Dialogs.cs │ │ ├── FrameFinder.cs │ │ ├── PlaywrightDriver.cs │ │ ├── PlaywrightElement.cs │ │ ├── PlaywrightFrame.cs │ │ └── PlaywrightWindow.cs │ ├── Selenium │ │ ├── Cookies.cs │ │ ├── Dialogs.cs │ │ ├── DriverFactory.cs │ │ ├── ElementFinder.cs │ │ ├── FrameFinder.cs │ │ ├── ImageFormatParser.cs │ │ ├── MouseControl.cs │ │ ├── SeleniumElement.cs │ │ ├── SeleniumExtensions.cs │ │ ├── SeleniumFrame.cs │ │ ├── SeleniumWebDriver.cs │ │ ├── SeleniumWindow.cs │ │ ├── SeleniumWindowManager.cs │ │ ├── TextMatcher.cs │ │ └── WindowHandleFinder.cs │ └── XPath.cs ├── Element.cs ├── ElementFoundExtensions.cs ├── ElementScope.cs ├── FillInWith.cs ├── FinderException.cs ├── Finders │ ├── ButtonFinder.cs │ ├── CssFinder.cs │ ├── DisambiguationStrategy.cs │ ├── DocumentElementFinder.cs │ ├── ElementFinder.cs │ ├── FieldFinder.cs │ ├── FieldsetFinder.cs │ ├── FinderOptionsDisambiguationStrategy.cs │ ├── FrameFinder.cs │ ├── IdFinder.cs │ ├── LinkFinder.cs │ ├── OptionFinder.cs │ ├── SectionFinder.cs │ ├── SelectFinder.cs │ ├── StateFinder.cs │ ├── WindowFinder.cs │ ├── WithTextFinder.cs │ ├── XPathFinder.cs │ └── XPathQueryFinder.cs ├── FullyQualifiedUrlBuilder.cs ├── IDriver.cs ├── Match.cs ├── MissingDialogException.cs ├── MissingHtmlException.cs ├── MissingWindowException.cs ├── Options.cs ├── Properties │ └── AssemblyInfo.cs ├── Queries │ ├── ActionSatisfiesPredicateQuery.cs │ ├── DialogQuery.cs │ ├── DriverScopeQuery.cs │ ├── ElementExistsQuery.cs │ ├── ElementMissingQuery.cs │ ├── ElementQuery.cs │ ├── ElementScopeQuery.cs │ ├── FindAllCssWithPredicateQuery.cs │ ├── FindAllXPathWithPredicateQuery.cs │ ├── HasContentMatchQuery.cs │ ├── HasContentQuery.cs │ ├── HasDialogQuery.cs │ ├── HasNoContentMatchQuery.cs │ ├── HasNoContentQuery.cs │ ├── HasNoValueQuery.cs │ ├── HasValueQuery.cs │ ├── LambdaPredicateQuery.cs │ ├── LambdaQuery.cs │ ├── PredicateQuery.cs │ ├── Query.cs │ ├── WindowExistsQuery.cs │ └── WindowMissingQuery.cs ├── Scope.cs ├── SelectFrom.cs ├── SessionConfiguration.cs ├── SnapshotElementScope.cs ├── StaleElementException.cs ├── State.cs ├── SynchronisedElementScope.cs ├── Timing │ ├── RetryUntilTimeoutTimingStrategy.cs │ ├── StopwatchWaiter.cs │ ├── TimingStrategy.cs │ └── Waiter.cs ├── UrlBuilder.cs └── WebRequests │ ├── RequestCookieInjector.cs │ ├── RestrictedResourceDownloader.cs │ ├── WebClientWithCookies.cs │ └── WebRequestCookieInjector.cs ├── IMPORTANT.txt └── NuGet.config /.gitignore: -------------------------------------------------------------------------------- 1 | obj 2 | bin 3 | deploy 4 | deploy/* 5 | temp/ 6 | _ReSharper.* 7 | <<<<<<< HEAD 8 | *.ReSharper.user 9 | *.csproj.user 10 | *.sln.*.user 11 | *.resharper.user 12 | ======= 13 | *.user 14 | >>>>>>> citizenmatt-watin 15 | *.resharper 16 | *.suo 17 | *.cache 18 | ~$*.DS_Store 19 | .DS_Store 20 | ._* 21 | examples/self_test 22 | TestResult.xml 23 | .idea 24 | *.swp 25 | Coypu*.zip 26 | Coypu*.nupkg 27 | chromedriver.log 28 | Coypu-*.wrap 29 | src/packages 30 | /src/.vs/* 31 | /src/.vs/Coypu/v15/Server/sqlite3/* 32 | /src/.vs/Coypu/v15/Server/sqlite3* 33 | /src/.vs/Coypu/v15/Server/* 34 | *.ide 35 | /.vs/slnx.sqlite 36 | /src/Coypu.Tests/Coypu.Tests.nuget.props 37 | /src/Coypu.NUnit/Coypu.NUnit.nuget.props 38 | /src/Coypu.Drivers.Tests/Coypu.Drivers.Tests.nuget.targets 39 | /src/Coypu.Drivers.Tests/Coypu.Drivers.Tests.nuget.props 40 | /src/Coypu.AcceptanceTests/Coypu.AcceptanceTests.nuget.targets 41 | /src/Coypu.AcceptanceTests/Coypu.AcceptanceTests.nuget.props 42 | /src/Coypu.AcceptanceTests/project.lock.json 43 | /src/Coypu/project.lock.json 44 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Adrian Longley & Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /images/coypu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/featurist/coypu/889ebb8d91b03d8fa7bd348cc0788d30d1c6b6c2/images/coypu.jpg -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/BasicAuth.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Threading; 5 | using Coypu.AcceptanceTests.Sites; 6 | using Coypu.Drivers.Playwright; 7 | using Coypu.NUnit.Matchers; 8 | using NUnit.Framework; 9 | 10 | namespace Coypu.AcceptanceTests 11 | { 12 | [TestFixture] 13 | public class BasicAuth 14 | { 15 | private SelfHostedSite site; 16 | private BrowserSession browser; 17 | 18 | [SetUp] 19 | public void SetUp() 20 | { 21 | site = new SelfHostedSite(); 22 | } 23 | 24 | [TearDown] 25 | public void TearDown() 26 | { 27 | browser.Dispose(); 28 | site.Dispose(); 29 | } 30 | 31 | [Test] 32 | public void It_passes_through_basic_auth_from_url_as_auth_header() 33 | { 34 | var configuration = new SessionConfiguration 35 | { 36 | Timeout = TimeSpan.FromMilliseconds(1000), 37 | Port = site.BaseUri.Port, 38 | AppHost = "http://someUser:passw0rd@localhost", 39 | Driver = typeof(PlaywrightDriver), // Selenium can't do this 40 | Headless = false, 41 | Browser = Drivers.Browser.Chromium 42 | }; 43 | 44 | browser = new BrowserSession(configuration); 45 | browser.Visit("/"); 46 | 47 | browser.Visit("/headers"); 48 | Assert.That(browser, Shows.Content("Authorization: " + GetBasicAuthHeader("someUser", "passw0rd"))); 49 | } 50 | 51 | private string GetBasicAuthHeader(string username, string password) 52 | { 53 | var auth = $"{username}:{password}"; 54 | var bytes = Encoding.UTF8.GetBytes(auth); 55 | return "Basic " + Convert.ToBase64String(bytes); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/Coypu.AcceptanceTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {6E5D29AC-14C4-4BBE-826D-F8EEA1B40900} 4 | net6.0 5 | true 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | html\%(Filename)%(Extension) 30 | PreserveNewest 31 | 32 | 33 | Always 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/Examples/CheckboxExamples.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace Coypu.AcceptanceTests.Examples 4 | { 5 | public class CheckboxExamples : WaitAndRetryExamples 6 | { 7 | [Test] 8 | public void Can_find_checkbox_and_check_it() 9 | { 10 | var checkbox = Browser.FindCss("#uncheckedBox"); 11 | checkbox.Check(); 12 | Assert.IsTrue(Browser.FindField("uncheckedBox") 13 | .Selected); 14 | } 15 | 16 | [Test] 17 | public void Can_find_checkbox_and_uncheck_it() 18 | { 19 | var checkbox = Browser.FindCss("#checkedBox"); 20 | checkbox.Uncheck(); 21 | Assert.IsFalse(Browser.Query(() => Browser.FindField("checkedBox") 22 | .Selected, 23 | false)); 24 | } 25 | 26 | [Test] 27 | public void Check_example() 28 | { 29 | Browser.Check("uncheckedBox"); 30 | Assert.IsTrue(Browser.FindField("uncheckedBox") 31 | .Selected); 32 | } 33 | 34 | [Test] 35 | public void Uncheck_example() 36 | { 37 | Browser.Uncheck("checkedBox"); 38 | Assert.IsFalse(Browser.Query(() => Browser.FindField("checkedBox") 39 | .Selected, 40 | false)); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/Examples/EnterTextExamples.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.AcceptanceTests.Examples 5 | { 6 | public class EnterTextExamples : WaitAndRetryExamples 7 | { 8 | [Test] 9 | public void FillIn_file_example() 10 | { 11 | const string someLocalFile = @"local.file"; 12 | try 13 | { 14 | var directoryInfo = new DirectoryInfo(Path.GetTempPath()); 15 | var fullPath = Path.Combine(directoryInfo.FullName, someLocalFile); 16 | using (File.Create(fullPath)) { } 17 | 18 | Browser.FillIn("forLabeledFileFieldId") 19 | .With(fullPath); 20 | var findAgain = Browser.FindField("forLabeledFileFieldId"); 21 | Assert.That(findAgain.Value, Does.EndWith(someLocalFile)); 22 | } 23 | finally 24 | { 25 | File.Delete(someLocalFile); 26 | } 27 | } 28 | 29 | [Test] 30 | public void FillInWith_element_example() 31 | { 32 | Browser.FindField("scope2ContainerLabeledTextInputFieldId") 33 | .FillInWith("New text input value"); 34 | Assert.That(Browser.FindField("scope2ContainerLabeledTextInputFieldId") 35 | .Value, 36 | Is.EqualTo("New text input value")); 37 | } 38 | 39 | [Test] 40 | public void FillInWith_example() 41 | { 42 | Browser.FillIn("scope2ContainerLabeledTextInputFieldId") 43 | .With("New text input value"); 44 | Assert.That(Browser.FindField("scope2ContainerLabeledTextInputFieldId") 45 | .Value, 46 | Is.EqualTo("New text input value")); 47 | } 48 | 49 | [Test] 50 | public void SendKeys_example() 51 | { 52 | Browser.FindField("containerLabeledTextInputFieldId") 53 | .SendKeys(" - send these keys"); 54 | Assert.That(Browser.FindField("containerLabeledTextInputFieldId") 55 | .Value, 56 | Is.EqualTo("text input field two val - send these keys")); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/Examples/ModalDialogExamples.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace Coypu.AcceptanceTests.Examples 4 | { 5 | public class ModalDialogExamples : WaitAndRetryExamples 6 | { 7 | [Test] 8 | public void AcceptModalDialog_example() 9 | { 10 | Browser.AcceptAlert("You have triggered an alert and this is the text.", () => { 11 | Browser.ClickLink("Trigger an alert"); 12 | }); 13 | Browser.FindLink("Trigger an alert - accepted") 14 | .Now(); 15 | } 16 | 17 | [Test] 18 | public void CancelModalDialog_example() 19 | { 20 | Browser.CancelConfirm(() => { 21 | Browser.ClickLink("Trigger a confirm"); 22 | }); 23 | Browser.FindLink("Trigger a confirm - cancelled") 24 | .Now(); 25 | } 26 | 27 | [Test] 28 | public void ModalDialog_while_multiple_windows_are_open() 29 | { 30 | Browser.ClickLink("Open pop up window"); 31 | Browser.CancelConfirm(() => { 32 | Browser.ClickLink("Trigger a confirm"); 33 | }); 34 | Browser.FindLink("Trigger a confirm - cancelled") 35 | .Now(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/InnerAndOuterHtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.AcceptanceTests 6 | { 7 | [TestFixture] 8 | public class InnerAndOuterHtml 9 | { 10 | private SessionConfiguration SessionConfiguration; 11 | private BrowserSession browser; 12 | 13 | [OneTimeSetUp] 14 | public void SetUpFixture() 15 | { 16 | SessionConfiguration = new SessionConfiguration(); 17 | SessionConfiguration.Timeout = TimeSpan.FromMilliseconds(1000); 18 | browser = new BrowserSession(SessionConfiguration); 19 | 20 | browser.Visit(PathHelper.GetPageHtmlPath("table.htm")); 21 | } 22 | 23 | [OneTimeTearDown] 24 | public void TearDownFixture() 25 | { 26 | browser.Dispose(); 27 | } 28 | 29 | [Test] 30 | public void GrabsTheOuterHTMLFromAnElement() 31 | { 32 | var outerHTML = Normalise(browser.FindCss("table").OuterHTML); 33 | Assert.That(outerHTML, Is.EqualTo("
nameage
bob12
jane79
")); 34 | } 35 | 36 | [Test] 37 | public void GrabsTheInnerHTMLFromAnElement() 38 | { 39 | var innerHTML = Normalise(browser.FindCss("table").InnerHTML); 40 | Assert.That(innerHTML, Is.EqualTo("nameagebob12jane79")); 41 | } 42 | 43 | private static string Normalise(string innerHtml) 44 | { 45 | return new Regex(@"\s+", RegexOptions.Multiline).Replace(innerHtml, "").ToLower(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/MultipleSessions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.AcceptanceTests 5 | { 6 | [TestFixture] 7 | public class MultipleSessions 8 | { 9 | [Test] 10 | public void Two_browser_sessions_can_be_controlled_independently() 11 | { 12 | using (var sessionOne = new BrowserSession()) 13 | { 14 | using (var sessionTwo = new BrowserSession()) 15 | { 16 | 17 | VisitTestPage(sessionOne); 18 | VisitTestPage(sessionTwo); 19 | 20 | sessionOne.FindCss("input[type=text]", new Options { Match = Match.First }).FillInWith("from session one"); 21 | sessionTwo.FindCss("input[type=text]", new Options { Match = Match.First }).FillInWith("from session two"); 22 | 23 | Assert.That(sessionOne.FindCss("input[type=text]", new Options { Match = Match.First }).Value, Is.EqualTo("from session one")); 24 | Assert.That(sessionTwo.FindCss("input[type=text]", new Options { Match = Match.First }).Value, Is.EqualTo("from session two")); 25 | } 26 | } 27 | } 28 | 29 | private void VisitTestPage(BrowserSession browserSession) 30 | { 31 | browserSession.Visit(PathHelper.GetPageHtmlPath("InteractionTestsPage.htm")); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/PathHelper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.AcceptanceTests; 5 | 6 | public static class PathHelper 7 | { 8 | public static string GetPageHtmlPath(string page) 9 | { 10 | var schema = "file:///"; 11 | if (TestContext.CurrentContext.TestDirectory.Length > 0 && 12 | TestContext.CurrentContext.TestDirectory[0] == '/') 13 | { 14 | schema = "file://"; 15 | } 16 | return schema + Path.Combine(TestContext.CurrentContext.TestDirectory, "html", page).Replace("\\", "/"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | 8 | [assembly: AssemblyTitle("Coypu.AcceptanceTests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Coypu.AcceptanceTests")] 13 | [assembly: AssemblyCopyright("Copyright © Adrian Longley and contributors 2011-2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | 21 | [assembly: ComVisible(false)] 22 | 23 | // The following GUID is for the ID of the typelib if this project is exposed to COM 24 | 25 | [assembly: Guid("da05f0fa-4eb8-4204-b397-50e2d4c1252b")] 26 | 27 | // Version information for an assembly consists of the following four values: 28 | // 29 | // Major Version 30 | // Minor Version 31 | // Build Number 32 | // Revision 33 | // 34 | // You can specify all the values or you can default the Build and Revision Numbers 35 | // by using the '*' as shown below: 36 | // [assembly: AssemblyVersion("1.0.*")] 37 | 38 | [assembly: AssemblyVersion("3.14.0.0")] 39 | [assembly: AssemblyFileVersion("3.14.0.0")] 40 | -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/Properties/ParallelizableAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | [assembly: Parallelizable(ParallelScope.Fixtures)] 4 | [assembly: LevelOfParallelism(4)] -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/Screenshots.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.AcceptanceTests 5 | { 6 | [TestFixture] 7 | public class Screenshots : WaitAndRetryExamples 8 | { 9 | private static void SaveFileToAssertItExists(BrowserWindow browserWindow, string fileName) 10 | { 11 | try 12 | { 13 | browserWindow.SaveScreenshot(fileName); 14 | Assert.That(File.Exists(fileName), "Expected screenshot saved to " + new FileInfo(fileName).FullName); 15 | } 16 | finally 17 | { 18 | if (File.Exists(fileName)) 19 | File.Delete(fileName); 20 | } 21 | } 22 | 23 | [Test] 24 | public void CapturesCorrectWindow() 25 | { 26 | Browser.ClickLink("Open pop up window"); 27 | var popUp = Browser.FindWindow("Pop Up Window"); 28 | popUp.Visit(PathHelper.GetPageHtmlPath("test-card.jpg")); 29 | popUp.ResizeTo(800, 600); 30 | Browser.FindCss("body").Click(); 31 | 32 | SaveFileToAssertItExists(popUp, "screenshot-test-card.jpg"); 33 | } 34 | 35 | [Test] 36 | public void SavesJpgToSpecifiedLocation() 37 | { 38 | Browser.Visit(PathHelper.GetPageHtmlPath("test-card.jpg")); 39 | Browser.ResizeTo(800, 600); 40 | 41 | SaveFileToAssertItExists(Browser, "screenshot-test-card.jpg"); 42 | } 43 | 44 | [Test] 45 | public void SavesPngToSpecifiedLocation() 46 | { 47 | Browser.Visit(PathHelper.GetPageHtmlPath("test-card.png")); 48 | Browser.ResizeTo(800, 600); 49 | 50 | SaveFileToAssertItExists(Browser,"screenshot-test-card.png"); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/WaitAndRetryExamples.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Coypu.Drivers.Playwright; 4 | using Coypu.Drivers.Selenium; 5 | using NUnit.Framework; 6 | using NUnit.Framework.Interfaces; 7 | 8 | namespace Coypu.AcceptanceTests 9 | { 10 | public class WaitAndRetryExamples 11 | { 12 | protected BrowserSession Browser; 13 | 14 | [OneTimeSetUp] 15 | public void SetUpFixture() 16 | { 17 | var configuration = new SessionConfiguration 18 | { 19 | Browser = Drivers.Browser.Chrome, 20 | Driver = typeof(PlaywrightDriver), 21 | Headless = true 22 | }; 23 | Browser = new BrowserSession(configuration); 24 | } 25 | 26 | [OneTimeTearDown] 27 | public void TearDown() 28 | { 29 | Browser.Dispose(); 30 | } 31 | 32 | [SetUp] 33 | public void SetUp() 34 | { 35 | ReloadTestPageWithDelay(); 36 | } 37 | 38 | [TearDown] 39 | public void TearDownOnFail() 40 | { 41 | if (TestContext.CurrentContext.Result.Outcome.Status != TestStatus.Failed) return; 42 | TearDown(); 43 | SetUpFixture(); 44 | } 45 | 46 | protected void ApplyAsyncDelay() 47 | { 48 | // Hide the HTML then bring back after a short delay to test robustness 49 | Browser.ExecuteScript("window.holdIt = window.document.body.innerHTML;"); 50 | Browser.ExecuteScript("window.document.body.innerHTML = '';"); 51 | Browser.ExecuteScript("setTimeout(function() {document.body.innerHTML = window.holdIt},250)"); 52 | } 53 | 54 | protected void ReloadTestPage() 55 | { 56 | Browser.Visit(PathHelper.GetPageHtmlPath("InteractionTestsPage.htm")); 57 | } 58 | 59 | protected void ReloadTestPageWithDelay() 60 | { 61 | ReloadTestPage(); 62 | ApplyAsyncDelay(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/html/states.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 |
12 | State one reached 13 |
14 |
15 | State two reached 16 |
17 |
18 | State three reached 19 |
20 | 21 | -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/html/test-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/featurist/coypu/889ebb8d91b03d8fa7bd348cc0788d30d1c6b6c2/src/Coypu.AcceptanceTests/html/test-card.png -------------------------------------------------------------------------------- /src/Coypu.AcceptanceTests/html/tricky.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Pop Up Window 6 | 7 | 8 |
9 |

section 1

10 | the link 11 |
12 |
13 |

section 2

14 | the link 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/Coypu.Drivers.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {310D5EB9-2B2A-406E-8B8E-FBC85D882F54} 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | PreserveNewest 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_accessing_attributes.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | internal class When_accessing_attributes : DriverSpecs 7 | { 8 | [Test] 9 | public void Exposes_element_attributes() 10 | { 11 | var formWithAttributesToTest = Id("attributeTestForm", Root, DefaultOptions); 12 | formWithAttributesToTest["id"].ShouldBe("attributeTestForm"); 13 | formWithAttributesToTest["method"].ShouldBe("post"); 14 | formWithAttributesToTest["action"].ShouldBe("http://somesite.com/action.htm"); 15 | formWithAttributesToTest["target"].ShouldBe("_parent"); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_choosing.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | internal class When_choosing : DriverSpecs 7 | { 8 | [Test] 9 | public void Chooses_radio_button_from_list() 10 | { 11 | var radioButton1 = Field("chooseRadio1"); 12 | radioButton1.Selected.ShouldBeFalse(); 13 | 14 | // Choose 1 15 | Driver.Choose(radioButton1); 16 | 17 | var radioButton2 = Field("chooseRadio2"); 18 | radioButton2.Selected.ShouldBeFalse(); 19 | 20 | // Choose 2 21 | Driver.Choose(radioButton2); 22 | 23 | // New choice is now selected 24 | radioButton2 = Field("chooseRadio2"); 25 | radioButton2.Selected.ShouldBeTrue(); 26 | 27 | // Originally selected is no longer selected 28 | radioButton1 = Field("chooseRadio1"); 29 | radioButton1.Selected.ShouldBeFalse(); 30 | } 31 | 32 | 33 | [Test] 34 | public void Fires_onclick_event() 35 | { 36 | var radio = Field("chooseRadio2"); 37 | radio.Value.ShouldBe("Radio buttons - 2nd value"); 38 | 39 | Driver.Choose(radio); 40 | 41 | Field("chooseRadio2", Root).Value.ShouldBe("Radio buttons - 2nd value - clicked"); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_clicking.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Coypu.Finders; 3 | using Shouldly; 4 | using NUnit.Framework; 5 | 6 | namespace Coypu.Drivers.Tests 7 | { 8 | public class When_clicking : DriverSpecs 9 | { 10 | [Test] 11 | public void Clicks_the_underlying_element() 12 | { 13 | var element = Button("clickMeTest"); 14 | element.Value.ShouldBe("Click me"); 15 | 16 | Driver.Click(element); 17 | 18 | Button("clickMeTest").Value.ShouldBe("Click me - clicked"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_executing_script.cs: -------------------------------------------------------------------------------- 1 | using Coypu.Finders; 2 | using Shouldly; 3 | using NUnit.Framework; 4 | using System; 5 | using Coypu.Tests; 6 | 7 | namespace Coypu.Drivers.Tests 8 | { 9 | internal class When_executing_script : DriverSpecs 10 | { 11 | [Test] 12 | public void Runs_the_script_in_the_browser() 13 | { 14 | Button("firstButtonId").Text.ShouldBe("first button"); 15 | 16 | Driver.ExecuteScript("document.getElementById('firstButtonId').innerHTML = 'script executed';", Root); 17 | 18 | Button("firstButtonId").Text.ShouldBe("script executed"); 19 | } 20 | 21 | [Test] 22 | public void Passes_the_arguments_to_the_browser() 23 | { 24 | Button("firstButtonId").Text.ShouldBe("first button"); 25 | 26 | Driver.ExecuteScript("arguments[0].innerHTML = 'script executed ' + arguments[1];", Root, Button("firstButtonId"), 5); 27 | 28 | Button("firstButtonId").Text.ShouldBe("script executed 5"); 29 | } 30 | 31 | [Test] 32 | public void Returns_the_result() 33 | { 34 | Driver.ExecuteScript("return document.getElementById('firstButtonId').innerHTML;", Root).ShouldBe("first button"); 35 | Driver.ExecuteScript("return 1234;", Root).ShouldBe(1234); 36 | } 37 | 38 | [Test] 39 | public void Returns_the_result_typed() 40 | { 41 | string str = Driver.ExecuteScript("return 'miles';", Root); 42 | str.GetType().ShouldBe(typeof(string)); 43 | 44 | int integer = Driver.ExecuteScript("return 1234;", Root); 45 | integer.GetType().ShouldBe(typeof(int)); 46 | 47 | DateTime dateTime = Driver.ExecuteScript("return new Date();", Root); 48 | dateTime.GetType().ShouldBe(typeof(DateTime)); 49 | 50 | try { 51 | Driver.ExecuteScript("return 'bob';", Root); 52 | throw new TestException("Expected an exception"); 53 | } catch(Exception e) { 54 | if (e is TestException) throw; 55 | }; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_all_elements_by_css.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Shouldly; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.Drivers.Tests 6 | { 7 | internal class When_finding_all_elements_by_css : DriverSpecs 8 | { 9 | 10 | [Test] 11 | public void Returns_empty_if_no_matches() 12 | { 13 | const string shouldNotFind = "#inspectingContent p.css-missing-test"; 14 | Assert.That(Driver.FindAllCss(shouldNotFind, Root, DefaultOptions), Is.Empty); 15 | } 16 | 17 | [Test] 18 | public void Returns_all_matches_by_css() 19 | { 20 | const string shouldFind = "#inspectingContent ul#cssTest li"; 21 | var all = Driver.FindAllCss(shouldFind, Root, DefaultOptions); 22 | all.Count().ShouldBe(3); 23 | all.ElementAt(1).Text.ShouldBe("two"); 24 | all.ElementAt(2).Text.ShouldBe("Me! Pick me!"); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_all_elements_by_xpath.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Coypu.Finders; 3 | using Shouldly; 4 | using NUnit.Framework; 5 | 6 | namespace Coypu.Drivers.Tests 7 | { 8 | internal class When_finding_all_elements_by_xpath : DriverSpecs 9 | { 10 | [Test] 11 | public void Returns_empty_if_no_matches() 12 | { 13 | const string shouldNotFind = "//*[@id = 'inspectingContent']//p[@class='css-missing-test']"; 14 | Assert.That(Driver.FindAllXPath(shouldNotFind, Root, DefaultOptions), Is.Empty); 15 | } 16 | 17 | [Test] 18 | public void Returns_all_matches_by_xpath() 19 | { 20 | const string shouldFind = "//*[@id='inspectingContent']//ul[@id='cssTest']/li"; 21 | var all = Driver.FindAllXPath(shouldFind, Root, DefaultOptions); 22 | all.Count().ShouldBe(3); 23 | all.ElementAt(1).Text.ShouldBe("two"); 24 | all.ElementAt(2).Text.ShouldBe("Me! Pick me!"); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_an_element_by_xpath.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using Shouldly; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.Drivers.Tests 6 | { 7 | internal class When_finding_an_element_by_xpath : DriverSpecs 8 | { 9 | [Test] 10 | public void Finds_present_examples() 11 | { 12 | var shouldFind = "//*[@id = 'inspectingContent']//p[@class='css-test']/span"; 13 | XPath(shouldFind).Text.ShouldBe("This"); 14 | 15 | shouldFind = "//ul[@id='cssTest']/li[3]"; 16 | XPath(shouldFind).Text.ShouldBe("Me! Pick me!"); 17 | } 18 | 19 | [Test] 20 | public void Does_not_find_missing_examples() 21 | { 22 | const string shouldNotFind = "//*[@id = 'inspectingContent']//p[@class='css-missing-test']"; 23 | Assert.Throws(() => XPath(shouldNotFind), 24 | "Expected not to find something at: " + shouldNotFind); 25 | } 26 | 27 | [Test] 28 | public void Only_finds_visible_elements() 29 | { 30 | const string shouldNotFind = "//*[@id = 'inspectingContent']//p[@class='css-test']/img"; 31 | Assert.Throws(() => XPath(shouldNotFind), 32 | "Expected not to find something at: " + shouldNotFind); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_buttons_within_scope.cs: -------------------------------------------------------------------------------- 1 | using Coypu.Finders; 2 | using Shouldly; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.Drivers.Tests 6 | { 7 | public class When_finding_buttons_within_scope : DriverSpecs 8 | { 9 | private DriverScope scope1; 10 | private DriverScope scope2; 11 | 12 | [SetUp] 13 | public void SetUpScope() 14 | { 15 | scope1 = new BrowserWindow(DefaultSessionConfiguration, new IdFinder(Driver, "scope1", Root, DefaultOptions), Driver,null,null,null,DisambiguationStrategy); 16 | scope2 = new BrowserWindow(DefaultSessionConfiguration, new IdFinder(Driver, "scope2", Root, DefaultOptions), Driver,null,null,null,DisambiguationStrategy); 17 | } 18 | 19 | [Test] 20 | public void Finds_button_by_name() 21 | { 22 | Button("scopedButtonName", scope1).Id.ShouldBe("scope1ButtonId"); 23 | Button( "scopedButtonName", scope2).Id.ShouldBe("scope2ButtonId"); 24 | } 25 | 26 | [Test] 27 | public void Finds_input_button_by_value() 28 | { 29 | Button( "scoped input button", scope1).Id.ShouldBe("scope1InputButtonId"); 30 | Button( "scoped input button", scope2).Id.ShouldBe("scope2InputButtonId"); 31 | } 32 | 33 | [Test] 34 | public void Finds_button_by_text() 35 | { 36 | Button( "scoped button", scope1).Id.ShouldBe("scope1ButtonId"); 37 | Button( "scoped button", scope2).Id.ShouldBe("scope2ButtonId"); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_fields_by_name.cs: -------------------------------------------------------------------------------- 1 | using Coypu.Finders; 2 | using Shouldly; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.Drivers.Tests 6 | { 7 | internal class When_finding_fields_by_name : DriverSpecs { 8 | 9 | [Test] 10 | public void Finds_text_input() 11 | { 12 | Field("containerLabeledTextInputFieldName").Value.ShouldBe("text input field two val"); 13 | } 14 | 15 | [Test] 16 | public void Finds_textarea() 17 | { 18 | Field("containerLabeledTextareaFieldName").Value.ShouldBe("textarea field two val"); 19 | } 20 | 21 | [Test] 22 | public void Finds_select() 23 | { 24 | Field("containerLabeledSelectFieldName").Id.ShouldBe("containerLabeledSelectFieldId"); 25 | } 26 | 27 | [Test] 28 | public void Finds_checkbox() 29 | { 30 | Field("containerLabeledCheckboxFieldName").Value.ShouldBe("checkbox field two val"); 31 | } 32 | 33 | [Test] 34 | public void Does_NOT_find_radio_button() 35 | { 36 | Assert.Throws(() => Button("containerLabeledRadioFieldName")); 37 | } 38 | 39 | [Test] 40 | public void Finds_password_input() 41 | { 42 | Field("containerLabeledPasswordFieldName").Id.ShouldBe("containerLabeledPasswordFieldId"); 43 | } 44 | 45 | [Test] 46 | public void Finds_file_input() 47 | { 48 | Field("containerLabeledFileFieldName").Id.ShouldBe("containerLabeledFileFieldId"); 49 | } 50 | 51 | } 52 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_fields_by_placeholder.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | internal class When_finding_fields_by_placeholder : DriverSpecs 7 | { 8 | [Test] 9 | public void Finds_text_field_by_placeholder() 10 | { 11 | Field("text input field with a placeholder").Id.ShouldBe("textInputFieldWithPlaceholder"); 12 | Field("textarea field with a placeholder").Id.ShouldBe("textareaFieldWithPlaceholder"); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_fields_by_value.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | internal class When_finding_fields_by_value : DriverSpecs 7 | { 8 | [Test] 9 | public void Finds_radio_button_by_value() { 10 | Field("radio field one val").Name.ShouldBe("forLabeledRadioFieldName"); 11 | Field("radio field two val").Name.ShouldBe("containerLabeledRadioFieldName"); 12 | } 13 | 14 | [Test] 15 | public void Finds_checkbox_by_value() { 16 | Field("checkbox one val").Name.ShouldBe("checkboxByValueOneFieldName"); 17 | Field("checkbox two val").Name.ShouldBe("checkboxByValueTwoFieldName"); 18 | } 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_fieldsets.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | internal class When_finding_fieldsets : DriverSpecs 7 | { 8 | [Test] 9 | public void Finds_by_legend_text() 10 | { 11 | Fieldset("Scope 1").Id.ShouldBe("fieldsetScope1"); 12 | Fieldset("Scope 2").Id.ShouldBe("fieldsetScope2"); 13 | } 14 | 15 | [Test] 16 | public void Finds_by_id() 17 | { 18 | Fieldset("fieldsetScope1").Id.ShouldBe("fieldsetScope1"); 19 | Fieldset("fieldsetScope2").Id.ShouldBe("fieldsetScope2"); 20 | } 21 | 22 | [Test] 23 | public void Finds_only_fieldsets() 24 | { 25 | Assert.Throws(() => Fieldset("scope1TextInputFieldId")); 26 | Assert.Throws(() => Fieldset("sectionOne")); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_frames.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | internal class When_finding_frames : DriverSpecs 7 | { 8 | protected override string TestPage 9 | { 10 | get { return @"frameset.htm"; } 11 | } 12 | 13 | [Test] 14 | public void Finds_by_header_text() 15 | { 16 | Frame("I am frame one").Name.ShouldBe("frame1"); 17 | Frame("I am frame two").Name.ShouldBe("frame2"); 18 | } 19 | 20 | [Test] 21 | public void Finds_by_name() 22 | { 23 | Frame("frame1").Name.ShouldBe("frame1"); 24 | Frame("frame2").Name.ShouldBe("frame2"); 25 | } 26 | 27 | [Test] 28 | public void Finds_by_id() 29 | { 30 | Frame("frame1id").Name.ShouldBe("frame1"); 31 | Frame("frame2id").Name.ShouldBe("frame2"); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_ids.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | public class When_finding_ids : DriverSpecs 7 | { 8 | [Test] 9 | public void Finds_element_by_id() 10 | { 11 | Id("firstLinkId").Id.ShouldBe("firstLinkId"); 12 | Id("secondLinkId").Id.ShouldBe("secondLinkId"); 13 | } 14 | 15 | [Test] 16 | public void Does_not_find_display_none() 17 | { 18 | Assert.Throws(() => Id("invisibleLinkByDisplayId")); 19 | } 20 | 21 | [Test] 22 | public void Does_not_find_visibility_hidden_links() 23 | { 24 | Assert.Throws(() => Id("invisibleLinkByVisibilityId")); 25 | } 26 | 27 | [Test] 28 | public void Ignores_exact_option() 29 | { 30 | Assert.Throws(() => Id("firstLink", options: Options.Substring)); 31 | Assert.Throws(() => Id("firstLink", options: Options.Exact)); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_iframes.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | internal class When_finding_iframes : DriverSpecs 7 | { 8 | [Test] 9 | public void Finds_by_header_text() 10 | { 11 | Frame("I am iframe one").Id.ShouldBe("iframe1"); 12 | Frame("I am iframe two").Id.ShouldBe("iframe2"); 13 | } 14 | 15 | [Test] 16 | public void Finds_by_id() 17 | { 18 | Frame("iframe1").Id.ShouldBe("iframe1"); 19 | Frame("iframe2").Id.ShouldBe("iframe2"); 20 | } 21 | 22 | [Test] 23 | public void Finds_by_title() 24 | { 25 | Frame("iframe one title").Id.ShouldBe("iframe1"); 26 | Frame("iframe two title").Id.ShouldBe("iframe2"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_links.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | public class When_finding_links : DriverSpecs 7 | { 8 | [Test] 9 | public void Finds_link_by_text() 10 | { 11 | Link("first link").Id.ShouldBe("firstLinkId"); 12 | Link("second link").Id.ShouldBe("secondLinkId"); 13 | } 14 | [Test] 15 | public void Finds_link_by_href() 16 | { 17 | Link("#link1href").Id.ShouldBe("firstLinkId"); 18 | Link("#link2href").Id.ShouldBe("secondLinkId"); 19 | } 20 | 21 | [Test] 22 | public void Does_not_find_display_none() 23 | { 24 | Assert.Throws(() => Link("I am an invisible link by display")); 25 | } 26 | 27 | [Test] 28 | public void Does_not_find_visibility_hidden_links() 29 | { 30 | Assert.Throws(() => Link("I am an invisible link by visibility")); 31 | } 32 | 33 | [Test] 34 | public void Finds_a_link_with_both_types_of_quote_in_its_text() 35 | { 36 | Assert.That(Link("I'm a link with \"both\" types of quote in my text").Id, Is.EqualTo("linkWithBothQuotesId")); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_sections.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | internal class When_finding_sections : DriverSpecs 7 | { 8 | [Test] 9 | public void Finds_by_h1_text() 10 | { 11 | Section("Section One h1").Id.ShouldBe("sectionOne"); 12 | Section("Section Two h1").Id.ShouldBe("sectionTwo"); 13 | } 14 | 15 | [Test] 16 | public void Finds_by_h2_text() 17 | { 18 | Section("Section One h2").Id.ShouldBe("sectionOne"); 19 | Section("Section Two h2").Id.ShouldBe("sectionTwo"); 20 | } 21 | 22 | [Test] 23 | public void Finds_by_h3_text() 24 | { 25 | Section("Section One h3").Id.ShouldBe("sectionOne"); 26 | Section("Section Two h3").Id.ShouldBe("sectionTwo"); 27 | } 28 | 29 | [Test] 30 | public void Finds_by_h6_text() 31 | { 32 | Section("Section One h6").Id.ShouldBe("sectionOne"); 33 | Section("Section Two h6").Id.ShouldBe("sectionTwo"); 34 | } 35 | 36 | [Test] 37 | public void Finds_section_by_id() 38 | { 39 | Section("sectionOne").Id.ShouldBe("sectionOne"); 40 | Section("sectionTwo").Id.ShouldBe("sectionTwo"); 41 | } 42 | 43 | 44 | [Test] 45 | public void Only_finds_div_and_section() 46 | { 47 | Assert.Throws(() => Section("scope1TextInputFieldId")); 48 | Assert.Throws(() => Section("fieldsetScope2")); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_finding_sections_as_divs.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | internal class When_finding_sections_as_divs : DriverSpecs 7 | { 8 | [Test] 9 | public void Finds_by_h1_text() 10 | { 11 | Section("Div Section One h1").Id.ShouldBe("divSectionOne"); 12 | Section("Div Section Two h1").Id.ShouldBe("divSectionTwo"); 13 | } 14 | 15 | [Test] 16 | public void Finds_by_h2_text() 17 | { 18 | Section("Div Section One h2").Id.ShouldBe("divSectionOne"); 19 | Section("Div Section Two h2").Id.ShouldBe("divSectionTwo"); 20 | } 21 | 22 | [Test] 23 | public void Finds_by_h3_text() 24 | { 25 | Section("Div Section One h3").Id.ShouldBe("divSectionOne"); 26 | Section("Div Section Two h3").Id.ShouldBe("divSectionTwo"); 27 | } 28 | 29 | [Test] 30 | public void Finds_by_h6_text() 31 | { 32 | Section("Div Section One h6").Id.ShouldBe("divSectionOne"); 33 | Section("Div Section Two h6").Id.ShouldBe("divSectionTwo"); 34 | } 35 | 36 | 37 | [Test] 38 | public void Finds_by_h2_text_within_child_link() 39 | { 40 | Section("Div Section One h2 with link").Id.ShouldBe("divSectionOneWithLink"); 41 | Section("Div Section Two h2 with link").Id.ShouldBe("divSectionTwoWithLink"); 42 | } 43 | 44 | 45 | [Test] 46 | public void Finds_by_div_by_id() 47 | { 48 | Section("divSectionOne").Text.ShouldBe(Section("Div Section One h1").Text); 49 | Section("divSectionTwo").Text.ShouldBe(Section("Div Section Two h1").Text); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_forced_to_consider_invisible_elements.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.Drivers.Tests 6 | { 7 | public class When_forced_to_find_invisible_elements : DriverSpecs 8 | { 9 | [Test] 10 | public void Does_find_hidden_inputs() 11 | { 12 | Assert.That(Field("firstHiddenInputId", options: Options.Invisible) 13 | .Value, 14 | Is.EqualTo("first hidden input")); 15 | Assert.Throws(() => Field("firstHiddenInputId")); 16 | } 17 | 18 | [Test] 19 | public void Does_find_invisible_elements() 20 | { 21 | Assert.That(Button("firstInvisibleInputId", options: Options.Invisible) 22 | .Name, 23 | Is.EqualTo("firstInvisibleInputName")); 24 | Assert.Throws(() => Button("firstInvisibleInputId")); 25 | } 26 | 27 | [Test] 28 | public void Explains_it_cannot_find_invisible_elements_by_text() 29 | { 30 | Assert.Throws(() => Css("#firstInvisibleSpanId", new Regex("I am an invisible span"), options: Options.Invisible)); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_hovering.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | public class When_hovering : DriverSpecs 7 | { 8 | //Broken in firefox & edge due to the driver not scrolling when moving to an element 9 | [Test] 10 | public void Mouses_over_the_underlying_element() 11 | { 12 | var element = Id("hoverOnMeTest"); 13 | element.Text.ShouldBe("Hover on me"); 14 | Driver.Hover(element); 15 | 16 | Id("hoverOnMeTest").Text.ShouldBe("Hover on me - hovered"); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_inspecting_dialog_text.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | using Coypu.Drivers.Playwright; 4 | namespace Coypu.Drivers.Tests 5 | { 6 | internal class When_inspecting_dialog_text : DriverSpecs 7 | { 8 | [Test] 9 | public void Finds_exact_text_in_alert() 10 | { 11 | if (Driver is PlaywrightDriver) 12 | { 13 | Assert.Ignore("Playwright does not support the obsolete HasDialog API"); 14 | } 15 | using (Driver) 16 | { 17 | Driver.Click(Link("Trigger an alert")); 18 | Driver.HasDialog("You have triggered an alert and this is the text.", Root).ShouldBeTrue(); 19 | Driver.HasDialog("You have triggered a different alert and this is the different text.", Root).ShouldBeFalse(); 20 | } 21 | } 22 | [Test] 23 | public void Finds_exact_text_in_confirm() 24 | { 25 | if (Driver is PlaywrightDriver) 26 | { 27 | Assert.Ignore("Playwright does not support the obsolete HasDialog API"); 28 | } 29 | using (Driver) 30 | { 31 | Driver.Click(Link("Trigger a confirm")); 32 | Driver.HasDialog("You have triggered a confirm and this is the text.", Root).ShouldBeTrue(); 33 | Driver.HasDialog("You have triggered a different confirm and this is the different text.", Root).ShouldBeFalse(); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_inspecting_html.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text.RegularExpressions; 3 | using NUnit.Framework; 4 | using Coypu.AcceptanceTests; 5 | 6 | namespace Coypu.Drivers.Tests 7 | { 8 | internal class When_inspecting_html : DriverSpecs 9 | { 10 | public void VisitTestPage() 11 | { 12 | Driver.Visit(PathHelper.GetPageHtmlPath("table.htm"), Root); 13 | } 14 | 15 | [Test] 16 | public void FindsElementOuterHTML() 17 | { 18 | VisitTestPage(); 19 | 20 | var outerHTML = Normalise(Css("table").OuterHTML); 21 | Assert.That(outerHTML, Is.EqualTo("
nameage
bob12
jane79
")); 22 | } 23 | 24 | [Test] 25 | public void FindsElementInnerHTML() 26 | { 27 | VisitTestPage(); 28 | 29 | var innerHTML = Normalise(Css("table").InnerHTML); 30 | Assert.That(innerHTML, Is.EqualTo("nameagebob12jane79")); 31 | } 32 | 33 | [Test] 34 | public void FindsWindowOuterHTML() 35 | { 36 | VisitTestPage(); 37 | 38 | var outerHTML = Normalise(Driver.Window.OuterHTML); 39 | Assert.That(outerHTML, Is.EqualTo("table
nameage
bob12
jane79
")); 40 | } 41 | 42 | [Test] 43 | public void FindsWindowInnerHTML() 44 | { 45 | VisitTestPage(); 46 | 47 | var innerHTML = Normalise(Driver.Window.InnerHTML); 48 | Assert.That(innerHTML, Is.EqualTo("table
nameage
bob12
jane79
")); 49 | } 50 | 51 | private static string Normalise(string innerHtml) 52 | { 53 | return new Regex(@"\s+<", RegexOptions.Multiline).Replace(innerHtml, "<").Replace(" webdriver=\"true\"","").ToLower().TrimEnd(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_inspecting_location.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Coypu.Actions; 4 | using Coypu.Finders; 5 | using Coypu.Timing; 6 | using NUnit.Framework; 7 | 8 | namespace Coypu.Drivers.Tests 9 | { 10 | internal class When_navigating : DriverSpecs 11 | { 12 | [Test] 13 | public void Gets_the_current_browser_location() 14 | { 15 | Driver.Visit(TestSiteUrl("/"), Root); 16 | Assert.That(Driver.Location(Root), Is.EqualTo(new Uri(TestSiteUrl("/")))); 17 | 18 | Driver.Visit(TestSiteUrl("/auto_login"), Root); 19 | Assert.That(Driver.Location(Root), Is.EqualTo(new Uri(TestSiteUrl("/auto_login")))); 20 | } 21 | 22 | [Test] 23 | public void Gets_location_for_correct_window_scope() 24 | { 25 | Driver.Click(Link("Open pop up window")); 26 | var popUp = new BrowserWindow(DefaultSessionConfiguration, new WindowFinder(Driver, "Pop Up Window", Root, DefaultOptions), Driver, null, null, null, DisambiguationStrategy); 27 | RetryUntilTimeoutTimingStrategy.Retry(() => popUp.Now()); 28 | Assert.That(Driver.Location(popUp) 29 | .AbsoluteUri, 30 | Does.EndWith("src/Coypu.Drivers.Tests/bin/Debug/net6.0/html/popup.htm")); 31 | } 32 | 33 | [Test] 34 | public void Not_just_when_set_by_visit() 35 | { 36 | Driver.Visit(TestSiteUrl("/auto_login"), Root); 37 | Driver.ExecuteScript("document.location.href = '" + TestSiteUrl("/resource/bdd") + "'", Root); 38 | 39 | // Seems like WebDriver is not waiting on JS, has exec been made asnyc? 40 | Thread.Sleep(500); 41 | 42 | Assert.That(Driver.Location(Root), Is.EqualTo(new Uri(TestSiteUrl("/resource/bdd")))); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_inspecting_title.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | internal class When_inspecting_title : DriverSpecs 7 | { 8 | [Test] 9 | public void Gets_the_current_page_title() 10 | { 11 | Driver.Title(Root).ShouldBe("Coypu interaction tests page"); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_navigating.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Finders; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.Drivers.Tests 6 | { 7 | internal class When_inspecting_location : DriverSpecs 8 | { 9 | [Test] 10 | public void Go_back_and_forward_in_history() 11 | { 12 | using (Driver) 13 | { 14 | Driver.Visit(TestSiteUrl("/"), Root); 15 | Driver.Visit(TestSiteUrl("/auto_login"), Root); 16 | 17 | Driver.GoBack(Root); 18 | Assert.That(Driver.Location(Root), Is.EqualTo(new Uri(TestSiteUrl("/")))); 19 | 20 | Driver.GoForward(Root); 21 | Assert.That(Driver.Location(Root), Is.EqualTo(new Uri(TestSiteUrl("/auto_login")))); 22 | 23 | } 24 | } 25 | 26 | [Test] 27 | public void Go_back_and_forward_in_correct_window_scope() 28 | { 29 | using (Driver) 30 | { 31 | Driver.Click(Link("Open pop up window")); 32 | var popUp = new BrowserWindow(DefaultSessionConfiguration, new WindowFinder(Driver, "Pop Up Window", Root, DefaultOptions), 33 | Driver, null, null, null, DisambiguationStrategy); 34 | 35 | Driver.Visit(TestSiteUrl("/auto_login"), Root); 36 | Driver.Visit(TestSiteUrl("/"), popUp); 37 | 38 | Driver.GoBack(popUp); 39 | Assert.That(Driver.Location(popUp).AbsoluteUri, 40 | Does.EndWith("src/Coypu.Drivers.Tests/bin/Debug/net6.0/html/popup.htm")); 41 | Assert.That(Driver.Location(Root).AbsoluteUri, Is.EqualTo(TestSiteUrl("/auto_login"))); 42 | 43 | Driver.GoForward(popUp); 44 | Assert.That(Driver.Location(popUp).AbsoluteUri, Is.EqualTo(TestSiteUrl("/"))); 45 | 46 | Driver.GoBack(Root); 47 | Assert.That(Driver.Location(Root).AbsoluteUri, Does.EndWith("/html/InteractionTestsPage.htm")); 48 | Assert.That(Driver.Location(popUp).AbsoluteUri, Is.EqualTo(TestSiteUrl("/"))); 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_refreshing_windows.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Finders; 3 | using Coypu.Timing; 4 | using NUnit.Framework; 5 | 6 | namespace Coypu.Drivers.Tests 7 | { 8 | internal class When_refreshing_windows : DriverSpecs 9 | { 10 | [Test] 11 | public void RefreshCausesPageToReload() 12 | { 13 | RefreshCausesScopeToReload(Root); 14 | } 15 | 16 | [Test] 17 | public void RefreshesCorrectWindowScope() 18 | { 19 | Driver.Click(Link("Open pop up window")); 20 | var popUp = new BrowserWindow(DefaultSessionConfiguration, new WindowFinder(Driver,"Pop Up Window",Root,DefaultOptions), Driver, null, null, null, DisambiguationStrategy); 21 | RetryUntilTimeoutTimingStrategy.Retry(() => popUp.Now()); 22 | RefreshCausesScopeToReload(popUp); 23 | } 24 | 25 | private static void RefreshCausesScopeToReload(DriverScope driverScope) 26 | { 27 | var tickBeforeRefresh = long.Parse(Driver.ExecuteScript("return window.SpecData.CurrentTick;", driverScope).ToString()); 28 | 29 | Driver.Refresh(driverScope); 30 | 31 | var tickAfterRefresh = long.Parse(Driver.ExecuteScript("return window.SpecData.CurrentTick;", driverScope).ToString()); 32 | 33 | Assert.That(tickAfterRefresh, Is.GreaterThan(tickBeforeRefresh)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_saving_screenshots.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.IO; 3 | using NUnit.Framework; 4 | using Coypu.AcceptanceTests; 5 | 6 | namespace Coypu.Drivers.Tests 7 | { 8 | internal class When_saving_screenshots : DriverSpecs 9 | { 10 | [Test] 11 | public void SavesAScreenshot() 12 | { 13 | Scope driverScope = Root; 14 | Driver.Visit(PathHelper.GetPageHtmlPath("test-card.jpg"), driverScope); 15 | Driver.ResizeTo(new Size(800, 600), driverScope); 16 | 17 | string saveAs = Path.Combine(Path.GetTempPath(), "expect-saved-here.jpg"); 18 | try 19 | { 20 | Driver.SaveScreenshot(saveAs, driverScope); 21 | Assert.That(File.Exists(saveAs), "Expected screenshot saved to " + new FileInfo(saveAs).FullName); 22 | } 23 | finally 24 | { 25 | if (File.Exists(saveAs)) 26 | File.Delete(saveAs); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_sending_keys_to_elements.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Drivers.Tests 5 | { 6 | internal class When_sending_keys_to_elements : DriverSpecs 7 | { 8 | [Test] 9 | public void Sets_value_of_text_input_field_with_id() 10 | { 11 | var textField = Field("containerLabeledTextInputFieldName"); 12 | textField.Value.ShouldBe("text input field two val"); 13 | 14 | Driver.SendKeys(textField, " - send keys without any click, clear first, etc"); 15 | 16 | textField.Value.ShouldBe("text input field two val - send keys without any click, clear first, etc"); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/When_uploading_files.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Shouldly; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.Drivers.Tests 6 | { 7 | internal class When_uploading_files : DriverSpecs 8 | { 9 | [Test] 10 | public void Sets_the_path_to_be_uploaded() 11 | { 12 | const string someLocalFile = @"local.file"; 13 | try 14 | { 15 | var directoryInfo = new DirectoryInfo(Path.GetTempPath()); 16 | var fullPath = Path.Combine(directoryInfo.FullName, someLocalFile); 17 | using (File.Create(fullPath)) 18 | { 19 | } 20 | 21 | var textField = Field("forLabeledFileFieldId"); 22 | Driver.Set(textField, fullPath); 23 | 24 | var findAgain = Field("forLabeledFileFieldId"); 25 | findAgain.Value.ShouldEndWith(someLocalFile); 26 | } 27 | finally 28 | { 29 | File.Delete(someLocalFile); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/html/frame1.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | frame one title 6 | 7 | 8 |

I am frame one

9 | 10 |
11 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/html/frame2.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | frame two title 6 | 7 | 8 |

I am frame two

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/html/frameset.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/html/iFrame1.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | iframe one title 6 | 7 | 8 |

I am iframe one

9 | 10 |
11 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/html/iFrame2.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | iframe two title 6 | 7 | 8 |

I am iframe two

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/html/popUp.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Pop Up Window 6 | 7 | 8 |

I am a pop up window

9 | 10 |
11 | 15 | 16 |
17 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/html/popUp2.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Pop Up Window 2 6 | 7 | 8 |

I am a pop up window 2

9 | 10 |
11 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/html/table.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Table 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
NameAge
Bob12
Jane79
24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/html/test-card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/featurist/coypu/889ebb8d91b03d8fa7bd348cc0788d30d1c6b6c2/src/Coypu.Drivers.Tests/html/test-card.jpg -------------------------------------------------------------------------------- /src/Coypu.Drivers.Tests/html/test-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/featurist/coypu/889ebb8d91b03d8fa7bd348cc0788d30d1c6b6c2/src/Coypu.Drivers.Tests/html/test-card.png -------------------------------------------------------------------------------- /src/Coypu.NUnit/Coypu.NUnit.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {14FB5A97-D3A7-4759-BB6A-03CAFC9B8FAB} 4 | net6.0 5 | true 6 | Coypu.NUnit 7 | 4.2.0.0 8 | 4.2.0.0 9 | Coypu.NUnit 10 | 4.2.0 11 | NUnit matchers for Coypu. 12 | MIT 13 | https://github.com/featurist/coypu/ 14 | Copyright © 2024 Adrian Longley and Contributors 15 | browser, automation, selenium, testing, nunit 16 | 7.3 17 | Adrian Longley, Jakub Obstarczyk 18 | Coypu 19 | https://github.com/featurist/coypu 20 | git 21 | en 22 | true 23 | true 24 | snupkg 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | full 33 | 34 | 35 | pdbonly 36 | 37 | 38 | bin\Debug\Coypu.NUnit.xml 39 | bin\Debug\ 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/Coypu.NUnit/Matchers/AggregateMatcher.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework.Constraints; 3 | 4 | namespace Coypu.NUnit.Matchers 5 | { 6 | public class AggregateMatcher : Constraint where T:Constraint 7 | { 8 | readonly IEnumerable _innerConstraints; 9 | 10 | public AggregateMatcher(IEnumerable innerConstraints) 11 | { 12 | _innerConstraints = innerConstraints; 13 | 14 | } 15 | 16 | public override ConstraintResult ApplyTo(TActual actual) 17 | { 18 | foreach (var innerConstraint in _innerConstraints) 19 | { 20 | var result = innerConstraint.ApplyTo(actual); 21 | 22 | if (!result.IsSuccess) 23 | { 24 | return new ConstraintResult(innerConstraint, result.ActualValue, false); 25 | } 26 | } 27 | 28 | return new ConstraintResult(this, actual, true); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Coypu.NUnit/Matchers/HasContentMatcher.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using NUnit.Framework.Constraints; 3 | 4 | namespace Coypu.NUnit.Matchers 5 | { 6 | public class HasContentMatcher : Constraint 7 | { 8 | private readonly string _expectedContent; 9 | private readonly Options _options; 10 | private string _actualContent; 11 | 12 | public HasContentMatcher(string expectedContent, Options options) 13 | { 14 | _expectedContent = expectedContent; 15 | _options = options; 16 | } 17 | 18 | public override ConstraintResult ApplyTo(TActual actual) 19 | { 20 | var scope = actual as DriverScope; 21 | var hasContent = scope.HasContent(_expectedContent, _options); 22 | if (!hasContent) 23 | { 24 | _actualContent = scope.Text; 25 | hasContent = _actualContent.Contains(_expectedContent); 26 | } 27 | return new ConstraintResult(this, actual, hasContent); 28 | } 29 | 30 | public override string Description => "Expected to find content: " + _expectedContent + "\nin:\n" + _actualContent; 31 | } 32 | 33 | public class HasContentMatchMatcher : Constraint 34 | { 35 | private readonly Regex _expectedContent; 36 | private readonly Options _options; 37 | private string _actualContent; 38 | 39 | public HasContentMatchMatcher(Regex expectedContent, Options options) 40 | { 41 | _expectedContent = expectedContent; 42 | _options = options; 43 | } 44 | 45 | public override ConstraintResult ApplyTo(TActual actual) 46 | { 47 | var scope = actual as DriverScope; 48 | var hasContent = scope.HasContentMatch(_expectedContent, _options); 49 | if (!hasContent) 50 | { 51 | _actualContent = scope.Text; 52 | hasContent = _expectedContent.IsMatch(_actualContent); 53 | } 54 | return new ConstraintResult(this, actual, hasContent); 55 | } 56 | 57 | public override string Description => "Expected to find content: " + _expectedContent + "\nin:\n" + _actualContent; 58 | } 59 | } -------------------------------------------------------------------------------- /src/Coypu.NUnit/Matchers/HasNoContentMatcher.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework.Constraints; 2 | 3 | namespace Coypu.NUnit.Matchers 4 | { 5 | public class HasNoContentMatcher : Constraint 6 | { 7 | private readonly string _expectedContent; 8 | private readonly Options _options; 9 | private string _actualContent; 10 | 11 | public HasNoContentMatcher(string expectedContent, Options options) 12 | { 13 | _expectedContent = expectedContent; 14 | _options = options; 15 | } 16 | 17 | public override ConstraintResult ApplyTo(TActual actual) 18 | { 19 | var scope = (Scope)actual; 20 | var hasNoContent = scope.HasNoContent(_expectedContent, _options); 21 | if (!hasNoContent) 22 | { 23 | _actualContent = scope.Now().Text; 24 | hasNoContent = !_actualContent.Contains(_expectedContent); 25 | } 26 | 27 | return new ConstraintResult(this, actual, hasNoContent); 28 | } 29 | 30 | public override string Description => "Expected NOT to find content: " + _expectedContent + "\nin:\n" + _actualContent; 31 | } 32 | } -------------------------------------------------------------------------------- /src/Coypu.NUnit/Matchers/HasNoValueMatcher.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework.Constraints; 2 | 3 | namespace Coypu.NUnit.Matchers 4 | { 5 | public class HasNoValueMatcher : Constraint 6 | { 7 | private readonly string _expectedContent; 8 | private readonly Options _options; 9 | private string _actualContent; 10 | 11 | public HasNoValueMatcher(string expectedContent, Options options) 12 | { 13 | _expectedContent = expectedContent; 14 | _options = options; 15 | } 16 | 17 | public override ConstraintResult ApplyTo(TActual actual) 18 | { 19 | var elementScope = actual as ElementScope; 20 | var hasNoValue = elementScope.HasNoValue(_expectedContent, _options); 21 | if (!hasNoValue) 22 | _actualContent = elementScope.Value; 23 | return new ConstraintResult(this, actual, hasNoValue); 24 | } 25 | 26 | public override string Description => "Expected NOT to find value: " + _expectedContent + "\nin:\n" + _actualContent; 27 | } 28 | } -------------------------------------------------------------------------------- /src/Coypu.NUnit/Matchers/HasValueMatcher.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework.Constraints; 2 | 3 | namespace Coypu.NUnit.Matchers 4 | { 5 | public class HasValueMatcher : Constraint 6 | { 7 | private readonly string _expectedContent; 8 | private readonly Options _options; 9 | private string _actualContent; 10 | 11 | public HasValueMatcher(string expectedContent, Options options) 12 | { 13 | _expectedContent = expectedContent; 14 | _options = options; 15 | } 16 | 17 | public override ConstraintResult ApplyTo(TActual actual) 18 | { 19 | var elementScope = actual as ElementScope; 20 | var hasValue = elementScope.HasValue(_expectedContent, _options); 21 | if (!hasValue) 22 | { 23 | _actualContent = elementScope.Value; 24 | hasValue = _actualContent == _expectedContent; 25 | } 26 | return new ConstraintResult(this, actual, hasValue); 27 | } 28 | 29 | public override string Description => "Expected to find value: " + _expectedContent + "\nin:\n" + _actualContent; 30 | } 31 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/Coypu.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | {3ACF8A72-2EAB-42E7-B60E-BB781D3B0DB2} 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Coypu.Tests/TestBuilders/FindsFirstWitNoDisambiguationStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using Coypu.Finders; 6 | 7 | namespace Coypu.Tests.TestBuilders 8 | { 9 | public class FirstOrDefaultNoDisambiguationStrategy : DisambiguationStrategy 10 | { 11 | public Element ResolveQuery(ElementFinder elementFinder) 12 | { 13 | return elementFinder.Find(elementFinder.Options).FirstOrDefault(); 14 | } 15 | } 16 | 17 | public class ThrowsWhenMissingButNoDisambiguationStrategy : DisambiguationStrategy 18 | { 19 | public Element ResolveQuery(ElementFinder elementFinder) 20 | { 21 | var all = elementFinder.Find(elementFinder.Options); 22 | var array = AsArray(all, elementFinder); 23 | if (!array.Any()) 24 | throw elementFinder.GetMissingException(); 25 | 26 | return all.First(); 27 | } 28 | 29 | private static Element[] AsArray(IEnumerable all, ElementFinder elementFinder) 30 | { 31 | try { 32 | return all.ToArray(); 33 | } 34 | catch(InvalidOperationException) { 35 | // Elements have changed due to async page behaviour 36 | throw elementFinder.GetMissingException(); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Coypu.Tests/TestBuilders/TestSessionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Coypu.Timing; 2 | using Coypu.Tests.When_interacting_with_the_browser; 3 | using Coypu.WebRequests; 4 | 5 | namespace Coypu.Tests.TestBuilders 6 | { 7 | internal class TestSessionBuilder 8 | { 9 | internal static BrowserSession Build(SessionConfiguration sessionConfiguration, 10 | IDriver driver, 11 | TimingStrategy timingStrategy, 12 | Waiter waiter, 13 | RestrictedResourceDownloader restrictedResourceDownloader, 14 | UrlBuilder urlBuilder, 15 | DisambiguationStrategy disambiguationStrategy = null) 16 | { 17 | disambiguationStrategy = disambiguationStrategy ?? new FirstOrDefaultNoDisambiguationStrategy(); 18 | 19 | return new BrowserSession(sessionConfiguration, 20 | new StubDriverFactory(driver), 21 | timingStrategy, 22 | waiter, 23 | urlBuilder, 24 | disambiguationStrategy, 25 | restrictedResourceDownloader); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/TestDoubles/ImmediateSingleExecutionFakeTimingStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Actions; 3 | using Coypu.Queries; 4 | using Coypu.Timing; 5 | 6 | namespace Coypu.Tests.TestDoubles 7 | { 8 | public class ImmediateSingleExecutionFakeTimingStrategy : TimingStrategy 9 | { 10 | public T Synchronise(Query query) 11 | { 12 | return query.Run(); 13 | } 14 | 15 | public void TryUntil(BrowserAction tryThis, PredicateQuery until, Options options) 16 | { 17 | tryThis.Act(); 18 | } 19 | 20 | public bool ZeroTimeout{get; set; } 21 | public void SetOverrideTimeout(TimeSpan timeout) 22 | { 23 | } 24 | 25 | public void ClearOverrideTimeout() 26 | { 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/TestDoubles/SpyRestrictedResourceDownloader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | using Coypu.WebRequests; 4 | 5 | namespace Coypu.Tests.TestDoubles 6 | { 7 | public class SpyRestrictedResourceDownloader : RestrictedResourceDownloader 8 | { 9 | private readonly IList downloadedFiles = new List(); 10 | 11 | public IList DownloadedFiles 12 | { 13 | get { return downloadedFiles; } 14 | } 15 | 16 | public void SetCookies(IEnumerable cookies) 17 | { 18 | this.Cookies = cookies; 19 | } 20 | 21 | protected IEnumerable Cookies { get; private set; } 22 | 23 | public void DownloadFile(string resource, string saveAs) 24 | { 25 | DownloadedFiles.Add(new DownloadedFile(resource, saveAs, Cookies)); 26 | } 27 | } 28 | 29 | public class DownloadedFile 30 | { 31 | public string Resource { get; private set; } 32 | public string SaveAs { get; private set; } 33 | public IEnumerable Cookies { get; set; } 34 | 35 | public DownloadedFile(string resource, string saveAs, IEnumerable cookies) 36 | { 37 | Resource = resource; 38 | SaveAs = saveAs; 39 | Cookies = cookies; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/TestDoubles/StubElement.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | namespace Coypu.Tests.TestDoubles 3 | { 4 | public class StubElement : Element 5 | { 6 | private readonly Dictionary attributes = new Dictionary(); 7 | 8 | public string Id { get; set; } 9 | 10 | public string Text { get; set; } 11 | 12 | public string Value { get; set; } 13 | 14 | public string Name { get; set; } 15 | 16 | public string InnerHTML { get; set; } 17 | 18 | public string Title { get; set; } 19 | 20 | public bool Disabled { get; set; } 21 | 22 | public string OuterHTML { get; set; } 23 | 24 | public string SelectedOption { get; set; } 25 | 26 | public bool Selected { get; set; } 27 | 28 | public object Native { get; set; } 29 | 30 | public string this[string attributeName] 31 | { 32 | get 33 | { 34 | return attributes.ContainsKey(attributeName) ? attributes[attributeName] : string.Empty; 35 | } 36 | } 37 | 38 | public void StubAttribute(string attributeName, string value) 39 | { 40 | attributes[attributeName] = value; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/TestDoubles/StubWebResources.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | 4 | namespace Coypu.Tests.TestDoubles 5 | { 6 | public class StubWebResources 7 | { 8 | private readonly Dictionary resources = new Dictionary(); 9 | 10 | public void StubResource(string resource, StubWebResponse stubWebResponse) 11 | { 12 | resources[resource] = stubWebResponse; 13 | } 14 | 15 | public WebResponse Get(string resource) 16 | { 17 | if (!resources.ContainsKey(resource)) 18 | throw new System.ArgumentException("There was no resource at " + resource, "resource"); 19 | 20 | return resources[resource]; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/TestDoubles/StubWebResponse.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Net; 3 | 4 | namespace Coypu.Tests.TestDoubles 5 | { 6 | public class StubWebResponse : WebResponse 7 | { 8 | private readonly Stream responseStream = new MemoryStream(); 9 | 10 | public override Stream GetResponseStream() 11 | { 12 | return responseStream; 13 | } 14 | 15 | public override void Close() 16 | { 17 | responseStream.Dispose(); 18 | base.Close(); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/TestException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Coypu.Tests 4 | { 5 | public class TestException : Exception 6 | { 7 | public TestException(string message) : base(message) 8 | { 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_applying_match_strategy/Any_match_strategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Coypu.Finders; 4 | 5 | namespace Coypu.Tests.When_applying_match_strategy 6 | { 7 | public abstract class Any_match_strategy 8 | { 9 | public abstract Match Match {get;} 10 | public abstract TextPrecision TextPrecision {get;} 11 | 12 | public Options FinderOptions() 13 | { 14 | return new Options 15 | { 16 | Match = this.Match, 17 | TextPrecision = this.TextPrecision, 18 | // Some other stuff to check they are merged 19 | ConsiderInvisibleElements = true, 20 | RetryInterval = TimeSpan.FromMilliseconds(4321) 21 | }; 22 | } 23 | 24 | protected static void StubExactResults(StubElementFinder finder, Options finderOptions, IEnumerable exactResults) 25 | { 26 | finder.StubbedFindResults[Options.Merge(Options.Exact,finderOptions)] = exactResults; 27 | } 28 | 29 | protected static void StubSubstringResults(StubElementFinder finder, Options finderOptions, IEnumerable exactResults) 30 | { 31 | finder.StubbedFindResults[Options.Merge(Options.Substring, finderOptions)] = exactResults; 32 | } 33 | 34 | protected static Element ResolveQuery(StubElementFinder finder) 35 | { 36 | var strategy = new FinderOptionsDisambiguationStrategy(); 37 | var results = strategy.ResolveQuery(finder); 38 | return results; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_applying_match_strategy/StubElementFinder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Coypu.Finders; 3 | 4 | namespace Coypu.Tests.When_applying_match_strategy 5 | { 6 | public class StubElementFinder : ElementFinder 7 | { 8 | private readonly bool _supportsSubstringTextMatching; 9 | private readonly string _queryDescription; 10 | 11 | public IDictionary> StubbedFindResults = new Dictionary>(); 12 | 13 | public StubElementFinder(Options options, bool supportsSubstringTextMatching = true, string queryDescription = null) : base(null, null, null, options) 14 | { 15 | _supportsSubstringTextMatching = supportsSubstringTextMatching; 16 | _queryDescription = queryDescription; 17 | } 18 | 19 | public override bool SupportsSubstringTextMatching 20 | { 21 | get { return _supportsSubstringTextMatching; } 22 | } 23 | 24 | internal override IEnumerable Find(Options options) 25 | { 26 | return StubbedFindResults[options]; 27 | } 28 | 29 | internal override string QueryDescription 30 | { 31 | get { return _queryDescription; } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_applying_match_strategy/When_using_first_exact.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Coypu.Tests.TestDoubles; 4 | using NUnit.Framework; 5 | 6 | namespace Coypu.Tests.When_applying_match_strategy 7 | { 8 | public class When_using_first_exact : Any_match_strategy 9 | { 10 | public override Match Match 11 | { 12 | get { return Match.First; } 13 | } 14 | 15 | public override TextPrecision TextPrecision 16 | { 17 | get { return TextPrecision.Exact; } 18 | } 19 | 20 | [Test] 21 | public void When_there_is_more_than_one_match_it_picks_the_first_one() 22 | { 23 | var finderOptions = FinderOptions(); 24 | var finder = new StubElementFinder(finderOptions); 25 | var exactResults = new List { new StubElement(), new StubElement() }; 26 | 27 | StubExactResults(finder, finderOptions, exactResults); 28 | 29 | var results = ResolveQuery(finder); 30 | 31 | Assert.That(results, Is.SameAs(exactResults.First())); 32 | } 33 | 34 | 35 | [Test] 36 | public void When_there_are_no_exact_matches_it_throws_missing_exception_and_only_looks_for_exact() 37 | { 38 | var finderOptions = FinderOptions(); 39 | var finder = new StubElementFinder(finderOptions, queryDescription: "something from StubElementFinder"); 40 | 41 | StubExactResults(finder, finderOptions, new List()); 42 | 43 | try 44 | { 45 | ResolveQuery(finder); 46 | Assert.Fail("Expected missing html exception"); 47 | } 48 | catch (MissingHtmlException ex) 49 | { 50 | Assert.That(ex.Message, Is.EqualTo("Unable to find something from StubElementFinder")); 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_applying_match_strategy/When_using_first_substring.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Coypu.Tests.TestDoubles; 5 | using NUnit.Framework; 6 | 7 | namespace Coypu.Tests.When_applying_match_strategy 8 | { 9 | public class When_using_first_substring : Any_match_strategy 10 | { 11 | public override Match Match 12 | { 13 | get { return Match.First; } 14 | } 15 | 16 | public override TextPrecision TextPrecision 17 | { 18 | get { return TextPrecision.Substring; } 19 | } 20 | 21 | [Test] 22 | public void When_there_is_more_than_one_substring_match_it_picks_the_first_one() 23 | { 24 | var finderOptions = FinderOptions(); 25 | var finder = new StubElementFinder(finderOptions); 26 | var exactResults = new List { new StubElement(), new StubElement() }; 27 | 28 | StubSubstringResults(finder, finderOptions, exactResults); 29 | 30 | var results = ResolveQuery(finder); 31 | 32 | Assert.That(results, Is.SameAs(exactResults.First())); 33 | } 34 | 35 | [Test] 36 | public void When_there_are_no_substring_matches_it_throws_missing_exception() 37 | { 38 | var finderOptions = FinderOptions(); 39 | var finder = new StubElementFinder(finderOptions, queryDescription: "something from StubElementFinder"); 40 | 41 | StubSubstringResults(finder, finderOptions, new List()); 42 | 43 | try 44 | { 45 | ResolveQuery(finder); 46 | Assert.Fail("Expected missing html exception"); 47 | } 48 | catch (MissingHtmlException ex) 49 | { 50 | Assert.That(ex.Message, Is.EqualTo("Unable to find something from StubElementFinder")); 51 | } 52 | } 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_constructing_xpath.cs: -------------------------------------------------------------------------------- 1 | using Coypu.Drivers; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Tests.When_interacting_with_the_browser 5 | { 6 | [TestFixture] 7 | public class When_constructing_xpath 8 | { 9 | [Test] 10 | public void It_handles_double_and_single_quotes_with_concat() 11 | { 12 | It_handles_double_and_single_quotes_with_concat("foo", "\"foo\""); // no quotes 13 | It_handles_double_and_single_quotes_with_concat("\"foo", "'\"foo'"); // double quotes only 14 | It_handles_double_and_single_quotes_with_concat("'foo", "\"'foo\""); // single quotes only 15 | It_handles_double_and_single_quotes_with_concat("'foo\"bar", "concat(\"'foo\", '\"', \"bar\")"); // both; double quotes in mid-string 16 | It_handles_double_and_single_quotes_with_concat("'foo\"bar\"baz", "concat(\"'foo\", '\"', \"bar\", '\"', \"baz\")"); // multiple double quotes in mid-string 17 | It_handles_double_and_single_quotes_with_concat("'foo\"", "concat(\"'foo\", '\"')"); // string ends with double quotes 18 | It_handles_double_and_single_quotes_with_concat("'foo\"\"", "concat(\"'foo\", '\"', '\"')"); // string ends with run of double quotes 19 | It_handles_double_and_single_quotes_with_concat("\"'foo", "concat('\"', \"'foo\")"); // string begins with double quotes 20 | It_handles_double_and_single_quotes_with_concat("\"\"'foo", "concat('\"', '\"', \"'foo\")"); // string begins with run of double quotes 21 | It_handles_double_and_single_quotes_with_concat("'foo\"\"bar", "concat(\"'foo\", '\"', '\"', \"bar\")"); // run of double quotes in mid-string 22 | } 23 | 24 | public void It_handles_double_and_single_quotes_with_concat(string input, string escaped) 25 | { 26 | Assert.That(new XPath().Literal(input), Is.EqualTo(escaped)); 27 | } 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_controlling_the_window.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Tests.When_interacting_with_the_browser 5 | { 6 | [TestFixture] 7 | public class When_controlling_the_window : BrowserInteractionTests 8 | { 9 | [Test] 10 | public void MaximiseWindow_acts_immediately_on_driver() 11 | { 12 | var mainWindow = browserSession; 13 | mainWindow.MaximiseWindow(); 14 | 15 | Assert.That(driver.MaximiseWindowCalls.Single(), Is.SameAs(mainWindow)); 16 | } 17 | 18 | [Test] 19 | public void MaximiseWindow_acts_on_current_scope() 20 | { 21 | popupScope.MaximiseWindow(); 22 | 23 | Assert.That(driver.MaximiseWindowCalls.Single(), Is.SameAs(popupScope)); 24 | } 25 | 26 | [Test] 27 | public void ResizeWindow_acts_immediately_on_driver() 28 | { 29 | var mainWindow = browserSession; 30 | mainWindow.ResizeTo(500, 600); 31 | 32 | Assert.That(driver.ResizeToCalls.Single().Request.Width, Is.EqualTo(500)); 33 | Assert.That(driver.ResizeToCalls.Single().Request.Height, Is.EqualTo(600)); 34 | Assert.That(driver.ResizeToCalls.Single().Scope, Is.SameAs(mainWindow)); 35 | } 36 | 37 | [Test] 38 | public void ResizeWindow_acts_on_current_scope() 39 | { 40 | popupScope.ResizeTo(500, 600); 41 | 42 | Assert.That(driver.ResizeToCalls.Single().Scope, Is.SameAs(popupScope)); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_executing_script.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace Coypu.Tests.When_interacting_with_the_browser 4 | { 5 | [TestFixture] 6 | public class When_executing_script : BrowserInteractionTests 7 | { 8 | [Test] 9 | public void Visit_passes_message_directly_to_the_driver_returning_response_immediately() 10 | { 11 | const string script = "document.getElementById('asdf').click();"; 12 | const string expectedReturnValue = "script return value"; 13 | 14 | driver.StubExecuteScript(script, expectedReturnValue, browserSession); 15 | 16 | Assert.That(browserSession.ExecuteScript(script), Is.EqualTo(expectedReturnValue)); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_finding_then_checking_an_element.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Coypu.Tests.TestDoubles; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.Tests.When_interacting_with_the_browser { 6 | [TestFixture] 7 | public class When_finding_then_checking_an_element : BrowserInteractionTests 8 | { 9 | [Test] 10 | public void It_finds_then_synchronises_check_element_on_underlying_driver() 11 | { 12 | var element = new StubElement(); 13 | driver.StubId("something_to_click", element, browserSession, sessionConfiguration); 14 | SpyTimingStrategy.AlwaysReturnFromSynchronise(element); 15 | 16 | var toCheck = browserSession.FindCss("something_to_click"); 17 | 18 | toCheck.Check(); 19 | 20 | Assert.That(driver.CheckedElements, Has.No.Member(toCheck), "Check was not synchronised"); 21 | 22 | RunQueryAndCheckTiming(); 23 | 24 | Assert.That(driver.CheckedElements, Has.Member(toCheck)); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_finding_then_clicking_an_element.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Coypu.Tests.TestDoubles; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.Tests.When_interacting_with_the_browser { 6 | [TestFixture] 7 | public class When_finding_then_clicking_an_element_ : BrowserInteractionTests 8 | { 9 | [Test] 10 | public void It_finds_then_synchronises_click_element_on_underlying_driver() 11 | { 12 | var element = new StubElement(); 13 | driver.StubId("something_to_click", element, browserSession, sessionConfiguration); 14 | SpyTimingStrategy.AlwaysReturnFromSynchronise(element); 15 | 16 | var elementScope = browserSession.FindId("something_to_click"); 17 | elementScope.Click(); 18 | 19 | Assert.That(driver.ClickedElements, Has.No.Member(elementScope), "Click was not synchronised"); 20 | 21 | RunQueryAndCheckTiming(); 22 | 23 | Assert.That(driver.ClickedElements, Has.Member(elementScope)); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_finding_then_filling_in_any_field.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Coypu.Tests.TestDoubles; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.Tests.When_interacting_with_the_browser { 6 | [TestFixture] 7 | public class When_finding_then_filling_in_any_field : BrowserInteractionTests 8 | { 9 | [Test] 10 | public void It_makes_robust_call_to_find_then_clicks_element_on_underlying_driver() 11 | { 12 | var element = new StubElement(); 13 | driver.StubId("something_to_fill_in", element, browserSession, sessionConfiguration); 14 | SpyTimingStrategy.AlwaysReturnFromSynchronise(element); 15 | 16 | var elementScope = browserSession.FindId("something_to_fill_in"); 17 | 18 | elementScope.FillInWith("some filled in stuff"); 19 | 20 | Assert.That(driver.SetFields.Keys, Has.No.Member(elementScope)); 21 | 22 | RunQueryAndCheckTiming(); 23 | 24 | Assert.That(driver.SetFields.Keys, Has.Member(elementScope)); 25 | Assert.That(driver.SetFields[elementScope].Value, Is.EqualTo("some filled in stuff")); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_finding_then_hovering.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Coypu.Tests.TestDoubles; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.Tests.When_interacting_with_the_browser 6 | { 7 | [TestFixture] 8 | public class When_finding_then_hovering : BrowserInteractionTests 9 | { 10 | [Test] 11 | public void It_makes_robust_call_to_find_then_hover_element_on_underlying_driver() 12 | { 13 | var element = new StubElement(); 14 | driver.StubId("something_to_hover", element, browserSession, sessionConfiguration); 15 | SpyTimingStrategy.AlwaysReturnFromSynchronise(element); 16 | 17 | browserSession.FindId("something_to_hover").Hover(); 18 | 19 | Assert.That(driver.HoveredElements, Has.No.Member(element)); 20 | 21 | RunQueryAndCheckTiming(); 22 | 23 | Assert.That(driver.HoveredElements, Has.Member(element)); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_finding_then_unchecking_any_element.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Coypu.Tests.TestDoubles; 4 | using NUnit.Framework; 5 | 6 | namespace Coypu.Tests.When_interacting_with_the_browser 7 | { 8 | [TestFixture] 9 | public class When_finding_then_unchecking_any_element : BrowserInteractionTests 10 | { 11 | [Test] 12 | public void It_makes_robust_call_to_find_then_clicks_element_on_underlying_driver() 13 | { 14 | var element = new StubElement(); 15 | driver.StubId("something_to_click", element, browserSession, sessionConfiguration); 16 | SpyTimingStrategy.AlwaysReturnFromSynchronise(element); 17 | 18 | var toCheck = browserSession.FindCss("something_to_click"); 19 | 20 | toCheck.Uncheck(); 21 | 22 | Assert.That(driver.UncheckedElements, Has.No.Member(toCheck), "Uncheck was not synchronised"); 23 | 24 | RunQueryAndCheckTiming(); 25 | 26 | Assert.That(driver.UncheckedElements, Has.Member(toCheck)); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_inspecting_location.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Tests.When_interacting_with_the_browser 5 | { 6 | [TestFixture] 7 | public class When_inspecting_location : BrowserInteractionTests 8 | { 9 | [Test] 10 | public void It_returns_the_driver_url() 11 | { 12 | var driverLocation = new Uri("https://blank.org:8080/actual_location"); 13 | driver.StubLocation(driverLocation,browserSession); 14 | Assert.That(browserSession.Location, Is.EqualTo(driverLocation)); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_inspecting_modal_dialogs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Tests.When_interacting_with_the_browser 5 | { 6 | [TestFixture] 7 | public class When_inspecting_modal_dialogs : When_inspecting 8 | { 9 | [Test] 10 | public void HasDialog_should_wait_for_robustly_Positive_example() 11 | { 12 | Queries_robustly(true, browserSession.HasDialog, driver.StubDialog); 13 | } 14 | 15 | [Test] 16 | public void HasDialog_should_wait_for_robustly_Negative_example() 17 | { 18 | Queries_robustly(false, browserSession.HasDialog, driver.StubDialog); 19 | } 20 | 21 | [Test] 22 | public void HasNoDialog_should_wait_for_robustly_Positive_example() 23 | { 24 | Queries_robustly_reversing_result(true, browserSession.HasNoDialog, driver.StubDialog); 25 | } 26 | 27 | [Test] 28 | public void HasNoDialog_should_wait_for_robustly_Negative_example() 29 | { 30 | Queries_robustly_reversing_result(false, browserSession.HasNoDialog, driver.StubDialog); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_inspecting_title.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace Coypu.Tests.When_interacting_with_the_browser 4 | { 5 | [TestFixture] 6 | public class When_inspecting_title : BrowserInteractionTests 7 | { 8 | [Test] 9 | public void It_returns_the_driver_page_title() 10 | { 11 | var pageTitle = "Coypu interaction tests page"; 12 | driver.StubTitle(pageTitle, browserSession); 13 | Assert.That(browserSession.Title, Is.EqualTo(pageTitle)); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_interacting_with_modal_dialogs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.Tests.When_interacting_with_the_browser 6 | { 7 | [TestFixture] 8 | public class When_interacting_with_modal_dialogs : BrowserInteractionTests 9 | { 10 | [Test] 11 | public void AcceptDialog_should_make_robust_call_to_underlying_driver() 12 | { 13 | browserSession.AcceptModalDialog(); 14 | 15 | Assert.That(driver.ModalDialogsAccepted.Any(), Is.False); 16 | RunQueryAndCheckTiming(); 17 | Assert.That(driver.ModalDialogsAccepted.Single(), Is.SameAs(browserSession)); 18 | } 19 | 20 | [Test] 21 | public void CancelDialog_should_make_robust_call_to_underlying_driver() 22 | { 23 | browserSession.CancelModalDialog(); 24 | 25 | Assert.That(driver.ModalDialogsCancelled.Any(), Is.False); 26 | RunQueryAndCheckTiming(); 27 | Assert.That(driver.ModalDialogsCancelled.Single(), Is.SameAs(browserSession)); 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_saving_screenshots.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Tests.When_interacting_with_the_browser 5 | { 6 | [TestFixture] 7 | public class When_saving_screenshots : BrowserInteractionTests 8 | { 9 | [Test] 10 | public void TakeScreenshot_acts_immediately_on_driver() 11 | { 12 | TakeScreenshot_acts_immediately_on_driver(popupScope); 13 | } 14 | 15 | [Test] 16 | public void TakeScreenshot_uses_current_window_scope() 17 | { 18 | TakeScreenshot_acts_immediately_on_driver(popupScope); 19 | } 20 | 21 | private void TakeScreenshot_acts_immediately_on_driver(BrowserWindow mainWindow) 22 | { 23 | mainWindow.SaveScreenshot("save-me-here.png"); 24 | 25 | var saveScreenshotCall = driver.SaveScreenshotCalls.Single(); 26 | 27 | Assert.That(saveScreenshotCall.Request, Is.EqualTo("save-me-here.png")); 28 | 29 | Assert.That(saveScreenshotCall.Scope, Is.EqualTo(mainWindow)); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_scoping_interactions.cs: -------------------------------------------------------------------------------- 1 | using Coypu.Tests.TestBuilders; 2 | using Coypu.Tests.TestDoubles; 3 | using NUnit.Framework; 4 | 5 | namespace Coypu.Tests.When_interacting_with_the_browser 6 | { 7 | [TestFixture] 8 | public class When_scoping_interactions : BrowserInteractionTests 9 | { 10 | [Test] 11 | public void It_sets_the_scope_on_the_driver() 12 | { 13 | browserSession = TestSessionBuilder.Build(new SessionConfiguration(), driver, new ImmediateSingleExecutionFakeTimingStrategy(), fakeWaiter, null, stubUrlBuilder); 14 | var section = new StubElement(); 15 | var expectedLink = new StubElement(); 16 | driver.StubId("some_section", section, browserSession, sessionConfiguration); 17 | 18 | var innerScope = browserSession.FindSection("some section"); 19 | 20 | driver.StubId("some_element", expectedLink, innerScope, sessionConfiguration); 21 | 22 | var actualLink = innerScope.FindId("some_element").Now(); 23 | 24 | Assert.That(actualLink, Is.SameAs(expectedLink)); 25 | 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_interacting_with_the_browser/When_visiting.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Tests.When_interacting_with_the_browser 5 | { 6 | [TestFixture] 7 | public class When_visiting : BrowserInteractionTests 8 | { 9 | [Test] 10 | public void It_uses_a_fully_qualified_url_from_the_url_builder() 11 | { 12 | stubUrlBuilder.SetStubUrl("/some/resource", "http://blank.org"); 13 | browserSession.Visit("/some/resource"); 14 | Assert.That(driver.Visits.Single().Request, Is.EqualTo("http://blank.org")); 15 | Assert.That(driver.Visits.Single().Scope, Is.EqualTo(browserSession)); 16 | } 17 | 18 | [Test] 19 | public void It_uses_the_current_scope() 20 | { 21 | stubUrlBuilder.SetStubUrl("/some/resource", "http://blank.org"); 22 | popupScope.Visit("/some/resource"); 23 | 24 | Assert.That(driver.Visits.Single().Request, Is.EqualTo("http://blank.org")); 25 | Assert.That(driver.Visits.Single().Scope, Is.EqualTo(popupScope)); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_making_browser_interactions_robust/TestActions.cs: -------------------------------------------------------------------------------- 1 | using Coypu.Actions; 2 | 3 | namespace Coypu.Tests.When_making_browser_interactions_robust 4 | { 5 | public class CountTriesAction : BrowserAction 6 | { 7 | public CountTriesAction(Options options) : base(null, options) 8 | { 9 | } 10 | 11 | public int Tries { get; private set; } 12 | 13 | public override void Act() 14 | { 15 | Tries++; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/When_making_browser_interactions_robust/When_waiting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using Coypu.Timing; 4 | using NUnit.Framework; 5 | 6 | namespace Coypu.Tests.When_making_browser_interactions_robust 7 | { 8 | [TestFixture] 9 | public class When_waiting 10 | { 11 | public const int AccuracyMilliseconds = 40; 12 | 13 | [Test] 14 | public void It_sleeps_for_the_expected_time_Case_1() 15 | { 16 | It_sleeps_for_the_expected_time(100); 17 | } 18 | 19 | [Test] 20 | public void It_sleeps_for_the_expected_time_Case_2() 21 | { 22 | It_sleeps_for_the_expected_time(200); 23 | } 24 | 25 | public void It_sleeps_for_the_expected_time(int expectedDurationMilliseconds) 26 | { 27 | var waiter = new StopwatchWaiter(); 28 | var stopWatch = Stopwatch.StartNew(); 29 | var expectedDuration = TimeSpan.FromMilliseconds(expectedDurationMilliseconds); 30 | 31 | waiter.Wait(expectedDuration); 32 | 33 | var actualWait = stopWatch.ElapsedMilliseconds; 34 | 35 | const int toleranceMilliseconds = AccuracyMilliseconds; 36 | 37 | Assert.That((int)actualWait, Is.InRange(expectedDurationMilliseconds - toleranceMilliseconds, 38 | expectedDurationMilliseconds + toleranceMilliseconds)); 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Coypu.Tests/When_starting_and_ending_sessions.cs: -------------------------------------------------------------------------------- 1 | using Coypu.Tests.TestDoubles; 2 | using NUnit.Framework; 3 | 4 | namespace Coypu.Tests 5 | { 6 | [TestFixture] 7 | public class When_starting_and_ending_sessions 8 | { 9 | private SessionConfiguration SessionConfiguration; 10 | 11 | [SetUp] 12 | public void SetUp() 13 | { 14 | SessionConfiguration = new SessionConfiguration(); 15 | SessionConfiguration.Driver = typeof (FakeDriver); 16 | } 17 | 18 | [Test] 19 | public void Dispose_handles_a_disposed_session() 20 | { 21 | var browserSession = new BrowserSession(SessionConfiguration); 22 | 23 | browserSession.Dispose(); 24 | browserSession.Dispose(); 25 | } 26 | 27 | [Test] 28 | public void A_session_gets_its_driver_from_config() 29 | { 30 | SessionConfiguration.Driver = typeof (FakeDriver); 31 | using (var browserSession = new BrowserSession(SessionConfiguration)) 32 | { 33 | Assert.That(browserSession.Driver, Is.TypeOf(typeof(FakeDriver))); 34 | } 35 | 36 | SessionConfiguration.Driver = typeof(StubDriver); 37 | using (var browserSession = new BrowserSession(SessionConfiguration)) 38 | { 39 | Assert.That(browserSession.Driver, Is.TypeOf(typeof(StubDriver))); 40 | } 41 | } 42 | 43 | [Test] 44 | public void Session_exposes_native_driver_if_you_really_need_it() 45 | { 46 | using (var browserSession = new BrowserSession(SessionConfiguration)) 47 | { 48 | Assert.That(browserSession.Native, Is.EqualTo("Native driver on fake driver")); 49 | } 50 | } 51 | 52 | } 53 | } -------------------------------------------------------------------------------- /src/Coypu.Tests/proxysettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information" 5 | } 6 | }, 7 | "AllowedHosts": "*", 8 | "ReverseProxy": { 9 | "Routes": { 10 | "route1" : { 11 | "ClusterId": "cluster1", 12 | "Match": { 13 | "Path": "{**catch-all}", 14 | "Hosts": ["www.example.com"] 15 | } 16 | }, 17 | "route2" : { 18 | "ClusterId": "cluster2", 19 | "Match": { 20 | "Path": "{**catch-all}" 21 | } 22 | } 23 | }, 24 | "Clusters": { 25 | "cluster1": { 26 | "Destinations": { 27 | "destination1": { 28 | "Address": "http://localhost:5000/" 29 | } 30 | } 31 | }, 32 | "cluster2": { 33 | "Destinations": { 34 | "destination1": { 35 | "Address": "https://*/" 36 | } 37 | } 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Coypu/Actions/AcceptModalDialog.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Actions 2 | { 3 | internal class AcceptModalDialog : DriverAction 4 | { 5 | private readonly DriverScope driverScope; 6 | 7 | internal AcceptModalDialog(DriverScope driverScope, IDriver driver, Options options) : base(driver, driverScope, options) 8 | { 9 | this.driverScope = driverScope; 10 | } 11 | 12 | public override void Act() 13 | { 14 | Driver.AcceptModalDialog(driverScope); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Coypu/Actions/BrowserAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Queries; 3 | 4 | namespace Coypu.Actions 5 | { 6 | public abstract class BrowserAction : Query 7 | { 8 | public Options Options { get; } 9 | public DriverScope Scope { get; } 10 | 11 | protected BrowserAction(DriverScope scope, Options options) 12 | { 13 | Options = options; 14 | Scope = scope; 15 | } 16 | 17 | public abstract void Act(); 18 | 19 | public object Run() 20 | { 21 | Act(); 22 | return null; 23 | } 24 | 25 | public object ExpectedResult => null; 26 | } 27 | } -------------------------------------------------------------------------------- /src/Coypu/Actions/CancelModalDialog.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Actions 2 | { 3 | internal class CancelModalDialog : DriverAction 4 | { 5 | private readonly DriverScope driverScope; 6 | 7 | internal CancelModalDialog(DriverScope driverScope, IDriver driver, Options options) : base(driver, driverScope, options) 8 | { 9 | this.driverScope = driverScope; 10 | } 11 | 12 | public override void Act() 13 | { 14 | Driver.CancelModalDialog(driverScope); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Coypu/Actions/CheckAction.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Actions 2 | { 3 | internal class CheckAction : DriverAction 4 | { 5 | private readonly ElementScope element; 6 | 7 | internal CheckAction(IDriver driver, ElementScope element, Options options) 8 | : base(driver, element, options) 9 | { 10 | this.element = element; 11 | } 12 | 13 | public override void Act() 14 | { 15 | Driver.Check(element); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Coypu/Actions/Choose.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Actions 2 | { 3 | internal class Choose : DriverAction 4 | { 5 | private readonly ElementScope elementScope; 6 | 7 | internal Choose(IDriver driver, ElementScope elementScope, Options options) 8 | : base(driver, elementScope, options) 9 | { 10 | this.elementScope = elementScope; 11 | } 12 | 13 | public override void Act() 14 | { 15 | Driver.Choose(elementScope); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Coypu/Actions/ClickAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Timing; 3 | 4 | namespace Coypu.Actions 5 | { 6 | internal class ClickAction : DriverAction 7 | { 8 | private readonly ElementScope _elementScope; 9 | private readonly TimeSpan _waitBeforeClick; 10 | private readonly Waiter _waiter; 11 | 12 | internal ClickAction(ElementScope elementScope, 13 | IDriver driver, 14 | Options options, 15 | Waiter waiter) : base(driver, elementScope, options) 16 | { 17 | _waitBeforeClick = options.WaitBeforeClick; 18 | _elementScope = elementScope; 19 | _waiter = waiter; 20 | } 21 | 22 | public override void Act() 23 | { 24 | _waiter.Wait(_waitBeforeClick); 25 | Driver.Click(_elementScope); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Coypu/Actions/DblClickAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Timing; 3 | 4 | namespace Coypu.Actions 5 | { 6 | internal class DblClickAction : DriverAction 7 | { 8 | private readonly ElementScope _elementScope; 9 | private readonly TimeSpan _waitBeforeClick; 10 | private readonly Waiter _waiter; 11 | 12 | internal DblClickAction(ElementScope elementScope, 13 | IDriver driver, 14 | Options options, 15 | Waiter waiter) : base(driver, elementScope, options) 16 | { 17 | _waitBeforeClick = options.WaitBeforeClick; 18 | _elementScope = elementScope; 19 | _waiter = waiter; 20 | } 21 | 22 | public override void Act() 23 | { 24 | _waiter.Wait(_waitBeforeClick); 25 | Driver.DblClick(_elementScope); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Coypu/Actions/DriverAction.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Actions 2 | { 3 | public abstract class DriverAction : BrowserAction 4 | { 5 | protected readonly IDriver Driver; 6 | 7 | protected DriverAction(IDriver driver, DriverScope scope, Options options) 8 | : base(scope, options) 9 | { 10 | Driver = driver; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Coypu/Actions/FillIn.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Actions 2 | { 3 | internal class FillIn : DriverAction 4 | { 5 | private readonly string value; 6 | private readonly ElementScope element; 7 | 8 | internal FillIn(IDriver driver, ElementScope element, string value, Options options) : base(driver, element, options) 9 | { 10 | this.element = element; 11 | this.value = value; 12 | } 13 | 14 | private void BringIntoFocus() 15 | { 16 | Driver.Click(element); 17 | } 18 | 19 | internal void Set() 20 | { 21 | Driver.Set(element, value); 22 | } 23 | 24 | internal void Focus() 25 | { 26 | if (element["type"] != "file") 27 | BringIntoFocus(); 28 | } 29 | 30 | public override void Act() 31 | { 32 | Focus(); 33 | Set(); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/Coypu/Actions/Hover.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Actions 2 | { 3 | internal class Hover : DriverAction 4 | { 5 | private readonly DriverScope _driverScope; 6 | 7 | internal Hover(DriverScope driverScope, 8 | IDriver driver, 9 | Options options) : base(driver, driverScope, options) 10 | { 11 | _driverScope = driverScope; 12 | } 13 | 14 | public override void Act() 15 | { 16 | var element = _driverScope.Now(); 17 | Driver.Hover(element); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Coypu/Actions/LambdaDriverAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Coypu.Actions 4 | { 5 | public class LambdaBrowserAction : BrowserAction 6 | { 7 | private readonly Action action; 8 | 9 | public LambdaBrowserAction(Action action, Options options) 10 | : base(null, options) 11 | { 12 | this.action = action; 13 | } 14 | 15 | public override void Act() 16 | { 17 | action(); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Coypu/Actions/SendKeys.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Actions 2 | { 3 | internal class SendKeys : DriverAction 4 | { 5 | private readonly string keys; 6 | private readonly DriverScope driverScope; 7 | 8 | internal SendKeys(string keys, DriverScope driverScope, IDriver driver, Options options) 9 | : base(driver, driverScope, options) 10 | { 11 | this.keys = keys; 12 | this.driverScope = driverScope; 13 | } 14 | 15 | public override void Act() 16 | { 17 | var element = driverScope.Now(); 18 | Driver.SendKeys(element,keys); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Coypu/Actions/Uncheck.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Actions 2 | { 3 | internal class Uncheck : DriverAction 4 | { 5 | private readonly ElementScope element; 6 | 7 | internal Uncheck(IDriver driver, ElementScope element, Options options) 8 | : base(driver, element, options) 9 | { 10 | this.element = element; 11 | } 12 | 13 | public override void Act() 14 | { 15 | Driver.Uncheck(element); 16 | } 17 | 18 | } 19 | } -------------------------------------------------------------------------------- /src/Coypu/Actions/WaitThenClick.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Finders; 3 | using Coypu.Timing; 4 | 5 | namespace Coypu.Actions 6 | { 7 | internal class WaitThenClick : DriverAction 8 | { 9 | private readonly Waiter waiter; 10 | private readonly ElementFinder elementFinder; 11 | private readonly DisambiguationStrategy disambiguationStrategy; 12 | private readonly TimeSpan waitBeforeClick; 13 | 14 | internal WaitThenClick(IDriver driver, DriverScope scope, Options options, Waiter waiter, ElementFinder elementFinder, DisambiguationStrategy disambiguationStrategy) 15 | : base(driver, scope, options) 16 | { 17 | waitBeforeClick = options.WaitBeforeClick; 18 | this.waiter = waiter; 19 | this.elementFinder = elementFinder; 20 | this.disambiguationStrategy = disambiguationStrategy; 21 | } 22 | 23 | public override void Act() 24 | { 25 | var element = disambiguationStrategy.ResolveQuery(elementFinder); 26 | waiter.Wait(waitBeforeClick); 27 | Driver.Click(element); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Coypu/ActivatorDriverFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace Coypu 5 | { 6 | public class ActivatorDriverFactory : DriverFactory 7 | { 8 | public static int OpenDrivers { get; set; } 9 | 10 | public IDriver NewWebDriver(SessionConfiguration sessionConfiguration) 11 | { 12 | try 13 | { 14 | var driver = (IDriver)Activator.CreateInstance(sessionConfiguration.Driver, sessionConfiguration); 15 | OpenDrivers++; 16 | return driver; 17 | } 18 | catch (TargetInvocationException e) 19 | { 20 | throw e.InnerException; 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Coypu/AmbiguousException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Coypu 5 | { 6 | public class AmbiguousException : Exception 7 | { 8 | public AmbiguousException(string message) 9 | : base(message) 10 | { 11 | } 12 | 13 | public AmbiguousException(string message, Exception innerException) 14 | : base(message, innerException) 15 | { 16 | } 17 | 18 | public AmbiguousException(SerializationInfo info, StreamingContext context) 19 | : base(info, context) 20 | { 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Coypu/AsyncExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Coypu 4 | { 5 | /// 6 | /// Provides extension methods for working with asynchronous tasks. 7 | /// 8 | public static class AsyncExtensions 9 | { 10 | /// 11 | /// Waits for the task to complete and returns the result. 12 | /// 13 | /// The type of the task result. 14 | /// The task to wait for. 15 | /// The result of the completed task. 16 | public static T Sync(this Task task) 17 | { 18 | task.Wait(); 19 | return task.Result; 20 | } 21 | 22 | /// 23 | /// Waits for the task to complete. 24 | /// 25 | /// The task to wait for. 26 | public static void Sync(this Task task) 27 | { 28 | task.Wait(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Coypu/Cookies.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | 4 | namespace Coypu 5 | { 6 | public interface Cookies 7 | { 8 | public void AddCookie(Cookie cookie, Options options = null); 9 | public void DeleteAll(); 10 | public void DeleteCookie(Cookie cookie); 11 | public void DeleteCookieNamed(string cookieName); 12 | public IEnumerable GetAll(); 13 | public Cookie GetCookieNamed(string cookieName); 14 | public void WaitUntilCookieExists(Cookie cookie, Options options); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Coypu/DriverFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Coypu 4 | { 5 | public interface DriverFactory 6 | { 7 | IDriver NewWebDriver(SessionConfiguration sessionConfiguration); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Coypu/DriverProxy.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Coypu 4 | { 5 | /// 6 | /// Proxy information for a Driver 7 | /// 8 | public class DriverProxy 9 | { 10 | /// 11 | /// The Username for the proxy 12 | /// 13 | public string Username { get; set; } 14 | 15 | /// 16 | /// The Password for the proxy 17 | /// 18 | public string Password { get; set; } 19 | 20 | /// 21 | /// The Server of the proxy 22 | /// 23 | public string Server { get; set; } 24 | 25 | /// 26 | /// Use proxy for SSL 27 | /// 28 | public bool Ssl { get; set; } = true; 29 | 30 | /// 31 | /// Use type of proxy 32 | /// 33 | public DriverProxyType Type { get; set; } = DriverProxyType.Http; 34 | 35 | /// 36 | /// Domains to bypass 37 | /// 38 | public IEnumerable BypassAddresses { get; set; } 39 | } 40 | } -------------------------------------------------------------------------------- /src/Coypu/DriverProxyType.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu 2 | { 3 | public enum DriverProxyType 4 | { 5 | Socks, 6 | Http 7 | } 8 | } -------------------------------------------------------------------------------- /src/Coypu/Drivers/Browser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | #pragma warning disable 1591 7 | 8 | namespace Coypu.Drivers 9 | { 10 | /// 11 | /// The browser that will be used by your chosen driver 12 | /// 13 | public class Browser 14 | { 15 | public static Browser Firefox = new Browser 16 | { 17 | Javascript = true, 18 | UppercaseTagNames = true 19 | }; 20 | 21 | public static Browser InternetExplorer = new Browser {Javascript = true, Name = "InternetExplorer"}; 22 | public static Browser Chrome = new Browser {Javascript = true, Name = "Chrome"}; 23 | public static Browser Chromium = new Browser {Javascript = true, Name = "Chromium"}; 24 | public static Browser Edge = new Browser {Javascript = true, Name = "Edge"}; 25 | public static Browser Opera = new Browser {Javascript = true, Name = "Opera"}; 26 | public static Browser Safari = new Browser {Javascript = true, Name = "Safari"}; 27 | public static Browser Webkit = new Browser {Javascript = true, Name = "Webkit"}; 28 | private Browser() { } 29 | 30 | public bool Javascript { get; private set; } 31 | 32 | public string Name { get; private set; } 33 | public bool UppercaseTagNames { get; private set; } 34 | 35 | public static Browser Parse(string browserName) 36 | { 37 | var fieldInfo = BrowserFields() 38 | .FirstOrDefault(f => f.Name.Equals(browserName.Replace(" ", ""), 39 | StringComparison.InvariantCultureIgnoreCase)); 40 | if (fieldInfo == null) 41 | throw new NoSuchBrowserException(browserName); 42 | return (Browser) fieldInfo.GetValue(null); 43 | } 44 | 45 | public override string ToString() => Name; 46 | 47 | private static IEnumerable BrowserFields() 48 | { 49 | return typeof(Browser).GetFields(BindingFlags.Public | BindingFlags.Static); 50 | } 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Coypu/Drivers/BrowserNotSupportedException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #pragma warning disable 1591 3 | 4 | namespace Coypu.Drivers 5 | { 6 | /// 7 | /// Thrown when your chosen browser is not supported by your chosen driver 8 | /// 9 | public class BrowserNotSupportedException : Exception 10 | { 11 | public BrowserNotSupportedException(Browser browser, 12 | Type driverType) 13 | : this(browser, driverType, null) { } 14 | 15 | public BrowserNotSupportedException(Browser browser, 16 | Type driverType, 17 | Exception inner) 18 | : base($"{browser} is not supported by {driverType.Name}", inner) { } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Coypu/Drivers/NoSuchBrowserException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Coypu.Drivers 4 | { 5 | /// 6 | /// There is no such browser 7 | /// 8 | public class NoSuchBrowserException : Exception 9 | { 10 | internal NoSuchBrowserException(string browserName) : base($"No such browser: {browserName}") 11 | { 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Coypu/Drivers/Playwright/Dialogs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Playwright; 3 | 4 | namespace Coypu.Drivers.Playwright 5 | { 6 | internal class Dialogs 7 | { 8 | internal void ActOnDialog(string text, IPage page, Action trigger, string dialogType, Action dialogAction) 9 | { 10 | var dialogFound = false; 11 | var match = false; 12 | EventHandler listener = (_, dialog) => 13 | { 14 | dialogFound = true; 15 | if (dialog.Type == dialogType && 16 | (text == null || dialog.Message == text)) 17 | { 18 | match = true; 19 | dialogAction(dialog); 20 | } 21 | else 22 | { 23 | dialog.DismissAsync(); 24 | } 25 | }; 26 | page.Dialog += listener; 27 | try 28 | { 29 | trigger.Invoke(); 30 | } 31 | finally 32 | { 33 | page.Dialog -= listener; // Only applies for while the trigger action is running 34 | } 35 | if (!dialogFound) 36 | { 37 | throw new MissingDialogException("No dialog was present to accept"); 38 | } 39 | if(!match) 40 | { 41 | throw new MissingDialogException("A dialog was present but didn't match the expected text or type."); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Coypu/Drivers/Playwright/FrameFinder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.Playwright; 4 | 5 | namespace Coypu.Drivers.Playwright 6 | { 7 | internal class FrameFinder 8 | { 9 | private readonly IPage _page; 10 | private readonly XPath _xPath; 11 | 12 | public FrameFinder(IPage page) 13 | { 14 | _page = page; 15 | _xPath = new XPath(); 16 | } 17 | 18 | public IEnumerable FindFrame(string locator, 19 | IEnumerable frameElements, 20 | Options options) 21 | { 22 | return PlaywrightFrames(locator, frameElements, options); 23 | } 24 | 25 | private IEnumerable PlaywrightFrames(string locator, 26 | IEnumerable frameElements, 27 | Options options) 28 | { 29 | var allPlaywrightFrames = frameElements.Select(f => new PlaywrightFrame(f)); 30 | return allPlaywrightFrames.Where(f => { 31 | var t = f.Title; 32 | return f.Id == locator || 33 | f.Name == locator || 34 | (options.TextPrecisionExact 35 | ? f.Title == locator 36 | : f.Title 37 | .Contains(locator)) || 38 | FrameContentsMatch(f, locator, options); 39 | }); 40 | } 41 | 42 | private bool FrameContentsMatch(PlaywrightFrame frame, 43 | string locator, 44 | Options options) 45 | { 46 | return frame.Title == locator || 47 | 48 | ((IFrame) frame.Native).QuerySelectorAllAsync( 49 | $"xpath=.//h1[{_xPath.IsText(locator, options)}]" 50 | ).Sync().Any(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Coypu/Drivers/Playwright/PlaywrightElement.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Runtime.Serialization; 3 | using System.Threading.Tasks; 4 | using Microsoft.Playwright; 5 | 6 | namespace Coypu.Drivers.Playwright 7 | { 8 | internal class PlaywrightElement : Element 9 | { 10 | // ReSharper disable once InconsistentNaming 11 | protected readonly IElementHandle _native; 12 | 13 | public PlaywrightElement(IElementHandle PlaywrightElement) 14 | { 15 | _native = PlaywrightElement; 16 | } 17 | 18 | private string GetAttribute(string attributeName) 19 | { 20 | return _native.GetAttributeAsync(attributeName).Sync(); 21 | } 22 | 23 | public string Id => GetAttribute("id"); 24 | 25 | public virtual string Text => _native.InnerTextAsync().Sync(); 26 | 27 | public string Value { 28 | get { 29 | var inputTags = new[] { "input", "textarea", "select" }; 30 | if (inputTags.Contains(TagName.ToLower())) 31 | return _native.InputValueAsync().Sync(); 32 | 33 | return this["value"]; 34 | } 35 | } 36 | 37 | public string Name => GetAttribute("name"); 38 | 39 | public string TagName => _native.EvaluateAsync("e => e.tagName").Sync()?.GetString(); 40 | 41 | public virtual string OuterHTML => _native.EvaluateAsync("el => el.outerHTML").Sync().ToString(); 42 | 43 | public virtual string InnerHTML => _native.InnerHTMLAsync().Sync(); 44 | 45 | public string Title => GetAttribute("title"); 46 | 47 | public bool Disabled => !_native.IsEnabledAsync().Sync(); 48 | 49 | public string SelectedOption 50 | { 51 | get 52 | { 53 | return _native.EvaluateAsync("sel => sel.options[sel.options.selectedIndex].innerText").Sync().ToString(); 54 | } 55 | } 56 | 57 | public bool Selected { 58 | get { 59 | return _native.IsCheckedAsync().Sync(); 60 | } 61 | } 62 | 63 | public virtual object Native => _native; 64 | 65 | public string this[string attributeName] => GetAttribute(attributeName); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Coypu/Drivers/Playwright/PlaywrightFrame.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Playwright; 2 | 3 | namespace Coypu.Drivers.Playwright 4 | { 5 | internal class PlaywrightFrame : Element 6 | { 7 | 8 | private readonly IFrame _frame; 9 | private readonly IElementHandle _frameElement; 10 | private string _id; 11 | 12 | public PlaywrightFrame(IElementHandle frameElement) 13 | { 14 | _frameElement = frameElement; 15 | _frame = frameElement.ContentFrameAsync().Sync(); 16 | } 17 | 18 | public string this[string attributeName] => GetAttribute(attributeName); 19 | 20 | private string GetAttribute(string attributeName) 21 | { 22 | return _frameElement.GetAttributeAsync(attributeName).Sync(); 23 | } 24 | 25 | public string Text => FindBody().Text; 26 | 27 | public string OuterHTML => FindBody().OuterHTML; 28 | 29 | public string InnerHTML => FindBody().InnerHTML; 30 | 31 | public object Native 32 | { 33 | get 34 | { 35 | return _frame; 36 | } 37 | } 38 | 39 | public string Id { 40 | get { 41 | return this["id"]; 42 | } 43 | } 44 | 45 | public string Value => throw new System.NotImplementedException(); 46 | 47 | public string Name => _frame.Name; 48 | 49 | public string SelectedOption => throw new System.NotImplementedException(); 50 | 51 | public bool Selected => throw new System.NotImplementedException(); 52 | 53 | public string Title => _frame.TitleAsync().Sync(); 54 | 55 | public bool Disabled => throw new System.NotImplementedException(); 56 | 57 | private PlaywrightElement FindBody() 58 | { 59 | return new PlaywrightElement(_frame.QuerySelectorAsync("body").Sync()); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Coypu/Drivers/Playwright/PlaywrightWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using OpenQA.Selenium; 3 | using Microsoft.Playwright; 4 | using System.Linq; 5 | 6 | namespace Coypu.Drivers.Playwright 7 | { 8 | internal class PlaywrightWindow : Element 9 | { 10 | private readonly IPage _page; 11 | 12 | public PlaywrightWindow(IPage page) 13 | { 14 | _page = page; 15 | } 16 | 17 | public string Id => throw new NotSupportedException(); 18 | 19 | public string Text => ((IPage) Native).InnerTextAsync("xpath=/html/body").Sync(); 20 | 21 | public string InnerHTML => ((IPage) Native).InnerHTMLAsync("xpath=./*").Sync().ToString(); 22 | 23 | public string Title => _page.TitleAsync().Sync(); 24 | 25 | public bool Disabled => throw new NotSupportedException(); 26 | 27 | public string OuterHTML => ((IPage) Native).EvalOnSelectorAsync("html", "h => h.outerHTML").Sync().ToString(); 28 | 29 | public string Value => throw new NotSupportedException(); 30 | 31 | public string Name => throw new NotSupportedException(); 32 | 33 | public string SelectedOption => throw new NotSupportedException(); 34 | 35 | public bool Selected => throw new NotSupportedException(); 36 | 37 | public object Native 38 | { 39 | get 40 | { 41 | return _page; 42 | } 43 | } 44 | 45 | public string this[string attributeName] => throw new NotSupportedException(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Coypu/Drivers/Selenium/Dialogs.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Coypu.Drivers.Selenium 4 | { 5 | internal class Dialogs 6 | { 7 | private readonly IWebDriver _selenium; 8 | 9 | public Dialogs(IWebDriver selenium) 10 | { 11 | _selenium = selenium; 12 | } 13 | 14 | public bool HasDialog(string withText) 15 | { 16 | if (!HasAnyDialog() ) return false; 17 | 18 | var alert = _selenium.SwitchTo().Alert(); 19 | 20 | return alert.Text == withText; 21 | } 22 | 23 | public bool HasAnyDialog() 24 | { 25 | try 26 | { 27 | return _selenium.SwitchTo() != null && 28 | _selenium.SwitchTo() 29 | .Alert() != null; 30 | } 31 | catch (NoAlertPresentException) 32 | { 33 | return false; 34 | } 35 | } 36 | 37 | public void AcceptModalDialog(string prompt = null) 38 | { 39 | try 40 | { 41 | var alert = _selenium.SwitchTo() 42 | .Alert(); 43 | if (prompt != null) { 44 | alert.SendKeys(prompt); 45 | } 46 | alert.Accept(); 47 | } 48 | catch (NoAlertPresentException ex) 49 | { 50 | throw new MissingDialogException("No dialog was present to accept", ex); 51 | } 52 | } 53 | 54 | public void CancelModalDialog() 55 | { 56 | try 57 | { 58 | _selenium.SwitchTo() 59 | .Alert() 60 | .Dismiss(); 61 | } 62 | catch (NoAlertPresentException ex) 63 | { 64 | throw new MissingDialogException("No dialog was present to accept", ex); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Coypu/Drivers/Selenium/ElementFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using OpenQA.Selenium; 5 | 6 | namespace Coypu.Drivers.Selenium 7 | { 8 | internal class ElementFinder 9 | { 10 | public IEnumerable FindAll(By by, 11 | Scope scope, 12 | Options options, 13 | Func predicate = null) 14 | { 15 | try 16 | { 17 | return SeleniumScope(scope) 18 | .FindElements(by) 19 | .Where(e => Matches(predicate, e) && IsDisplayed(e, options)); 20 | } 21 | catch (StaleElementReferenceException e) 22 | { 23 | throw new StaleElementException(e); 24 | } 25 | } 26 | 27 | public ISearchContext SeleniumScope(Scope scope) 28 | { 29 | return (ISearchContext) scope.Now() 30 | .Native; 31 | } 32 | 33 | private static bool Matches(Func predicate, 34 | IWebElement element) 35 | { 36 | return predicate == null || predicate(element); 37 | } 38 | 39 | public bool IsDisplayed(IWebElement e, 40 | Options options) 41 | { 42 | return options.ConsiderInvisibleElements || e.IsDisplayed(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Coypu/Drivers/Selenium/ImageFormatParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using OpenQA.Selenium; 4 | 5 | namespace Coypu.Drivers.Selenium 6 | { 7 | internal class ImageFormatParser 8 | { 9 | internal static ScreenshotImageFormat GetImageFormat(string fileName) 10 | { 11 | var extension = new FileInfo(fileName).Extension.ToLower() 12 | .Replace("jpg", "jpeg"); 13 | 14 | extension = extension.Replace(".", ""); 15 | 16 | ScreenshotImageFormat format; 17 | if (AreEqual(extension, ScreenshotImageFormat.Bmp)) 18 | format = ScreenshotImageFormat.Bmp; 19 | else if (AreEqual(extension, ScreenshotImageFormat.Gif)) 20 | format = ScreenshotImageFormat.Gif; 21 | else if (AreEqual(extension, ScreenshotImageFormat.Jpeg)) 22 | format = ScreenshotImageFormat.Jpeg; 23 | else 24 | format = ScreenshotImageFormat.Png; 25 | 26 | return format; 27 | } 28 | 29 | private static bool AreEqual(string extension, 30 | ScreenshotImageFormat imageFormat) 31 | { 32 | return extension.Equals(imageFormat.ToString(), StringComparison.InvariantCultureIgnoreCase); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Coypu/Drivers/Selenium/MouseControl.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Coypu.Drivers.Selenium 4 | { 5 | internal class MouseControl 6 | { 7 | private readonly IWebDriver _selenium; 8 | 9 | public MouseControl(IWebDriver selenium) 10 | { 11 | _selenium = selenium; 12 | } 13 | 14 | public void Hover(Element element) 15 | { 16 | var sequenceBuilder = new OpenQA.Selenium.Interactions.Actions(_selenium); 17 | var actionSequenceBuilder = sequenceBuilder.MoveToElement((IWebElement) element.Native); 18 | var action = actionSequenceBuilder.Build(); 19 | action.Perform(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Coypu/Drivers/Selenium/SeleniumElement.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using OpenQA.Selenium; 3 | 4 | namespace Coypu.Drivers.Selenium 5 | { 6 | internal class SeleniumElement : Element 7 | { 8 | // ReSharper disable once InconsistentNaming 9 | protected readonly IWebElement _native; 10 | protected readonly IWebDriver Selenium; 11 | 12 | public SeleniumElement(IWebElement seleniumElement, 13 | IWebDriver selenium) 14 | { 15 | _native = seleniumElement; 16 | Selenium = selenium; 17 | } 18 | 19 | public string Id => _native.GetAttribute("id"); 20 | 21 | public virtual string Text => _native.Text; 22 | 23 | public string Value => _native.GetAttribute("value"); 24 | 25 | public string Name => _native.GetAttribute("name"); 26 | 27 | public virtual string OuterHTML => _native.GetAttribute("outerHTML"); 28 | 29 | public virtual string InnerHTML => _native.GetAttribute("innerHTML"); 30 | 31 | public string Title => _native.GetAttribute("title"); 32 | 33 | public bool Disabled => !_native.Enabled; 34 | 35 | public string SelectedOption 36 | { 37 | get 38 | { 39 | return _native.FindElements(By.TagName("option")) 40 | .Where(e => e.Selected) 41 | .Select(e => e.Text) 42 | .FirstOrDefault(); 43 | } 44 | } 45 | 46 | public bool Selected => _native.Selected; 47 | 48 | public virtual object Native => _native; 49 | 50 | public string this[string attributeName] => _native.GetAttribute(attributeName); 51 | } 52 | } -------------------------------------------------------------------------------- /src/Coypu/Drivers/Selenium/SeleniumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using OpenQA.Selenium; 5 | 6 | namespace Coypu.Drivers.Selenium 7 | { 8 | internal static class SeleniumExtensions 9 | { 10 | internal static bool IsDisplayed(this IWebElement webElement) 11 | { 12 | return webElement.Displayed; 13 | } 14 | 15 | internal static IWebElement FirstDisplayedOrDefault(this IEnumerable elements) 16 | { 17 | return elements.FirstOrDefault(IsDisplayed); 18 | } 19 | 20 | internal static bool AnyDisplayed(this IEnumerable elements) 21 | { 22 | return elements.Any(IsDisplayed); 23 | } 24 | 25 | internal static bool AnyDisplayed(this IEnumerable elements, 26 | Func predicate) 27 | { 28 | return elements.Any(e => predicate(e) && IsDisplayed(e)); 29 | } 30 | 31 | internal static IWebElement FirstDisplayedOrDefault(this IEnumerable elements, 32 | Func predicate) 33 | { 34 | return elements.FirstOrDefault(e => predicate(e) && IsDisplayed(e)); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Coypu/Drivers/Selenium/SeleniumFrame.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Coypu.Drivers.Selenium 4 | { 5 | internal class SeleniumFrame : SeleniumElement 6 | { 7 | private readonly SeleniumWindowManager _seleniumWindowManager; 8 | 9 | public SeleniumFrame(IWebElement seleniumElement, 10 | IWebDriver selenium, 11 | SeleniumWindowManager seleniumWindowManager) 12 | : base(seleniumElement, selenium) 13 | { 14 | _seleniumWindowManager = seleniumWindowManager; 15 | } 16 | 17 | public override string Text => FindBody() 18 | .Text; 19 | 20 | public override string OuterHTML => FindBody() 21 | .GetAttribute("outerHTML"); 22 | 23 | public override string InnerHTML => FindBody() 24 | .GetAttribute("innerHTML"); 25 | 26 | public override object Native 27 | { 28 | get 29 | { 30 | _seleniumWindowManager.SwitchToFrame(_native); 31 | return Selenium; 32 | } 33 | } 34 | 35 | private IWebElement FindBody() 36 | { 37 | return ((IWebDriver) Native).FindElement(By.CssSelector("body")); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Coypu/Drivers/Selenium/SeleniumWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using OpenQA.Selenium; 3 | 4 | namespace Coypu.Drivers.Selenium 5 | { 6 | internal class SeleniumWindow : Element 7 | { 8 | private readonly SeleniumWindowManager _seleniumWindowManager; 9 | private readonly IWebDriver _webDriver; 10 | private readonly string _windowHandle; 11 | 12 | public SeleniumWindow(IWebDriver webDriver, 13 | string windowHandle, 14 | SeleniumWindowManager seleniumWindowManager) 15 | { 16 | _webDriver = webDriver; 17 | _windowHandle = windowHandle; 18 | _seleniumWindowManager = seleniumWindowManager; 19 | } 20 | 21 | public string Id => throw new NotSupportedException(); 22 | 23 | public string Text => ((ISearchContext) Native).FindElement(By.CssSelector("body")) 24 | .Text; 25 | 26 | public string InnerHTML => ((ISearchContext) Native).FindElement(By.XPath("./*")) 27 | .GetAttribute("innerHTML"); 28 | 29 | public string Title => _webDriver.Title; 30 | 31 | public bool Disabled => throw new NotSupportedException(); 32 | 33 | public string OuterHTML => ((ISearchContext) Native).FindElement(By.XPath("./*")) 34 | .GetAttribute("outerHTML"); 35 | 36 | public string Value => throw new NotSupportedException(); 37 | 38 | public string Name => throw new NotSupportedException(); 39 | 40 | public string SelectedOption => throw new NotSupportedException(); 41 | 42 | public bool Selected => throw new NotSupportedException(); 43 | 44 | public object Native 45 | { 46 | get 47 | { 48 | SwitchTo(_windowHandle); 49 | return _webDriver; 50 | } 51 | } 52 | 53 | public string this[string attributeName] => throw new NotSupportedException(); 54 | 55 | private void SwitchTo(string windowName) 56 | { 57 | _seleniumWindowManager.SwitchToWindow(windowName); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/Coypu/Drivers/Selenium/SeleniumWindowManager.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | using OpenQA.Selenium.Firefox; 3 | 4 | namespace Coypu.Drivers.Selenium 5 | { 6 | internal class SeleniumWindowManager 7 | { 8 | private readonly IWebDriver _webDriver; 9 | private IWebDriver _switchedToFrame; 10 | private IWebElement _switchedToFrameElement; 11 | 12 | public SeleniumWindowManager(IWebDriver webDriver) 13 | { 14 | _webDriver = webDriver; 15 | } 16 | 17 | public bool SwitchedToAFrame => _switchedToFrame != null; 18 | 19 | public string LastKnownWindowHandle { get; private set; } 20 | 21 | public IWebDriver SwitchToFrame(IWebElement webElement) 22 | { 23 | if (Equals(_switchedToFrameElement, webElement)) 24 | return _switchedToFrame; 25 | 26 | var frame = _webDriver.SwitchTo() 27 | .Frame(webElement); 28 | 29 | _switchedToFrameElement = webElement; 30 | _switchedToFrame = frame; 31 | 32 | return frame; 33 | } 34 | 35 | public void SwitchToWindow(string windowName) 36 | { 37 | if (LastKnownWindowHandle != windowName || SwitchedToAFrame) 38 | { 39 | _webDriver.SwitchTo() 40 | .Window(windowName); 41 | 42 | // Fix for https://bugzilla.mozilla.org/show_bug.cgi?id=1305822 43 | if (_webDriver is FirefoxDriver) 44 | { 45 | _webDriver.SwitchTo() 46 | .DefaultContent(); 47 | } 48 | 49 | LastKnownWindowHandle = windowName; 50 | } 51 | 52 | _switchedToFrame = null; 53 | _switchedToFrameElement = null; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Coypu/Drivers/Selenium/TextMatcher.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using OpenQA.Selenium; 3 | 4 | namespace Coypu.Drivers.Selenium 5 | { 6 | internal class TextMatcher 7 | { 8 | public bool TextMatches(IWebElement e, 9 | Regex pattern) 10 | { 11 | return e.Text != null && pattern.IsMatch(e.Text.Trim()); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Coypu/Element.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu 2 | { 3 | /// 4 | /// An HTML element 5 | /// 6 | public interface Element { 7 | 8 | /// 9 | /// The value of the 'id' attribute 10 | /// 11 | string Id { get; } 12 | /// 13 | /// The inner text of the element 14 | /// 15 | string Text { get; } 16 | /// 17 | /// The value of the 'value' attribute 18 | /// 19 | string Value { get; } 20 | /// 21 | /// The value of the 'name' attribute 22 | /// 23 | string Name { get; } 24 | /// 25 | /// The selected option - applies to select elements only 26 | /// 27 | string SelectedOption { get; } 28 | /// 29 | /// Whether the element is selected 30 | /// 31 | bool Selected { get; } 32 | 33 | /// 34 | /// The native element returned by your chosen driver 35 | /// 36 | object Native { get; } 37 | 38 | /// 39 | /// The attributes of the HTML element 40 | /// 41 | string this[string attributeName] { get; } 42 | 43 | /// 44 | /// The outer HTML of the element 45 | /// 46 | string OuterHTML { get; } 47 | 48 | /// 49 | /// The inner HTML of the element 50 | /// 51 | string InnerHTML { get; } 52 | 53 | /// 54 | /// The title of the element 55 | /// 56 | string Title { get; } 57 | 58 | /// 59 | /// Whether the element is disabled 60 | /// 61 | bool Disabled { get; } 62 | } 63 | } -------------------------------------------------------------------------------- /src/Coypu/ElementFoundExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Coypu 5 | { 6 | internal static class ElementFoundExtensions 7 | { 8 | public static IEnumerable AsSnapshotElementScopes(this IEnumerable elements, DriverScope driverScope, Options options) 9 | { 10 | return elements.Select(elementFound => new SnapshotElementScope(elementFound, driverScope, options)); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Coypu/FillInWith.cs: -------------------------------------------------------------------------------- 1 | using Coypu.Actions; 2 | using Coypu.Timing; 3 | 4 | namespace Coypu 5 | { 6 | public class FillInWith 7 | { 8 | private readonly IDriver driver; 9 | private readonly TimingStrategy timingStrategy; 10 | private readonly Options options; 11 | private readonly ElementScope element; 12 | 13 | 14 | internal FillInWith(ElementScope element, IDriver driver, TimingStrategy timingStrategy, Options options) 15 | { 16 | this.element = element; 17 | this.driver = driver; 18 | this.timingStrategy = timingStrategy; 19 | this.options = options; 20 | } 21 | 22 | /// 23 | /// Supply a value for the text field 24 | /// 25 | /// The value to fill in 26 | /// Thrown if the element cannot be found 27 | public void With(string value) 28 | { 29 | timingStrategy.Synchronise(new FillIn(driver, element, value, options)); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Coypu/FinderException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Coypu 5 | { 6 | public class FinderException : Exception 7 | { 8 | public FinderException(string message) : base(message) 9 | { 10 | } 11 | 12 | public FinderException(string message, Exception innerException) : base(message, innerException) 13 | { 14 | } 15 | 16 | public FinderException(SerializationInfo info, StreamingContext context) 17 | : base(info, context) 18 | { 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/ButtonFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Drivers; 3 | 4 | namespace Coypu.Finders 5 | { 6 | internal class ButtonFinder : XPathQueryFinder 7 | { 8 | internal ButtonFinder(IDriver driver, string locator, DriverScope scope, Options options) : base(driver, locator, scope, options) { } 9 | 10 | public override bool SupportsSubstringTextMatching => true; 11 | 12 | protected override Func GetQuery(Html html) 13 | { 14 | return html.Button; 15 | } 16 | 17 | internal override string QueryDescription => "button: " + Locator; 18 | } 19 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/CssFinder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace Coypu.Finders 5 | { 6 | internal class CssFinder : WithTextFinder 7 | { 8 | protected override string SelectorType => "css"; 9 | 10 | public CssFinder(IDriver driver, string locator, DriverScope scope, Options options) 11 | : base(driver, locator, scope, options) 12 | { 13 | } 14 | 15 | public CssFinder(IDriver driver, string locator, DriverScope scope, Options options, Regex textPattern) 16 | : base(driver, locator, scope, options, textPattern) 17 | { 18 | } 19 | 20 | public CssFinder(IDriver driver, string locator, DriverScope scope, Options options, string text) 21 | : base(driver, locator, scope, options, text) 22 | { 23 | } 24 | 25 | public override bool SupportsSubstringTextMatching => true; 26 | 27 | internal override IEnumerable Find(Options options) 28 | { 29 | return Driver.FindAllCss(Locator, Scope, options, TextPattern(options.TextPrecision == TextPrecision.Exact)); 30 | } 31 | 32 | public Regex TextPattern(bool exact) 33 | { 34 | return text != null ? TextAsRegex(text, exact) : textPattern; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/DisambiguationStrategy.cs: -------------------------------------------------------------------------------- 1 | using Coypu; 2 | using Coypu.Finders; 3 | 4 | public interface DisambiguationStrategy 5 | { 6 | Element ResolveQuery(ElementFinder elementFinder); 7 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/DocumentElementFinder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Coypu.Finders 4 | { 5 | internal class DocumentElementFinder : ElementFinder 6 | { 7 | private Element window; 8 | 9 | public DocumentElementFinder(IDriver driver, Options options) : base(driver, "Window", null, options) 10 | { 11 | } 12 | 13 | public override bool SupportsSubstringTextMatching => false; 14 | 15 | internal override IEnumerable Find(Options options) 16 | { 17 | return new[] { window ??= Driver.Window }; 18 | } 19 | 20 | internal override string QueryDescription => "Document Element"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Coypu/Finders/ElementFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Coypu.Finders 6 | { 7 | public abstract class ElementFinder 8 | { 9 | protected internal readonly IDriver Driver; 10 | protected readonly DriverScope Scope; 11 | 12 | protected ElementFinder(IDriver driver, string locator, DriverScope scope, Options options) 13 | { 14 | Driver = driver; 15 | Locator = locator; 16 | Scope = scope; 17 | Options = options; 18 | } 19 | 20 | public Options Options { get; } 21 | 22 | public abstract bool SupportsSubstringTextMatching { get; } 23 | 24 | internal string Locator { get; } 25 | 26 | internal abstract IEnumerable Find(Options options); 27 | 28 | internal abstract string QueryDescription { get; } 29 | 30 | protected internal virtual Exception GetMissingException() 31 | { 32 | return new MissingHtmlException("Unable to find " + QueryDescription); 33 | } 34 | 35 | internal ElementScope AsScope() 36 | { 37 | return new SynchronisedElementScope(this, Scope, Options); 38 | } 39 | 40 | public override string ToString() 41 | { 42 | return QueryDescription; 43 | } 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/FieldFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Drivers; 3 | 4 | namespace Coypu.Finders 5 | { 6 | internal class FieldFinder : XPathQueryFinder 7 | { 8 | internal FieldFinder(IDriver driver, string locator, DriverScope scope, Options options) : base(driver, locator, scope, options) { } 9 | 10 | public override bool SupportsSubstringTextMatching => true; 11 | 12 | protected override Func GetQuery(Html html) 13 | { 14 | return html.Field; 15 | } 16 | 17 | internal override string QueryDescription => "field: " + Locator; 18 | } 19 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/FieldsetFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Coypu.Drivers; 4 | 5 | namespace Coypu.Finders 6 | { 7 | internal class FieldsetFinder : XPathQueryFinder 8 | { 9 | internal FieldsetFinder(IDriver driver, string locator, DriverScope scope, Options options) : base(driver, locator, scope, options) { } 10 | 11 | public override bool SupportsSubstringTextMatching => true; 12 | 13 | internal override string QueryDescription => "fieldset: " + Locator; 14 | 15 | protected override Func GetQuery(Html html) 16 | { 17 | return html.Fieldset; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/FinderOptionsDisambiguationStrategy.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace Coypu.Finders 4 | { 5 | internal class FinderOptionsDisambiguationStrategy : DisambiguationStrategy 6 | { 7 | public Element ResolveQuery(ElementFinder elementFinder) 8 | { 9 | Element[] results; 10 | 11 | if (elementFinder.Options.TextPrecision == TextPrecision.PreferExact) 12 | results = PreferExact(elementFinder); 13 | else 14 | results = Find(elementFinder); 15 | 16 | if (elementFinder.Options.Match == Match.Single && results.Length > 1) 17 | throw new AmbiguousException(elementFinder.Options.BuildAmbiguousMessage(elementFinder.QueryDescription, results.Length)); 18 | 19 | if (!results.Any()) 20 | throw elementFinder.GetMissingException(); 21 | 22 | return results.First(); 23 | } 24 | 25 | private static Element[] PreferExact(ElementFinder elementFinder) 26 | { 27 | var results = Find(elementFinder, Options.Exact); 28 | if (results.Any() || !elementFinder.SupportsSubstringTextMatching) 29 | return results; 30 | 31 | return Find(elementFinder, Options.Substring); 32 | } 33 | 34 | private static Element[] Find(ElementFinder elementFinder, Options preferredOptions = null) 35 | { 36 | return elementFinder.Find(Options.Merge(preferredOptions, elementFinder.Options)).ToArray(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Coypu/Finders/FrameFinder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Coypu.Finders 4 | { 5 | internal class FrameFinder : ElementFinder 6 | { 7 | internal FrameFinder(IDriver driver, string locator, DriverScope scope, Options options) : base(driver, locator, scope, options) { } 8 | 9 | public override bool SupportsSubstringTextMatching => true; 10 | 11 | internal override IEnumerable Find(Options options) 12 | { 13 | return Driver.FindFrames(Locator, Scope, options); 14 | } 15 | 16 | internal override string QueryDescription => "frame: " + Locator; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Coypu/Finders/IdFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using Coypu.Drivers; 5 | 6 | namespace Coypu.Finders 7 | { 8 | internal class IdFinder : XPathQueryFinder 9 | { 10 | internal IdFinder(IDriver driver, string locator, DriverScope scope, Options options) : base(driver, locator, scope, options) { } 11 | 12 | public override bool SupportsSubstringTextMatching => false; 13 | 14 | protected override Func GetQuery(Html html) 15 | { 16 | return html.Id; 17 | } 18 | 19 | internal override string QueryDescription => "id: " + Locator; 20 | } 21 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/LinkFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Coypu.Drivers; 4 | 5 | namespace Coypu.Finders 6 | { 7 | internal class LinkFinder : XPathQueryFinder 8 | { 9 | internal LinkFinder(IDriver driver, string locator, DriverScope scope, Options options) : base(driver, locator, scope, options) { } 10 | 11 | 12 | public override bool SupportsSubstringTextMatching => true; 13 | 14 | protected override Func GetQuery(Html html) 15 | { 16 | return html.Link; 17 | } 18 | 19 | internal override string QueryDescription => "link: " + Locator; 20 | } 21 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/OptionFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Drivers; 3 | 4 | namespace Coypu.Finders 5 | { 6 | internal class OptionFinder : XPathQueryFinder 7 | { 8 | internal OptionFinder(IDriver driver, string locator, DriverScope scope, Options options) : base(driver, locator, scope, Options.Merge(Options.Invisible, options)) { } 9 | 10 | public override bool SupportsSubstringTextMatching => true; 11 | 12 | protected override Func GetQuery(Html html) 13 | { 14 | return html.Option; 15 | } 16 | 17 | internal override string QueryDescription => "option: " + Locator; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Coypu/Finders/SectionFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Drivers; 3 | 4 | namespace Coypu.Finders 5 | { 6 | internal class SectionFinder : XPathQueryFinder 7 | { 8 | internal SectionFinder(IDriver driver, string locator, DriverScope scope, Options options) : base(driver, locator, scope, options) { } 9 | 10 | public override bool SupportsSubstringTextMatching => true; 11 | 12 | internal override string QueryDescription => "section: " + Locator; 13 | 14 | protected override Func GetQuery(Html html) 15 | { 16 | return html.Section; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/SelectFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Drivers; 3 | 4 | namespace Coypu.Finders 5 | { 6 | internal class SelectFinder : XPathQueryFinder 7 | { 8 | internal SelectFinder(IDriver driver, string locator, DriverScope scope, Options options) : base(driver, locator, scope, options) { } 9 | 10 | public override bool SupportsSubstringTextMatching => true; 11 | 12 | protected override Func GetQuery(Html html) 13 | { 14 | return html.Select; 15 | } 16 | 17 | internal override string QueryDescription => "select: " + Locator; 18 | } 19 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/StateFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Coypu.Queries; 4 | using Coypu.Timing; 5 | 6 | namespace Coypu.Finders 7 | { 8 | internal class StateFinder 9 | { 10 | private readonly TimingStrategy timingStrategy; 11 | 12 | public StateFinder(TimingStrategy timingStrategy) 13 | { 14 | this.timingStrategy = timingStrategy; 15 | } 16 | 17 | internal State FindState(State[] states, Scope scope, Options options) 18 | { 19 | var query = new LambdaPredicateQuery(() => 20 | { 21 | var was = timingStrategy.ZeroTimeout; 22 | timingStrategy.ZeroTimeout = true; 23 | try 24 | { 25 | return ((Func)(() => states.Any(s => s.CheckCondition())))(); 26 | } 27 | finally 28 | { 29 | timingStrategy.ZeroTimeout = was; 30 | } 31 | }, options); 32 | 33 | var foundState = timingStrategy.Synchronise(query); 34 | 35 | if (!foundState) 36 | throw new MissingHtmlException("None of the given states was reached within the configured timeout."); 37 | 38 | return states.First(e => e.ConditionWasMet); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/WindowFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Coypu.Finders 5 | { 6 | internal class WindowFinder : ElementFinder 7 | { 8 | internal WindowFinder(IDriver driver, string locator, DriverScope scope, Options options) : base(driver, locator, scope, options) { } 9 | 10 | public override bool SupportsSubstringTextMatching => true; 11 | 12 | internal override IEnumerable Find(Options options) 13 | { 14 | return Driver.FindWindows(Locator, Scope, options); 15 | } 16 | 17 | internal override string QueryDescription => "window: " + Locator; 18 | 19 | protected internal override Exception GetMissingException() 20 | { 21 | return new MissingWindowException("Unable to find " + QueryDescription); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/WithTextFinder.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace Coypu.Finders 4 | { 5 | internal abstract class WithTextFinder : ElementFinder 6 | { 7 | protected readonly Regex textPattern; 8 | protected readonly string text; 9 | 10 | internal WithTextFinder(IDriver driver, string locator, DriverScope scope, Options options) 11 | : base(driver, locator, scope, options) 12 | { 13 | } 14 | 15 | internal WithTextFinder(IDriver driver, string locator, DriverScope scope, Options options, Regex textPattern) 16 | : this(driver, locator, scope, options) 17 | { 18 | this.textPattern = textPattern; 19 | } 20 | 21 | internal WithTextFinder(IDriver driver, string locator, DriverScope scope, Options options, string text) 22 | : this(driver, locator, scope, options) 23 | { 24 | this.text = text; 25 | } 26 | 27 | internal override string QueryDescription 28 | { 29 | get 30 | { 31 | var queryDesciption = SelectorType + ": " + Locator; 32 | if (textPattern != null) 33 | queryDesciption += " with text matching /" + (text ?? textPattern.ToString()) + "/"; 34 | 35 | return queryDesciption; 36 | } 37 | } 38 | 39 | protected abstract string SelectorType { get; } 40 | 41 | public static Regex TextAsRegex(string textEquals, bool exact) 42 | { 43 | Regex textMatches = null; 44 | if (textEquals != null) 45 | { 46 | var escapedText = Regex.Escape(textEquals); 47 | if (exact) 48 | escapedText = string.Format("^{0}$", escapedText); 49 | 50 | textMatches = new Regex(escapedText, RegexOptions.Multiline); 51 | } 52 | 53 | return textMatches; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/Coypu/Finders/XPathFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.RegularExpressions; 4 | using Coypu.Drivers; 5 | 6 | namespace Coypu.Finders 7 | { 8 | internal class XPathFinder : XPathQueryFinder 9 | { 10 | private readonly string text; 11 | private readonly Regex textPattern; 12 | 13 | protected string SelectorType => "xpath"; 14 | 15 | public XPathFinder(IDriver driver, string locator, DriverScope scope, Options options) 16 | : base(driver, locator, scope, options) 17 | { 18 | } 19 | 20 | public XPathFinder(IDriver driver, string locator, DriverScope scope, Options options, Regex textPattern) 21 | : base(driver, locator, scope, options) 22 | { 23 | this.textPattern = textPattern; 24 | } 25 | 26 | public XPathFinder(IDriver driver, string locator, DriverScope scope, Options options, string text) 27 | : base(driver, locator, scope, options) 28 | { 29 | this.text = text; 30 | } 31 | 32 | public override bool SupportsSubstringTextMatching => true; 33 | 34 | internal override string QueryDescription 35 | { 36 | get 37 | { 38 | var queryDesciption = SelectorType + ": " + Locator; 39 | if (text != null) 40 | queryDesciption += " with text " + text; 41 | if (textPattern != null) 42 | queryDesciption += " with text matching /" + (text ?? textPattern.ToString()) + "/"; 43 | 44 | return queryDesciption; 45 | } 46 | } 47 | 48 | protected override Func GetQuery(Html html) 49 | { 50 | return ((locator, options) => 51 | { 52 | if (string.IsNullOrEmpty(text)) 53 | { 54 | return Locator; 55 | } 56 | else 57 | { 58 | return Locator + XPath.Where(html.IsText(text, options)); 59 | } 60 | }); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Coypu/Finders/XPathQueryFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Coypu.Drivers; 4 | 5 | namespace Coypu.Finders 6 | { 7 | internal abstract class XPathQueryFinder : ElementFinder 8 | { 9 | protected XPathQueryFinder(IDriver driver, string locator, DriverScope scope, Options options) : base(driver, locator, scope, options) { } 10 | 11 | internal override IEnumerable Find(Options options) 12 | { 13 | var html = new Html(Scope.Browser.UppercaseTagNames); 14 | return Driver.FindAllXPath(GetQuery(html)(Locator, options), Scope, options); 15 | } 16 | 17 | protected abstract Func GetQuery(Html html); 18 | 19 | } 20 | } -------------------------------------------------------------------------------- /src/Coypu/FullyQualifiedUrlBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Coypu 4 | { 5 | internal class FullyQualifiedUrlBuilder : UrlBuilder 6 | { 7 | public string GetFullyQualifiedUrl(string virtualPath, SessionConfiguration sessionConfiguration) 8 | { 9 | var scheme = sessionConfiguration.SSL ? "https" : "http"; 10 | string userInfoPart = GetUserInfoPart(sessionConfiguration); 11 | var baseUrl = sessionConfiguration.Port == 80 12 | ? $"{scheme}://{userInfoPart}{sessionConfiguration.AppHost}" 13 | : $"{scheme}://{userInfoPart}{sessionConfiguration.AppHost}:{sessionConfiguration.Port}"; 14 | 15 | return new Uri(new Uri(baseUrl), virtualPath).AbsoluteUri; 16 | } 17 | 18 | private static string GetUserInfoPart(SessionConfiguration sessionConfiguration) 19 | { 20 | // PlaywrightDriver implements basic auth properly via the Authorization header 21 | if (sessionConfiguration.Driver == typeof(Drivers.Playwright.PlaywrightDriver) || 22 | string.IsNullOrEmpty(sessionConfiguration.UserInfo)) 23 | { 24 | return string.Empty; 25 | } 26 | return sessionConfiguration.UserInfo + "@"; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Coypu/Match.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu 2 | { 3 | /// 4 | /// With Match you can control how Coypu behaves when multiple elements match a query. 5 | /// Match.First just takes the first match and ignores any others 6 | /// Match.Single throws Coypu.AmbiguousException if there is more than one match 7 | /// 8 | public enum Match 9 | { 10 | /// 11 | /// Just picks the first element that matches 12 | /// 13 | First, 14 | 15 | /// 16 | /// Raises an error if more than one element matches 17 | /// 18 | Single 19 | } 20 | 21 | public enum TextPrecision 22 | { 23 | Exact, 24 | Substring, 25 | PreferExact 26 | } 27 | 28 | 29 | } -------------------------------------------------------------------------------- /src/Coypu/MissingDialogException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Coypu 5 | { 6 | /// 7 | /// Thrown whenever some expected HTML cannot be found 8 | /// 9 | public class MissingDialogException : FinderException 10 | { 11 | public MissingDialogException(string message) 12 | : base(message) 13 | { 14 | } 15 | 16 | public MissingDialogException(string message, Exception innerException) 17 | : base(message, innerException) 18 | { 19 | } 20 | 21 | public MissingDialogException(SerializationInfo info, StreamingContext context) 22 | : base(info, context) 23 | { 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Coypu/MissingHtmlException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Coypu 5 | { 6 | /// 7 | /// Thrown whenever some expected HTML cannot be found 8 | /// 9 | public class MissingHtmlException : FinderException 10 | { 11 | public MissingHtmlException(string message) 12 | : base(message) 13 | { 14 | } 15 | 16 | public MissingHtmlException(string message, Exception innerException) 17 | : base(message, innerException) 18 | { 19 | } 20 | 21 | public MissingHtmlException(SerializationInfo info, StreamingContext context) 22 | : base(info, context) 23 | { 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Coypu/MissingWindowException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Coypu 5 | { 6 | /// 7 | /// Thrown whenever an expected browser window cannot be found 8 | /// 9 | public class MissingWindowException : FinderException 10 | { 11 | public MissingWindowException(string message) 12 | : base(message) 13 | { 14 | } 15 | 16 | public MissingWindowException(string message, Exception innerException) 17 | : base(message, innerException) 18 | { 19 | } 20 | 21 | public MissingWindowException(SerializationInfo info, StreamingContext context) 22 | : base(info, context) 23 | { 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Coypu/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | [assembly: CLSCompliant(true)] 6 | 7 | // Setting ComVisible to false makes the types in this assembly not visible 8 | // to COM components. If you need to access a type in this assembly from 9 | // COM, set the ComVisible attribute to true on that type. 10 | 11 | [assembly: ComVisible(false)] 12 | 13 | // The following GUID is for the ID of the typelib if this project is exposed to COM 14 | 15 | [assembly: Guid("7937df23-f996-4702-b09d-2905f9acdaac")] 16 | [assembly: InternalsVisibleTo("Coypu.Tests")] 17 | [assembly: InternalsVisibleTo("Coypu.Drivers.Tests")] 18 | -------------------------------------------------------------------------------- /src/Coypu/Queries/ActionSatisfiesPredicateQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Coypu.Actions; 4 | using Coypu.Timing; 5 | 6 | namespace Coypu.Queries 7 | { 8 | internal class ActionSatisfiesPredicateQuery : Query 9 | { 10 | private readonly BrowserAction tryThis; 11 | private readonly PredicateQuery until; 12 | private readonly TimingStrategy timingStrategy; 13 | 14 | public Options Options { get; } 15 | public DriverScope Scope { get; } 16 | 17 | internal ActionSatisfiesPredicateQuery(BrowserAction tryThis, PredicateQuery until, Options options, TimingStrategy timingStrategy) 18 | { 19 | this.tryThis = tryThis; 20 | this.until = until; 21 | this.timingStrategy = timingStrategy; 22 | Options = options; 23 | Scope = tryThis.Scope; 24 | } 25 | 26 | public bool Run() 27 | { 28 | tryThis.Act(); 29 | return timingStrategy.Synchronise(until); 30 | } 31 | 32 | public object ExpectedResult => true; 33 | } 34 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/DialogQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | internal class HasNoDialogQuery : DriverScopeQuery 4 | { 5 | private readonly IDriver driver; 6 | private readonly string text; 7 | public override object ExpectedResult => true; 8 | 9 | protected internal HasNoDialogQuery(IDriver driver, string text, DriverScope driverScope, Options options) : base(driverScope,options) 10 | { 11 | this.driver = driver; 12 | this.text = text; 13 | } 14 | 15 | public override bool Run() 16 | { 17 | return !driver.HasDialog(text,Scope); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/DriverScopeQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | internal abstract class DriverScopeQuery : Query 4 | { 5 | public DriverScope Scope { get; } 6 | public Options Options { get; } 7 | 8 | internal DriverScopeQuery(DriverScope driverScope, Options options) 9 | { 10 | Scope = driverScope; 11 | Options = options; 12 | } 13 | 14 | public abstract T Run(); 15 | public abstract object ExpectedResult { get; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/ElementExistsQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | internal class ElementExistsQuery : DriverScopeQuery 4 | { 5 | protected internal ElementExistsQuery(DriverScope driverScope, Options options) 6 | : base(driverScope, options) 7 | { 8 | } 9 | 10 | public override object ExpectedResult => true; 11 | 12 | public override bool Run() 13 | { 14 | try 15 | { 16 | Scope.Stale = true; 17 | Scope.FindElement(); 18 | return true; 19 | } 20 | catch (MissingHtmlException) 21 | { 22 | return false; 23 | } 24 | catch (MissingWindowException) 25 | { 26 | return false; 27 | } 28 | catch (StaleElementException) 29 | { 30 | return false; 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/ElementMissingQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | internal class ElementMissingQuery : DriverScopeQuery 4 | { 5 | protected internal ElementMissingQuery(DriverScope driverScope, Options options) 6 | : base(driverScope, options) 7 | { 8 | } 9 | 10 | public override object ExpectedResult => true; 11 | 12 | public override bool Run() 13 | { 14 | try 15 | { 16 | Scope.Stale = true; 17 | Scope.FindElement(); 18 | return false; 19 | } 20 | catch (MissingHtmlException) 21 | { 22 | return true; 23 | } 24 | catch (MissingWindowException) 25 | { 26 | return true; 27 | } 28 | catch (StaleElementException) 29 | { 30 | return true; 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Coypu/Queries/ElementQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | internal class ElementQuery : DriverScopeQuery 4 | { 5 | public ElementQuery(DriverScope driverScope, Options options) : base(driverScope, options) 6 | { 7 | } 8 | 9 | public override object ExpectedResult => null; 10 | 11 | public override Element Run() 12 | { 13 | return Scope.FindElement(); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/ElementScopeQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | internal abstract class ElementScopeQuery : Query 4 | { 5 | public Options Options { get; } 6 | public DriverScope Scope => DriverScope; 7 | public DriverScope DriverScope { get; } 8 | 9 | internal ElementScopeQuery(DriverScope scope, Options options) 10 | { 11 | DriverScope = scope; 12 | Options = options; 13 | } 14 | 15 | public abstract T Run(); 16 | public abstract object ExpectedResult { get; } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/FindAllCssWithPredicateQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Coypu.Queries 6 | { 7 | internal class FindAllCssWithPredicateQuery : DriverScopeQuery> 8 | { 9 | private readonly string locator; 10 | private readonly Func,bool> predicate; 11 | 12 | public FindAllCssWithPredicateQuery(string locator, Func,bool> predicate, DriverScope driverScope, Options options) : base(driverScope, options) 13 | { 14 | if (predicate == null) 15 | predicate = e => true; 16 | 17 | this.predicate = predicate; 18 | this.locator = locator; 19 | 20 | } 21 | 22 | public override IEnumerable Run() 23 | { 24 | var allElements = Scope.FindAllCssNoPredicate(locator, Options).ToArray(); 25 | if (!predicate(allElements)) 26 | throw new MissingHtmlException("FindAllCss did not find elements matching your predicate"); 27 | 28 | return allElements; 29 | } 30 | 31 | public override object ExpectedResult => null; 32 | } 33 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/FindAllXPathWithPredicateQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Coypu.Queries 6 | { 7 | internal class FindAllXPathWithPredicateQuery : DriverScopeQuery> 8 | { 9 | private readonly string locator; 10 | private Func, bool> predicate; 11 | 12 | public FindAllXPathWithPredicateQuery(string locator, Func, bool> predicate, DriverScope driverScope, Options options) 13 | : base(driverScope, options) 14 | { 15 | if (predicate == null) 16 | predicate = e => true; 17 | 18 | this.predicate = predicate; 19 | this.locator = locator; 20 | } 21 | 22 | public override IEnumerable Run() 23 | { 24 | var allElements = Scope.FindAllXPathNoPredicate(locator, Options).ToArray(); 25 | if (!predicate(allElements)) 26 | throw new MissingHtmlException("FindAllXPath did not find elements matching your predicate"); 27 | 28 | return allElements; 29 | } 30 | 31 | public override object ExpectedResult => null; 32 | } 33 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/HasContentMatchQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace Coypu.Queries 4 | { 5 | internal class HasContentMatchQuery : DriverScopeQuery 6 | { 7 | private readonly Regex text; 8 | public override object ExpectedResult => true; 9 | 10 | protected internal HasContentMatchQuery(DriverScope scope, Regex text, Options options) : base(scope,options) 11 | { 12 | this.text = text; 13 | } 14 | 15 | public override bool Run() 16 | { 17 | return text.IsMatch(Scope.FindElement().Text); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/HasContentQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | internal class HasContentQuery : DriverScopeQuery 4 | { 5 | private readonly string text; 6 | public override object ExpectedResult => true; 7 | 8 | internal HasContentQuery(DriverScope scope, string text, Options options) : base(scope,options) 9 | { 10 | this.text = text; 11 | } 12 | 13 | public override bool Run() 14 | { 15 | return Scope.FindElement().Text.Contains(text); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/HasDialogQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | internal class HasDialogQuery : DriverScopeQuery 4 | { 5 | private readonly IDriver driver; 6 | private readonly string text; 7 | public override object ExpectedResult => true; 8 | 9 | protected internal HasDialogQuery(IDriver driver, string text, DriverScope driverScope, Options options) : base(driverScope,options) 10 | { 11 | this.driver = driver; 12 | this.text = text; 13 | } 14 | 15 | public override bool Run() 16 | { 17 | return driver.HasDialog(text, Scope); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Coypu/Queries/HasNoContentMatchQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace Coypu.Queries 4 | { 5 | internal class HasNoContentMatchQuery : DriverScopeQuery 6 | { 7 | private readonly Regex text; 8 | public override object ExpectedResult => true; 9 | 10 | protected internal HasNoContentMatchQuery(DriverScope scope, Regex text, Options options) 11 | : base(scope, options) 12 | { 13 | this.text = text; 14 | } 15 | 16 | public override bool Run() 17 | { 18 | return !text.IsMatch(Scope.FindElement().Text); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/HasNoContentQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | internal class HasNoContentQuery : DriverScopeQuery 4 | { 5 | private readonly string text; 6 | public override object ExpectedResult => true; 7 | 8 | protected internal HasNoContentQuery(DriverScope scope, string text, Options options) : base(scope,options) 9 | { 10 | this.text = text; 11 | } 12 | 13 | public override bool Run() 14 | { 15 | return !Scope.FindElement().Text.Contains(text); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/HasNoValueQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | internal class HasNoValueQuery : ElementScopeQuery 4 | { 5 | private readonly string text; 6 | public override object ExpectedResult => true; 7 | 8 | internal HasNoValueQuery(DriverScope scope, string text, Options options) 9 | : base(scope, options) 10 | { 11 | this.text = text; 12 | } 13 | 14 | public override bool Run() 15 | { 16 | return DriverScope.FindElement().Value != text; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/HasValueQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | internal class HasValueQuery : ElementScopeQuery 4 | { 5 | private readonly string text; 6 | public override object ExpectedResult => true; 7 | 8 | internal HasValueQuery(DriverScope scope, string text, Options options) 9 | : base(scope, options) 10 | { 11 | this.text = text; 12 | } 13 | 14 | public override bool Run() 15 | { 16 | return DriverScope.FindElement().Value == text; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/LambdaPredicateQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Coypu.Queries 4 | { 5 | public class LambdaPredicateQuery : PredicateQuery 6 | { 7 | private readonly Func query; 8 | 9 | public LambdaPredicateQuery(Func query, Options options = null) : base(options) 10 | { 11 | this.query = query; 12 | } 13 | 14 | public override bool Predicate() 15 | { 16 | return query(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/LambdaQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Coypu.Queries 4 | { 5 | public class LambdaQuery : Query 6 | { 7 | private readonly Func query; 8 | public Options Options { get; } 9 | public DriverScope Scope { get; } 10 | public object ExpectedResult { get; set; } 11 | 12 | public LambdaQuery(Func query) 13 | { 14 | this.query = query; 15 | } 16 | 17 | public LambdaQuery(Func query, Options options) : this(query) 18 | { 19 | Options = options; 20 | } 21 | 22 | public LambdaQuery(Func query, object expectedResult, Options options) : this(query, options) 23 | { 24 | ExpectedResult = expectedResult; 25 | } 26 | 27 | public LambdaQuery(Func query, object expectedResult, DriverScope scope, Options options) 28 | : this(query, expectedResult, options) 29 | { 30 | Scope = scope; 31 | } 32 | 33 | public T Run() 34 | { 35 | return query(); 36 | } 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/PredicateQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Coypu.Queries 4 | { 5 | public abstract class PredicateQuery : Query 6 | { 7 | public Options Options { get; } 8 | public DriverScope Scope { get; private set; } 9 | 10 | protected PredicateQuery(Options options) 11 | { 12 | Options = options; 13 | } 14 | 15 | public abstract bool Predicate(); 16 | 17 | public bool Run() 18 | { 19 | return Predicate(); 20 | } 21 | 22 | public object ExpectedResult => true; 23 | } 24 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/Query.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | public interface Query 4 | { 5 | TReturn Run(); 6 | object ExpectedResult { get; } 7 | Options Options { get; } 8 | DriverScope Scope { get; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/WindowExistsQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | internal class WindowExistsQuery : DriverScopeQuery 4 | { 5 | protected internal WindowExistsQuery(DriverScope driverScope, Options options) 6 | : base(driverScope,options) 7 | { 8 | } 9 | 10 | public override object ExpectedResult => true; 11 | 12 | public override bool Run() 13 | { 14 | try 15 | { 16 | Scope.Stale = true; 17 | Scope.FindElement(); 18 | return true; 19 | } 20 | catch (MissingWindowException) 21 | { 22 | return false; 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Coypu/Queries/WindowMissingQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu.Queries 2 | { 3 | internal class WindowMissingQuery : DriverScopeQuery 4 | { 5 | protected internal WindowMissingQuery(DriverScope driverScope, Options options) 6 | : base(driverScope, options) 7 | { 8 | } 9 | 10 | public override object ExpectedResult => true; 11 | 12 | public override bool Run() 13 | { 14 | try 15 | { 16 | Scope.Stale = true; 17 | Scope.FindElement(); 18 | return false; 19 | } 20 | catch (MissingWindowException) 21 | { 22 | return true; 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Coypu/SnapshotElementScope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Actions; 3 | using Coypu.Queries; 4 | 5 | namespace Coypu 6 | { 7 | /// 8 | /// The scope of an element already found in the document, therefore not deferred. 9 | /// 10 | /// If this element becomes stale then using this scope will not try to refind the element but 11 | /// will raise a MissingHtmlException immediately. 12 | /// 13 | public class SnapshotElementScope : ElementScope 14 | { 15 | private readonly Element element; 16 | private readonly Options options; 17 | 18 | internal SnapshotElementScope(Element element, DriverScope scope, Options options) 19 | : base(null, scope) 20 | { 21 | this.element = element; 22 | this.options = options; 23 | } 24 | 25 | internal override bool Stale 26 | { 27 | get => true; 28 | set {} 29 | } 30 | 31 | protected internal override Element FindElement() 32 | { 33 | return element; 34 | } 35 | 36 | internal override void Try(DriverAction action) 37 | { 38 | action.Act(); 39 | } 40 | 41 | internal override bool Try(Query query) 42 | { 43 | return query.Run(); 44 | } 45 | 46 | internal override T Try(Func getAttribute) 47 | { 48 | return getAttribute(); 49 | } 50 | 51 | public override bool Exists(Options options = null) 52 | { 53 | return FindXPath(".", options).Exists(); 54 | } 55 | 56 | public override bool Missing(Options options = null) 57 | { 58 | var scope = FindXPath(".", options); 59 | return scope.Missing(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Coypu/StaleElementException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Coypu 5 | { 6 | /// 7 | /// Thrown whenever a snapshot element returned from FindAllCss or FindAllXPath has been removed 8 | /// from the DOM and cannot be refound due to the snapshot nature of the FindAll methods 9 | /// 10 | public class StaleElementException : Exception 11 | { 12 | private new const string Message = "This element has been removed from the DOM. Coypu will normally re-find elements using the original locators in this situation, except if you have captured a snapshot list of all matching elements using FindAllCss() or FindAllXPath()"; 13 | 14 | public StaleElementException() 15 | : base(Message) 16 | { 17 | } 18 | 19 | public StaleElementException(Exception innerException) 20 | : base(Message, innerException) 21 | { 22 | } 23 | 24 | public StaleElementException(SerializationInfo info, StreamingContext context) 25 | : base(info, context) 26 | { 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Coypu/State.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Queries; 3 | 4 | namespace Coypu 5 | { 6 | /// 7 | /// A possible state for the current page 8 | /// 9 | public class State 10 | { 11 | private readonly Query condition; 12 | 13 | /// 14 | /// Describe a possible state for the page with a condition to identify this state. 15 | /// 16 | ///How to identify this state 17 | public State(Query condition) 18 | { 19 | this.condition = condition; 20 | } 21 | 22 | /// 23 | /// Describe a possible state for the page with a condition to identify this state. 24 | /// 25 | ///How to identify this state 26 | public State(Func condition) 27 | { 28 | this.condition = new LambdaQuery(condition,true, new Options{Timeout = TimeSpan.Zero}); 29 | } 30 | 31 | internal bool ConditionWasMet { get; private set; } 32 | 33 | internal bool CheckCondition() 34 | { 35 | return ConditionWasMet = condition.Run(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Coypu/Timing/StopwatchWaiter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Coypu.Timing 5 | { 6 | internal class StopwatchWaiter : Waiter 7 | { 8 | public void Wait(TimeSpan duration) 9 | { 10 | var stopWatch = Stopwatch.StartNew(); 11 | while(stopWatch.ElapsedMilliseconds < duration.TotalMilliseconds) 12 | { 13 | } 14 | stopWatch.Stop(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Coypu/Timing/TimingStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Coypu.Actions; 3 | using Coypu.Queries; 4 | 5 | namespace Coypu.Timing 6 | { 7 | public interface TimingStrategy 8 | { 9 | T Synchronise(Query query); 10 | void TryUntil(BrowserAction tryThis, PredicateQuery until, Options options); 11 | bool ZeroTimeout { get; set; } 12 | void SetOverrideTimeout(TimeSpan timeout); 13 | void ClearOverrideTimeout(); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Coypu/Timing/Waiter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Coypu.Timing 4 | { 5 | public interface Waiter 6 | { 7 | void Wait(TimeSpan duration); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Coypu/UrlBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Coypu 2 | { 3 | public interface UrlBuilder 4 | { 5 | string GetFullyQualifiedUrl(string virtualPath, SessionConfiguration sessionConfiguration); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Coypu/WebRequests/RequestCookieInjector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | 4 | namespace Coypu.WebRequests 5 | { 6 | internal interface RequestCookieInjector 7 | { 8 | WebRequest InjectCookies(WebRequest httpRequest, IEnumerable enumerable); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Coypu/WebRequests/RestrictedResourceDownloader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | 4 | namespace Coypu.WebRequests 5 | { 6 | internal interface RestrictedResourceDownloader 7 | { 8 | void SetCookies(IEnumerable getBrowserCookies); 9 | void DownloadFile(string resource, string saveAs); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Coypu/WebRequests/WebClientWithCookies.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | 5 | namespace Coypu.WebRequests 6 | { 7 | internal class WebClientWithCookies : WebClient, RestrictedResourceDownloader 8 | { 9 | private IEnumerable requestCookies; 10 | private readonly WebRequestCookieInjector webRequestCookieInjector; 11 | 12 | public WebClientWithCookies() 13 | { 14 | webRequestCookieInjector = new WebRequestCookieInjector(); 15 | } 16 | 17 | public void SetCookies(IEnumerable cookies) 18 | { 19 | requestCookies = cookies; 20 | } 21 | 22 | protected override WebRequest GetWebRequest(Uri address) 23 | { 24 | return webRequestCookieInjector.InjectCookies(base.GetWebRequest(address), requestCookies); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Coypu/WebRequests/WebRequestCookieInjector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | 4 | namespace Coypu.WebRequests 5 | { 6 | internal class WebRequestCookieInjector 7 | { 8 | internal WebRequest InjectCookies(WebRequest webRequest, IEnumerable cookies) 9 | { 10 | WebRequest request = webRequest; 11 | 12 | return request is HttpWebRequest httpRequest 13 | ? AddCookiesToCookieContainer(httpRequest, cookies) 14 | : request; 15 | } 16 | 17 | internal static HttpWebRequest AddCookiesToCookieContainer(HttpWebRequest httpRequest, 18 | IEnumerable cookies) 19 | { 20 | httpRequest.CookieContainer = new CookieContainer(); 21 | 22 | foreach (Cookie cookie in cookies) 23 | httpRequest.CookieContainer.Add(cookie); 24 | 25 | return httpRequest; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/IMPORTANT.txt: -------------------------------------------------------------------------------- 1 | 1. When changing version in properties of Coypu and Coypu.NUnit projects DO NOT iterate 'Assembly version' unless its a major version change (breaking change). 2 | Change only 'Assembly file version' e.g. when updating selenium webdriver dependency. 3 | https://blogs.msdn.microsoft.com/jjameson/2009/04/03/best-practices-for-net-assembly-versioning/ 4 | 5 | 2. Remember to change manually in nuget package of Coypu.NUnit the version range for Coypu dependency because of the issue: https://github.com/NuGet/Home/issues/5556 -------------------------------------------------------------------------------- /src/NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | --------------------------------------------------------------------------------