diff --git a/src/OrchardCoreContrib.Testing.UI/Browser.cs b/src/OrchardCoreContrib.Testing.UI/Browser.cs index 3162664..416e313 100644 --- a/src/OrchardCoreContrib.Testing.UI/Browser.cs +++ b/src/OrchardCoreContrib.Testing.UI/Browser.cs @@ -22,6 +22,9 @@ public class Browser(IPlaywrightBrowserAccessor playwrightBrowserAccessor) : IBr /// public string Version { get; set; } = playwrightBrowserAccessor.PlaywrightBrowser.Version; + /// + public UITestOptions TestOptions { get; set; } + /// public async Task OpenPageAsync(string url) { @@ -29,7 +32,7 @@ public async Task OpenPageAsync(string url) await playwrightPage.GotoAsync(url); - var page = new Page(new PlaywrightPageAccessor(playwrightPage)) + var page = new Page(new PlaywrightPageAccessor(playwrightPage), this) { Title = await playwrightPage.TitleAsync(), Content = await playwrightPage.ContentAsync() diff --git a/src/OrchardCoreContrib.Testing.UI/BrowserFactory.cs b/src/OrchardCoreContrib.Testing.UI/BrowserFactory.cs index fbc951f..7bd959d 100644 --- a/src/OrchardCoreContrib.Testing.UI/BrowserFactory.cs +++ b/src/OrchardCoreContrib.Testing.UI/BrowserFactory.cs @@ -39,6 +39,10 @@ public static async Task CreateAsync(IPlaywright playwright, UITestOpt _ => throw new NotSupportedException() }; - return new Browser(new PlaywrightBrowserAccessor(browser)) { Type = testOptions.BrowserType }; + return new Browser(new PlaywrightBrowserAccessor(browser)) + { + Type = testOptions.BrowserType, + TestOptions = testOptions + }; } } diff --git a/src/OrchardCoreContrib.Testing.UI/Element.cs b/src/OrchardCoreContrib.Testing.UI/Element.cs index 86a29b8..c6e23ba 100644 --- a/src/OrchardCoreContrib.Testing.UI/Element.cs +++ b/src/OrchardCoreContrib.Testing.UI/Element.cs @@ -7,8 +7,18 @@ namespace OrchardCoreContrib.Testing.UI; /// /// The . /// The . -public class Element(ILocator locator) : IElement +/// The . +public class Element(ILocator locator, IPage page) : IElement { + private readonly LocatorClickOptions _locatorClickOptions = page.Browser.TestOptions.Delay == 0 + ? null + : new() { Delay = page.Browser.TestOptions.Delay }; + private readonly LocatorPressSequentiallyOptions _locatorPressSequentiallyOptions = page.Browser.TestOptions.Delay == 0 + ? null + : new() { Delay = page.Browser.TestOptions.Delay }; + + IPage IElement.Page => page; + /// public string InnerText { get; set; } @@ -22,12 +32,12 @@ public class Element(ILocator locator) : IElement public bool Visible { get; set; } /// - public async Task ClickAsync() => await locator.ClickAsync(); + public async Task ClickAsync() => await locator.ClickAsync(_locatorClickOptions); /// public async Task TypeAsync(string text) { - await locator.FillAsync(text); + await locator.PressSequentiallyAsync(text, _locatorPressSequentiallyOptions); InnerText = await locator.InnerTextAsync(); InnerHtml = await locator.InnerHTMLAsync(); diff --git a/src/OrchardCoreContrib.Testing.UI/IBrowser.cs b/src/OrchardCoreContrib.Testing.UI/IBrowser.cs index 11946e4..1b78e1f 100644 --- a/src/OrchardCoreContrib.Testing.UI/IBrowser.cs +++ b/src/OrchardCoreContrib.Testing.UI/IBrowser.cs @@ -10,6 +10,11 @@ public interface IBrowser /// public Microsoft.Playwright.IBrowser InnerBrowser { get; } + /// + /// Gets the test options that will be applied; + /// + public UITestOptions TestOptions { get; set; } + /// /// Gets or sets the browser type. /// diff --git a/src/OrchardCoreContrib.Testing.UI/IElement.cs b/src/OrchardCoreContrib.Testing.UI/IElement.cs index 66e2958..4abbc9f 100644 --- a/src/OrchardCoreContrib.Testing.UI/IElement.cs +++ b/src/OrchardCoreContrib.Testing.UI/IElement.cs @@ -5,6 +5,8 @@ /// public interface IElement { + internal IPage Page { get; } + /// /// Gets the inner text of the element. /// diff --git a/src/OrchardCoreContrib.Testing.UI/IPage.cs b/src/OrchardCoreContrib.Testing.UI/IPage.cs index 2fbc7dc..2ccaa57 100644 --- a/src/OrchardCoreContrib.Testing.UI/IPage.cs +++ b/src/OrchardCoreContrib.Testing.UI/IPage.cs @@ -10,6 +10,8 @@ public interface IPage /// public Microsoft.Playwright.IPage InnerPage { get; } + internal IBrowser Browser { get; } + /// /// Gets or sets the page title. /// diff --git a/src/OrchardCoreContrib.Testing.UI/IUITest.cs b/src/OrchardCoreContrib.Testing.UI/IUITest.cs new file mode 100644 index 0000000..990f653 --- /dev/null +++ b/src/OrchardCoreContrib.Testing.UI/IUITest.cs @@ -0,0 +1,19 @@ +using Xunit; + +namespace OrchardCoreContrib.Testing.UI; + +/// +/// Represents a contract for UI test. +/// +public interface IUITest : IAsyncLifetime +{ + /// + /// Gets or sets the browser instance to be used during the test. + /// + public IBrowser Browser { get; set; } + + /// + /// Gets the options used during the test. + /// + public UITestOptions Options { get; } +} diff --git a/src/OrchardCoreContrib.Testing.UI/Page.cs b/src/OrchardCoreContrib.Testing.UI/Page.cs index 979f5ad..52ac48e 100644 --- a/src/OrchardCoreContrib.Testing.UI/Page.cs +++ b/src/OrchardCoreContrib.Testing.UI/Page.cs @@ -9,11 +9,14 @@ namespace OrchardCoreContrib.Testing.UI; /// Creates an instance of . /// /// The . -public class Page(IPlaywrightPageAccessor playwrightPageAccessor) : IPage +/// The . +public class Page(IPlaywrightPageAccessor playwrightPageAccessor, IBrowser browser) : IPage { /// public Microsoft.Playwright.IPage InnerPage => playwrightPageAccessor.PlaywrightPage; + IBrowser IPage.Browser => browser; + /// public string Title { get; set; } @@ -33,7 +36,7 @@ public async Task GoToAsync(string url) public IElement FindElement(string selector) { var locator = InnerPage.Locator(selector); - var element = new Element(locator) + var element = new Element(locator, this) { InnerText = locator.InnerTextAsync().GetAwaiter().GetResult(), InnerHtml = locator.InnerHTMLAsync().GetAwaiter().GetResult(), diff --git a/src/OrchardCoreContrib.Testing.UI/UITest.cs b/src/OrchardCoreContrib.Testing.UI/UITest.cs index e6cfe53..9e98153 100644 --- a/src/OrchardCoreContrib.Testing.UI/UITest.cs +++ b/src/OrchardCoreContrib.Testing.UI/UITest.cs @@ -1,43 +1,17 @@ -using Microsoft.Playwright; -using Xunit; - -namespace OrchardCoreContrib.Testing.UI; +namespace OrchardCoreContrib.Testing.UI; /// /// Represents a UI testing class. /// /// The browser type that will be used during the test. Defaults to . /// Whether the browser runs in headless mode or not. Defaults to true. -public class UITest(BrowserType browserType = BrowserType.Edge, bool headless = true) : IAsyncLifetime -{ - private IPlaywright _playwright; - - /// - /// Gets the browser instance to be used during the test. - /// - public IBrowser Browser { get; private set; } - - public UITestOptions Options { get; private set; } - - /// - public async Task InitializeAsync() +/// The amount of time to wait between execute two actions. Defaults to 0. +public class UITest(BrowserType browserType = BrowserType.Edge, bool headless = true, int delay = 0) + : UITestBase(new UITestOptions { - Options = new UITestOptions - { - BrowserType = browserType, - Headless = headless - }; - - _playwright = await Playwright.CreateAsync(); - - Browser = await BrowserFactory.CreateAsync(_playwright, Options); - } - - /// - public async Task DisposeAsync() - { - _playwright.Dispose(); - - await Task.CompletedTask; - } + BrowserType = browserType, + Headless = headless, + Delay = delay + }) +{ } diff --git a/src/OrchardCoreContrib.Testing.UI/UITestBase.cs b/src/OrchardCoreContrib.Testing.UI/UITestBase.cs index 5bd552f..5271059 100644 --- a/src/OrchardCoreContrib.Testing.UI/UITestBase.cs +++ b/src/OrchardCoreContrib.Testing.UI/UITestBase.cs @@ -1,18 +1,34 @@ -using OrchardCoreContrib.Testing.UI.Infrastructure; +using Microsoft.Playwright; namespace OrchardCoreContrib.Testing.UI; -/// -/// Represents a base class for UI testing. -/// -/// The startup class that will be used as entry point. -/// The . -public abstract class UITestBase(WebApplicationFactoryFixture fixture) where TStartup : class +public abstract class UITestBase(UITestOptions testOptions) : IUITest { + private IPlaywright _playwright; + + /// + /// Gets or sets the browser instance to be used during the test. + /// + public IBrowser Browser { get; set; } + /// - /// Gets the base URL used for the tested website. + /// Gets the options used during the test. /// - public string BaseUrl => fixture.ServerAddress; + public UITestOptions Options => testOptions; + + /// + public virtual async Task InitializeAsync() + { + _playwright = await Playwright.CreateAsync(); + + Browser = await BrowserFactory.CreateAsync(_playwright, Options); + } + + /// + public virtual async Task DisposeAsync() + { + _playwright.Dispose(); - public UITestOptions Options { get; protected set; } + await Task.CompletedTask; + } } diff --git a/src/OrchardCoreContrib.Testing.UI/UITestBaseOfT.cs b/src/OrchardCoreContrib.Testing.UI/UITestBaseOfT.cs new file mode 100644 index 0000000..b62826b --- /dev/null +++ b/src/OrchardCoreContrib.Testing.UI/UITestBaseOfT.cs @@ -0,0 +1,17 @@ +using OrchardCoreContrib.Testing.UI.Infrastructure; + +namespace OrchardCoreContrib.Testing.UI; + +/// +/// Represents a base class for UI testing. +/// +/// The startup class that will be used as entry point. +/// The . +public abstract class UITestBase(WebApplicationFactoryFixture fixture, UITestOptions testOptions) + : UITestBase(testOptions) where TStartup : class +{ + /// + /// Gets the base URL used for the tested website. + /// + public string BaseUrl => fixture.ServerAddress; +} diff --git a/src/OrchardCoreContrib.Testing.UI/UITestOfT.cs b/src/OrchardCoreContrib.Testing.UI/UITestOfT.cs index 8f3efab..7bc998d 100644 --- a/src/OrchardCoreContrib.Testing.UI/UITestOfT.cs +++ b/src/OrchardCoreContrib.Testing.UI/UITestOfT.cs @@ -1,6 +1,4 @@ -using Microsoft.Playwright; -using OrchardCoreContrib.Testing.UI.Infrastructure; -using Xunit; +using OrchardCoreContrib.Testing.UI.Infrastructure; namespace OrchardCoreContrib.Testing.UI; @@ -9,37 +7,14 @@ namespace OrchardCoreContrib.Testing.UI; /// /// The browser type that will be used during the test. Defaults to . /// Whether the browser runs in headless mode or not. Defaults to true. +/// The amount of time to wait between execute two actions. Defaults to 0. /// The startup class type that will be used as entry point. -public class UITest(BrowserType browserType = BrowserType.Edge, bool headless = true) : - UITestBase(new WebApplicationFactoryFixture()), - IAsyncLifetime where TStartup : class -{ - private IPlaywright _playwright; - - /// - /// Gets the browser instance to be used during the test. - /// - public IBrowser Browser { get; private set; } - - /// - public async Task InitializeAsync() +public class UITest(BrowserType browserType = BrowserType.Edge, bool headless = true, int delay = 0) : + UITestBase(new WebApplicationFactoryFixture(), new UITestOptions { - Options = new UITestOptions - { - BrowserType = browserType, - Headless = headless - }; - - _playwright = await Playwright.CreateAsync(); - - Browser = await BrowserFactory.CreateAsync(_playwright, Options); - } - - /// - public async Task DisposeAsync() - { - _playwright.Dispose(); - - await Task.CompletedTask; - } + BrowserType = browserType, + Headless = headless, + Delay = delay + }), IUITest where TStartup : class +{ } diff --git a/src/OrchardCoreContrib.Testing.UI/UITestOptions.cs b/src/OrchardCoreContrib.Testing.UI/UITestOptions.cs index 068244c..598e587 100644 --- a/src/OrchardCoreContrib.Testing.UI/UITestOptions.cs +++ b/src/OrchardCoreContrib.Testing.UI/UITestOptions.cs @@ -14,4 +14,9 @@ public class UITestOptions /// Gets or sets the browser type to run the test on. Defaults . /// public BrowserType BrowserType { get; set; } = BrowserType.Edge; + + /// + /// Gets or sets amount of time to wait before executing each event on the page. Defaults 0. + /// + public int Delay { get; set; } = 0; } diff --git a/test/OrchardCoreContrib.Testing.UI.Tests/ElementTests.cs b/test/OrchardCoreContrib.Testing.UI.Tests/ElementTests.cs index 79a8d48..b608829 100644 --- a/test/OrchardCoreContrib.Testing.UI.Tests/ElementTests.cs +++ b/test/OrchardCoreContrib.Testing.UI.Tests/ElementTests.cs @@ -2,11 +2,22 @@ public class ElementTests { + private readonly Page _page; + + public ElementTests() + { + var browser = new Browser(new PlaywrightBrowserAccessor(Mock.Of())) + { + TestOptions = new UITestOptions() + }; + _page = new Page(new PlaywrightPageAccessor(Mock.Of()), browser); + } + [Fact] public void GetElementInformation() { // Act - var element = new Element(Mock.Of()) + var element = new Element(Mock.Of(), _page) { InnerHtml = "

Orchard Core Contrib

", InnerText = "Orchard Core Contrib", @@ -26,7 +37,7 @@ public async Task ClickElement() { // Arrange var locatorMock = new Mock(); - var element = new Element(locatorMock.Object); + var element = new Element(locatorMock.Object, _page); // Act await element.ClickAsync(); @@ -40,13 +51,13 @@ public async Task TypeTextIntoElement() { // Arrange var locatorMock = new Mock(); - locatorMock.Setup(l => l.FillAsync(It.IsAny(), null)) + locatorMock.Setup(l => l.PressSequentiallyAsync(It.IsAny(), null)) .Callback(() => { locatorMock.Setup(l => l.InnerTextAsync(null)) .ReturnsAsync("Orchard Core Contrib"); }); - var element = new Element(locatorMock.Object); + var element = new Element(locatorMock.Object, _page); // Act await element.TypeAsync("Orchard Core Contrib"); diff --git a/test/OrchardCoreContrib.Testing.UI.Tests/PageTests.cs b/test/OrchardCoreContrib.Testing.UI.Tests/PageTests.cs index 0c19299..6a3702b 100644 --- a/test/OrchardCoreContrib.Testing.UI.Tests/PageTests.cs +++ b/test/OrchardCoreContrib.Testing.UI.Tests/PageTests.cs @@ -2,6 +2,16 @@ namespace OrchardCoreContrib.Testing.UI.Tests; public class PageTests { + private readonly Browser _browser; + + public PageTests() + { + _browser = new Browser(new PlaywrightBrowserAccessor(Mock.Of())) + { + TestOptions = new UITestOptions() + }; + } + [Fact] public void ShouldCreatePage() { @@ -9,7 +19,7 @@ public void ShouldCreatePage() var playwrightPageAccessor = new PlaywrightPageAccessor(Mock.Of()); // Act - var page = new Page(playwrightPageAccessor); + var page = new Page(playwrightPageAccessor, _browser); // Assert Assert.NotNull(page); @@ -31,7 +41,7 @@ public async Task GetPageInformation() var playwrightPageAccessor = new PlaywrightPageAccessor(pageMock.Object); // Act - var page = new Page(playwrightPageAccessor); + var page = new Page(playwrightPageAccessor, _browser); await page.GoToAsync("www.occ.com"); // Assert @@ -50,7 +60,7 @@ public void ShouldFindElement() var playwrightPageAccessor = new PlaywrightPageAccessor(pageMock.Object); - var page = new Page(playwrightPageAccessor); + var page = new Page(playwrightPageAccessor, _browser); // Act var result = page.FindElement("selector"); diff --git a/test/OrchardCoreContrib.Testing.UI.Tests/PlaywrightPageHelper.cs b/test/OrchardCoreContrib.Testing.UI.Tests/PlaywrightPageHelper.cs index 45b18f7..bf87057 100644 --- a/test/OrchardCoreContrib.Testing.UI.Tests/PlaywrightPageHelper.cs +++ b/test/OrchardCoreContrib.Testing.UI.Tests/PlaywrightPageHelper.cs @@ -5,12 +5,16 @@ internal class PlaywrightPageHelper public static async Task GotoAsync(string pageName) { var playwright = await Playwright.CreateAsync(); - var browser = await playwright.Chromium.LaunchAsync(); + var playwrightBrowser = await playwright.Chromium.LaunchAsync(); + var browser = new Browser(new PlaywrightBrowserAccessor(playwrightBrowser)) + { + TestOptions = new UITestOptions() + }; - var page = await browser.NewPageAsync(); + var page = await playwrightBrowser.NewPageAsync(); await page.GotoAsync(PageHelper.GetFullPath(pageName)); - return new Page(new PlaywrightPageAccessor(page)); + return new Page(new PlaywrightPageAccessor(page), browser); } }