PuppeteerSharp.Contrib alternatives and similar packages
Based on the "UI Automation" category.
Alternatively, view PuppeteerSharp.Contrib alternatives based on common mentions on social networks and blogs.
CodeRabbit: AI Code Reviews for Developers
* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of PuppeteerSharp.Contrib or a related project?
README
Puppeteer Sharp Contributions
Contributions to the Headless Chrome .NET API ๐๐งช
Puppeteer Sharp Contributions offers extensions to the Puppeteer Sharp API. It provides a convenient way to write readable and robust browser tests in .NET
Puppeteer Sharp is a .NET port of the official Node.JS Puppeteer API.
Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.
Content
Introduction
PuppeteerSharp
is a great library to automate the Chrome browser in .NET / C#.
Puppeteer Sharp Contributions consists of a few libraries that helps you write browser automation tests:
PuppeteerSharp.Contrib.Extensions
PuppeteerSharp.Contrib.Should
PuppeteerSharp.Contrib.PageObjects
PuppeteerSharp.Contrib.Extensions.Unsafe
PuppeteerSharp.Contrib.Should.Unsafe
These libraries contains extension methods to the Puppeteer Sharp API and they are test framework agnostic.
PuppeteerSharp.Contrib.Extensions
PuppeteerSharp.Contrib.Extensions
is a library with convenient extension methods for writing browser tests with the Puppeteer Sharp API.
:book: README: [PuppeteerSharp.Contrib.Extensions.md](PuppeteerSharp.Contrib.Extensions.md)
:boom: The sync over async versions of the extension methods has been moved to the unsafe package.
PuppeteerSharp.Contrib.Should
PuppeteerSharp.Contrib.Should
is a should assertion library for the Puppeteer Sharp API.
:book: README: [PuppeteerSharp.Contrib.Should.md](PuppeteerSharp.Contrib.Should.md)
:boom: The sync over async versions of the extension methods has been moved to the unsafe package.
PuppeteerSharp.Contrib.PageObjects
PuppeteerSharp.Contrib.PageObjects
is a library for writing browser tests using the page object pattern with the Puppeteer Sharp API.
:book: README: [PuppeteerSharp.Contrib.PageObjects.md](PuppeteerSharp.Contrib.PageObjects.md)
PuppeteerSharp.Contrib.Extensions.Unsafe
PuppeteerSharp.Contrib.Extensions.Unsafe
contains sync over async versions of the extension methods from the safe package.
:book: README: [PuppeteerSharp.Contrib.Extensions.Unsafe.md](PuppeteerSharp.Contrib.Extensions.Unsafe.md)
PuppeteerSharp.Contrib.Should.Unsafe
PuppeteerSharp.Contrib.Should.Unsafe
contains sync over async versions of the extension methods from the safe package.
:book: README: [PuppeteerSharp.Contrib.Should.Unsafe.md](PuppeteerSharp.Contrib.Should.Unsafe.md)
Samples
Sample projects are located in the samples
folder.
Examples are written with these test frameworks:
- Machine.Specifications
- MSTest
- NUnit
- SpecFlow
- Xunit
This is an example with NUnit
and PuppeteerSharp.Contrib.Should
:
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using PuppeteerSharp.Contrib.Extensions;
using PuppeteerSharp.Contrib.Should;
namespace PuppeteerSharp.Contrib.Sample
{
public class PuppeteerSharpRepoTests
{
private Browser Browser { get; set; }
[SetUp]
public async Task SetUp()
{
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
Browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true
});
}
[TearDown]
public async Task TearDown()
{
await Browser.CloseAsync();
}
[Test]
public async Task Should_be_first_search_result_on_GitHub()
{
var page = await Browser.NewPageAsync();
await page.GoToAsync("https://github.com/");
var h1 = await page.QuerySelectorAsync("h1");
await h1.ShouldHaveContentAsync("Built for developers");
var input = await page.QuerySelectorAsync("input.header-search-input");
if (await input.IsHiddenAsync()) await page.ClickAsync(".octicon-three-bars");
await page.TypeAsync("input.header-search-input", "Puppeteer Sharp");
await page.Keyboard.PressAsync("Enter");
await page.WaitForNavigationAsync();
var repositories = await page.QuerySelectorAllAsync(".repo-list-item");
Assert.IsNotEmpty(repositories);
var repository = repositories.First();
await repository.ShouldHaveContentAsync("hardkoded/puppeteer-sharp");
var text = await repository.QuerySelectorAsync("p");
await text.ShouldHaveContentAsync("Headless Chrome .NET API");
var link = await repository.QuerySelectorAsync("a");
await link.ClickAsync();
await page.WaitForNavigationAsync();
h1 = await page.QuerySelectorAsync("article > h1");
await h1.ShouldHaveContentAsync("Puppeteer Sharp");
Assert.AreEqual("https://github.com/hardkoded/puppeteer-sharp", page.Url);
}
[Test]
public async Task Should_have_successful_build_status()
{
var page = await Browser.NewPageAsync();
await page.GoToAsync("https://github.com/hardkoded/puppeteer-sharp");
var build = await page.QuerySelectorAsync("img[alt='Build status']");
await build.ClickAsync();
await page.WaitForNavigationAsync(new NavigationOptions { WaitUntil = new[] { WaitUntilNavigation.Networkidle0 } });
var success = await page.QuerySelectorAsync(".project-build.project-build-status.success");
success.ShouldExist();
}
[Test]
public async Task Should_be_up_to_date_with_the_Puppeteer_version()
{
var page = await Browser.NewPageAsync();
await page.GoToAsync("https://github.com/hardkoded/puppeteer-sharp");
var puppeteerSharpVersion = await GetLatestReleaseVersion();
await page.GoToAsync("https://github.com/GoogleChrome/puppeteer");
var puppeteerVersion = await GetLatestReleaseVersion();
Assert.AreEqual(puppeteerVersion, puppeteerSharpVersion);
async Task<string> GetLatestReleaseVersion()
{
var releases = await page.QuerySelectorWithContentAsync("a", "releases");
await releases.ClickAsync();
await page.WaitForNavigationAsync();
var latest = await page.QuerySelectorAsync(".release .release-header a");
return VersionWithoutPatch(await latest.TextContentAsync());
string VersionWithoutPatch(string version)
{
var tokens = version.Split(".".ToCharArray());
return string.Join(".", tokens.Take(2));
}
}
}
}
}
This is an example with NUnit
and PuppeteerSharp.Contrib.PageObjects
:
using System.Linq;
using System.Threading.Tasks;
using PuppeteerSharp.Contrib.Extensions;
using PuppeteerSharp.Contrib.PageObjects;
using PuppeteerSharp.Input;
namespace PuppeteerSharp.Contrib.Sample
{
public class GitHubStartPage : PageObject
{
[Selector("h1")]
public virtual Task<ElementHandle> Heading { get; }
[Selector(".HeaderMenu")]
public virtual Task<GitHubHeaderMenu> HeaderMenu { get; }
}
public class GitHubHeaderMenu : ElementObject
{
[Selector("input.header-search-input")]
public virtual Task<ElementHandle> SearchInput { get; }
public async Task<GitHubSearchPage> Search(string text)
{
var input = await SearchInput;
if (await input.IsHiddenAsync()) await Page.ClickAsync(".octicon-three-bars");
await input.TypeAsync(text);
await input.PressAsync(Key.Enter);
return await Page.WaitForNavigationAsync<GitHubSearchPage>();
}
}
public class GitHubSearchPage : PageObject
{
[Selector(".repo-list-item")]
public virtual Task<GitHubRepoListItem[]> RepoListItems { get; }
}
public class GitHubRepoListItem : ElementObject
{
[Selector("a")]
public virtual Task<ElementHandle> Link { get; }
[Selector("p")]
public virtual Task<ElementHandle> Text { get; }
}
public class GitHubRepoPage : PageObject
{
[Selector("article > h1")]
public virtual Task<ElementHandle> Heading { get; }
[Selector("img[alt='Build status']")]
public virtual Task<ElementHandle> BuildStatusBadge { get; }
public async Task<string> GetLatestReleaseVersion()
{
var releases = await Page.QuerySelectorWithContentAsync("a", "releases");
await releases.ClickAsync();
await Page.WaitForNavigationAsync();
var latest = await Page.QuerySelectorAsync(".release .release-header a");
return VersionWithoutPatch(await latest.TextContentAsync());
string VersionWithoutPatch(string version)
{
var tokens = version.Split(".".ToCharArray());
return string.Join(".", tokens.Take(2));
}
}
}
public class AppVeyorBuildPage : PageObject
{
public async Task<bool> Success()
{
var success = await Page.QuerySelectorAsync(".project-build.project-build-status.success");
return success.Exists();
}
}
}
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using PuppeteerSharp.Contrib.PageObjects;
using PuppeteerSharp.Contrib.Should;
namespace PuppeteerSharp.Contrib.Sample
{
public class PuppeteerSharpRepoPageObjectTests
{
private Browser Browser { get; set; }
[SetUp]
public async Task SetUp()
{
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
Browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true
});
}
[TearDown]
public async Task TearDown()
{
await Browser.CloseAsync();
}
[Test]
public async Task Should_be_first_search_result_on_GitHub()
{
var page = await Browser.NewPageAsync();
var startPage = await page.GoToAsync<GitHubStartPage>("https://github.com/");
var heading = await startPage.Heading;
await heading.ShouldHaveContentAsync("Built for developers");
var headerMenu = await startPage.HeaderMenu;
var searchPage = await headerMenu.Search("Puppeteer Sharp");
var repositories = await searchPage.RepoListItems;
Assert.IsNotEmpty(repositories);
var repository = repositories.First();
var text = await repository.Text;
await text.ShouldHaveContentAsync("Headless Chrome .NET API");
var link = await repository.Link;
await link.ShouldHaveContentAsync("hardkoded/puppeteer-sharp");
await link.ClickAsync();
var repoPage = await page.WaitForNavigationAsync<GitHubRepoPage>();
heading = await repoPage.Heading;
await heading.ShouldHaveContentAsync("Puppeteer Sharp");
Assert.AreEqual("https://github.com/hardkoded/puppeteer-sharp", repoPage.Page.Url);
}
[Test]
public async Task Should_have_successful_build_status()
{
var page = await Browser.NewPageAsync();
var repoPage = await page.GoToAsync<GitHubRepoPage>("https://github.com/hardkoded/puppeteer-sharp");
var badge = await repoPage.BuildStatusBadge;
await badge.ClickAsync();
var buildPage = await page.WaitForNavigationAsync<AppVeyorBuildPage>(new NavigationOptions { WaitUntil = new[] { WaitUntilNavigation.Networkidle0 } });
var success = await buildPage.Success();
Assert.True(success);
}
[Test]
public async Task Should_be_up_to_date_with_the_Puppeteer_version()
{
var page = await Browser.NewPageAsync();
var repoPage = await page.GoToAsync<GitHubRepoPage>("https://github.com/hardkoded/puppeteer-sharp");
var puppeteerSharpVersion = await repoPage.GetLatestReleaseVersion();
repoPage = await page.GoToAsync<GitHubRepoPage>("https://github.com/GoogleChrome/puppeteer");
var puppeteerVersion = await repoPage.GetLatestReleaseVersion();
Assert.AreEqual(puppeteerVersion, puppeteerSharpVersion);
}
}
}
Upgrading
Upgrading from version
1.0.0
to2.0.0
If you use the sync methods from PuppeteerSharp.Contrib.Extensions
or PuppeteerSharp.Contrib.Should
, please install:
PuppeteerSharp.Contrib.Extensions.Unsafe
PuppeteerSharp.Contrib.Should.Unsafe
Attribution
Puppeteer Sharp Contributions is standing on the shoulders of giants.
It would not exist without https://github.com/hardkoded/puppeteer-sharp and https://github.com/puppeteer/puppeteer
Inspiration and experience has been drawn from the previous usage of https://github.com/featurist/coypu and https://github.com/stirno/FluentAutomation