Skip to content

Commit

Permalink
Merge branch 'main' into delay-execution
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/OrchardCoreContrib.Testing.UI/Browser.cs
#	src/OrchardCoreContrib.Testing.UI/BrowserFactory.cs
#	src/OrchardCoreContrib.Testing.UI/Element.cs
#	src/OrchardCoreContrib.Testing.UI/IBrowser.cs
#	src/OrchardCoreContrib.Testing.UI/Page.cs
#	src/OrchardCoreContrib.Testing.UI/UITest.cs
#	src/OrchardCoreContrib.Testing.UI/UITestOfT.cs
#	test/OrchardCoreContrib.Testing.UI.Tests/BrowserFactoryTests.cs
#	test/OrchardCoreContrib.Testing.UI.Tests/BrowserTests.cs
#	test/OrchardCoreContrib.Testing.UI.Tests/ElementTests.cs
  • Loading branch information
hishamco committed May 1, 2024
2 parents bbdeaf3 + 0f1d717 commit 8ff71d8
Show file tree
Hide file tree
Showing 15 changed files with 137 additions and 102 deletions.
21 changes: 12 additions & 9 deletions src/OrchardCoreContrib.Testing.UI/Browser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,33 @@ namespace OrchardCoreContrib.Testing.UI;
/// <param name="playwrightBrowserAccessor">The <see cref="IPlaywrightBrowserAccessor"/>.</param>
/// <param name="type">The <see cref="BrowserType"/>.</param>
/// <param name="headless">Whether to run browser in headless mode.</param>
public class Browser(IPlaywrightBrowserAccessor playwrightBrowserAccessor, BrowserType type, bool headless, int delay) : IBrowser
public class Browser(IPlaywrightBrowserAccessor playwrightBrowserAccessor, int delay) : IBrowser
{
/// <inheritdoc/>
public PlaywrightBrowser InnerBrowser => playwrightBrowserAccessor.PlaywrightBrowser;

/// <inheritdoc/>
public bool Headless => headless;

/// <inheritdoc/>
public BrowserType Type => type;
public BrowserType Type { get; set; }

/// <inheritdoc/>
public int Delay => delay;

/// <inheritdoc/>
public string Version => InnerBrowser.Version;
public string Version { get; set; } = playwrightBrowserAccessor.PlaywrightBrowser.Version;

/// <inheritdoc/>
public async Task<IPage> OpenPageAsync(string url)
{
var page = await InnerBrowser.NewPageAsync();
var playwrightPage = await InnerBrowser.NewPageAsync();

await playwrightPage.GotoAsync(url);

await page.GotoAsync(url);
var page = new Page(new PlaywrightPageAccessor(playwrightPage))
{
Title = await playwrightPage.TitleAsync(),
Content = await playwrightPage.ContentAsync()
};

return new Page(this, new PlaywrightPageAccessor(page));
return page;
}
}
25 changes: 17 additions & 8 deletions src/OrchardCoreContrib.Testing.UI/BrowserFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,29 @@ public static class BrowserFactory
/// Creates a new instance of <see cref="IBrowser"/> with a given browser type.
/// </summary>
/// <param name="playwright">The <see cref="IPlaywright"/>.</param>
/// <param name="browserType">The browser type in which <see cref="IBrowser"/> will be created.</param>
/// <param name="headless">Whether the browser runs in headless mode or not.</param>
/// <param name="testOptions">The <see cref="UITestOptions"/>.</param>
/// <returns>An instance of <see cref="IBrowser"/>.</returns>
/// <exception cref="NotSupportedException"></exception>
public static async Task<IBrowser> CreateAsync(IPlaywright playwright, BrowserType browserType, bool headless, int delay)
public static async Task<IBrowser> CreateAsync(IPlaywright playwright, UITestOptions testOptions)
{
var browser = browserType switch
var browser = testOptions.BrowserType switch
{
BrowserType.Edge => _edgeBrowser ?? await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Channel = "msedge", Headless = headless }),
BrowserType.Chrome => _chromeBrowser ?? await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Headless = headless }),
BrowserType.Firefox => _fireFoxBrowser ?? await playwright.Firefox.LaunchAsync(new BrowserTypeLaunchOptions { Headless = headless }),
BrowserType.Edge => _edgeBrowser ?? await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Channel = "msedge",
Headless = testOptions.Headless
}),
BrowserType.Chrome => _chromeBrowser ?? await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless = testOptions.Headless
}),
BrowserType.Firefox => _fireFoxBrowser ?? await playwright.Firefox.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless = testOptions.Headless
}),
_ => throw new NotSupportedException()
};

return new Browser(new PlaywrightBrowserAccessor(browser), browserType, headless, delay);
return new Browser(new PlaywrightBrowserAccessor(browser)) { Type = testOptions.BrowserType };
}
}
16 changes: 11 additions & 5 deletions src/OrchardCoreContrib.Testing.UI/Element.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,26 @@ public class Element(IPage page, ILocator locator) : IElement
IPage IElement.Page => page;

/// <inheritdoc/>
public string InnerText => locator.InnerTextAsync().GetAwaiter().GetResult();
public string InnerText { get; set; }

/// <inheritdoc/>
public string InnerHtml => locator.InnerHTMLAsync().GetAwaiter().GetResult();
public string InnerHtml { get; set; }

/// <inheritdoc/>
public bool Enabled => locator.IsEnabledAsync().GetAwaiter().GetResult();
public bool Enabled { get; set; }

/// <inheritdoc/>
public bool Visible => locator.IsVisibleAsync().GetAwaiter().GetResult();
public bool Visible { get; set; }

/// <inheritdoc/>
public async Task ClickAsync() => await locator.ClickAsync(_locatorClickOptions);

/// <inheritdoc/>
public async Task TypeAsync(string text) => await locator.PressSequentiallyAsync(text, _locatorPressSequentiallyOptions);
public async Task TypeAsync(string text)
{
await locator.FillAsync(text);

InnerText = await locator.InnerTextAsync();
InnerHtml = await locator.InnerHTMLAsync();
}
}
15 changes: 4 additions & 11 deletions src/OrchardCoreContrib.Testing.UI/IBrowser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,14 @@ public interface IBrowser
public Microsoft.Playwright.IBrowser InnerBrowser { get; }

/// <summary>
/// Gets whether the browser runs on headless mode or not.
/// Gets or sets the browser type.
/// </summary>
public bool Headless { get; }
public BrowserType Type { get; set; }

/// <summary>
/// Gets the browser type.
/// Gets or sets the browser version.
/// </summary>
public BrowserType Type { get; }

public int Delay { get; }

/// <summary>
/// Gets the browser version.
/// </summary>
public string Version { get; }
public string Version { get; set; }

/// <summary>
/// Opens a page with a given URL
Expand Down
6 changes: 3 additions & 3 deletions src/OrchardCoreContrib.Testing.UI/IElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface IElement
/// <summary>
/// Gets the inner text of the element.
/// </summary>
public string InnerText { get; }
public string InnerText { get; set; }

/// <summary>
/// Gets the inner HTML of the element.
Expand All @@ -20,12 +20,12 @@ public interface IElement
/// <summary>
/// Gets whether the element is enabled.
/// </summary>
public bool Enabled { get; }
public bool Enabled { get; set; }

/// <summary>
/// Gets whether the element is visible.
/// </summary>
public bool Visible { get; }
public bool Visible { get; set; }

/// <summary>
/// Writes a given text into an element.
Expand Down
8 changes: 4 additions & 4 deletions src/OrchardCoreContrib.Testing.UI/IPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ public interface IPage
internal IBrowser Browser { get; }

/// <summary>
/// Gets the page title.
/// Gets or sets the page title.
/// </summary>
public string Title { get; }
public string Title { get; set; }

/// <summary>
/// Gets the page content in HTML format.
/// Gets or sets the page content in HTML format.
/// </summary>
public string Content { get; }
public string Content { get; set; }

/// <summary>
/// Navigates to a given URL.
Expand Down
34 changes: 20 additions & 14 deletions src/OrchardCoreContrib.Testing.UI/Page.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,43 @@ namespace OrchardCoreContrib.Testing.UI;
/// Creates an instance of <see cref="Page"/>.
/// </remarks>
/// <param name="playwrightPageAccessor">The <see cref="IPlaywrightPageAccessor"/>.</param>
public class Page(IBrowser browser, IPlaywrightPageAccessor playwrightPageAccessor) : IPage
public class Page(IPlaywrightPageAccessor playwrightPageAccessor) : IPage
{
private readonly PageClickOptions _pageClickOptions = browser.Delay == 0
? null
: new() { Delay = browser.Delay };

/// <inheritdoc/>
IBrowser IPage.Browser => browser;

/// <inheritdoc/>
public Microsoft.Playwright.IPage InnerPage => playwrightPageAccessor.PlaywrightPage;

/// <inheritdoc/>
public string Title => InnerPage.TitleAsync().GetAwaiter().GetResult();
public string Title { get; set; }

/// <inheritdoc/>
public string Content => InnerPage.ContentAsync().GetAwaiter().GetResult();
public string Content { get; set; }

/// <inheritdoc/>
public async Task GoToAsync(string url) => await InnerPage.GotoAsync(url);
public async Task GoToAsync(string url)
{
await InnerPage.GotoAsync(url);

Title = await InnerPage.TitleAsync();
Content = await InnerPage.ContentAsync();
}

/// <inheritdoc/>
public IElement FindElement(string selector)
{
var locator = InnerPage.Locator(selector);

return new Element(this, locator);
var element = new Element(locator)
{
InnerText = locator.InnerTextAsync().GetAwaiter().GetResult(),
InnerHtml = locator.InnerHTMLAsync().GetAwaiter().GetResult(),
Enabled = locator.IsEnabledAsync().GetAwaiter().GetResult(),
Visible = locator.IsVisibleAsync().GetAwaiter().GetResult()
};

return element;
}

/// <inheritdoc/>
public async Task ClickAsync(string selector) => await InnerPage.ClickAsync(selector, _pageClickOptions);
public async Task ClickAsync(string selector) => await FindElement(selector).ClickAsync();

/// <inheritdoc/>
public async Task ScreenShotAsync(string path, bool fullPage = false)
Expand Down
13 changes: 10 additions & 3 deletions src/OrchardCoreContrib.Testing.UI/UITest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Microsoft.Playwright;
using OrchardCoreContrib.Testing.UI.Infrastructure;
using Xunit;

namespace OrchardCoreContrib.Testing.UI;
Expand All @@ -9,7 +8,7 @@ namespace OrchardCoreContrib.Testing.UI;
/// </summary>
/// <param name="browserType">The browser type that will be used during the test. Defaults to <see cref="BrowserType.Edge"/>.</param>
/// <param name="headless">Whether the browser runs in headless mode or not. Defaults to <c>true</c>.</param>
public class UITest(BrowserType browserType = BrowserType.Edge, bool headless = true, int delay = 0) : IAsyncLifetime
public class UITest(BrowserType browserType = BrowserType.Edge, bool headless = true) : IAsyncLifetime
{
private IPlaywright _playwright;

Expand All @@ -18,12 +17,20 @@ public class UITest(BrowserType browserType = BrowserType.Edge, bool headless =
/// </summary>
public IBrowser Browser { get; private set; }

public UITestOptions Options { get; private set; }

/// <inheritdoc/>
public async Task InitializeAsync()
{
Options = new UITestOptions
{
BrowserType = browserType,
Headless = headless
};

_playwright = await Playwright.CreateAsync();

Browser = await BrowserFactory.CreateAsync(_playwright, browserType, headless, delay);
Browser = await BrowserFactory.CreateAsync(_playwright, Options);
}

/// <inheritdoc/>
Expand Down
2 changes: 2 additions & 0 deletions src/OrchardCoreContrib.Testing.UI/UITestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public abstract class UITestBase<TStartup>(WebApplicationFactoryFixture<TStartup
/// Gets the base URL used for the tested website.
/// </summary>
public string BaseUrl => fixture.ServerAddress;

public UITestOptions Options { get; protected set; }
}
10 changes: 8 additions & 2 deletions src/OrchardCoreContrib.Testing.UI/UITestOfT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace OrchardCoreContrib.Testing.UI;
/// <param name="browserType">The browser type that will be used during the test. Defaults to <see cref="BrowserType.Edge"/>.</param>
/// <param name="headless">Whether the browser runs in headless mode or not. Defaults to <c>true</c>.</param>
/// <typeparam name="TStartup">The startup class type that will be used as entry point.</typeparam>
public class UITest<TStartup>(BrowserType browserType = BrowserType.Edge, bool headless = true, int delay = 0) :
public class UITest<TStartup>(BrowserType browserType = BrowserType.Edge, bool headless = true) :
UITestBase<TStartup>(new WebApplicationFactoryFixture<TStartup>()),
IAsyncLifetime where TStartup : class
{
Expand All @@ -24,9 +24,15 @@ public class UITest<TStartup>(BrowserType browserType = BrowserType.Edge, bool h
/// <inheritdoc/>
public async Task InitializeAsync()
{
Options = new UITestOptions
{
BrowserType = browserType,
Headless = headless
};

_playwright = await Playwright.CreateAsync();

Browser = await BrowserFactory.CreateAsync(_playwright, browserType, headless, delay);
Browser = await BrowserFactory.CreateAsync(_playwright, Options);
}

/// <inheritdoc/>
Expand Down
17 changes: 17 additions & 0 deletions src/OrchardCoreContrib.Testing.UI/UITestOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace OrchardCoreContrib.Testing.UI;

/// <summary>
/// Represents a set of options to be used during the test.
/// </summary>
public class UITestOptions
{
/// <summary>
/// Gets or sets whether to run the browser on headless mode. Defaults <c>true</c>.
/// </summary>
public bool Headless { get; set; } = true;

/// <summary>
/// Gets or sets the browser type to run the test on. Defaults <see cref="BrowserType.Edge"/>.
/// </summary>
public BrowserType BrowserType { get; set; } = BrowserType.Edge;
}
26 changes: 16 additions & 10 deletions test/OrchardCoreContrib.Testing.UI.Tests/BrowserFactoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,44 @@ namespace OrchardCoreContrib.Testing.UI.Tests;

public class BrowserFactoryTests
{
[InlineData(BrowserType.Chrome, PlaywrightBrowserType.Chromium, false)]
[InlineData(BrowserType.Edge, PlaywrightBrowserType.Chromium, false)]
[InlineData(BrowserType.Firefox, PlaywrightBrowserType.Firefox, false)]
[InlineData(BrowserType.Chrome, PlaywrightBrowserType.Chromium, true)]
[InlineData(BrowserType.Edge, PlaywrightBrowserType.Chromium, true)]
[InlineData(BrowserType.Firefox, PlaywrightBrowserType.Firefox, true)]
[InlineData(BrowserType.Chrome, PlaywrightBrowserType.Chromium)]
[InlineData(BrowserType.Edge, PlaywrightBrowserType.Chromium)]
[InlineData(BrowserType.Firefox, PlaywrightBrowserType.Firefox)]
[Theory]
public async Task CreateBrowser(BrowserType browserType, string playwrightBrowserType, bool headless)
public async Task CreateBrowser(BrowserType browserType, string playwrightBrowserType)
{
// Arrange
var playwright = await Playwright.CreateAsync();

var testOptions = new UITestOptions
{
BrowserType = browserType
};

// Act
var browser = await BrowserFactory.CreateAsync(playwright, browserType, headless, delay: 0);
var browser = await BrowserFactory.CreateAsync(playwright, testOptions);
browser.Type = browserType;

// Assert
Assert.NotNull(browser);
Assert.Equal(browserType, browser.Type);
Assert.Equal(playwrightBrowserType, browser.InnerBrowser.BrowserType.Name);
Assert.Equal(headless, browser.Headless);
}

[Fact]
public async Task CreateBrowser_ThrowsException_WhenBrowserTypeInvalid()
{
// Arrange
var playwright = await Playwright.CreateAsync();
var testOptions = new UITestOptions
{
BrowserType = BrowserType.NotSet
};

// Act & Assert
await Assert.ThrowsAsync<NotSupportedException>(async () =>
{
await BrowserFactory.CreateAsync(playwright, BrowserType.NotSet, headless: true, delay: 0);
await BrowserFactory.CreateAsync(playwright, testOptions);
});
}
}
Loading

0 comments on commit 8ff71d8

Please sign in to comment.