Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/hlaueriksson/puppeteer-sharp-contrib
Contributions to the Headless Chrome .NET API ππ§ͺ
https://github.com/hlaueriksson/puppeteer-sharp-contrib
contributions csharp puppeteer
Last synced: about 2 months ago
JSON representation
Contributions to the Headless Chrome .NET API ππ§ͺ
- Host: GitHub
- URL: https://github.com/hlaueriksson/puppeteer-sharp-contrib
- Owner: hlaueriksson
- License: mit
- Created: 2018-12-23T16:49:18.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2023-11-20T17:57:08.000Z (about 1 year ago)
- Last Synced: 2024-05-06T09:36:11.311Z (8 months ago)
- Topics: contributions, csharp, puppeteer
- Language: C#
- Homepage:
- Size: 513 KB
- Stars: 81
- Watchers: 8
- Forks: 14
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Puppeteer Sharp Contributions
[![build](https://github.com/hlaueriksson/puppeteer-sharp-contrib/actions/workflows/build.yml/badge.svg)](https://github.com/hlaueriksson/puppeteer-sharp-contrib/actions/workflows/build.yml)
[![CodeFactor](https://www.codefactor.io/repository/github/hlaueriksson/puppeteer-sharp-contrib/badge)](https://www.codefactor.io/repository/github/hlaueriksson/puppeteer-sharp-contrib)[![PuppeteerSharp.Contrib.Extensions](https://img.shields.io/nuget/v/PuppeteerSharp.Contrib.Extensions.svg?label=PuppeteerSharp.Contrib.Extensions)](https://www.nuget.org/packages/PuppeteerSharp.Contrib.Extensions)
[![PuppeteerSharp.Contrib.Should](https://img.shields.io/nuget/v/PuppeteerSharp.Contrib.Should.svg?label=PuppeteerSharp.Contrib.Should)](https://www.nuget.org/packages/PuppeteerSharp.Contrib.Should)
[![PuppeteerSharp.Contrib.PageObjects](https://img.shields.io/nuget/v/PuppeteerSharp.Contrib.PageObjects.svg?label=PuppeteerSharp.Contrib.PageObjects)](https://www.nuget.org/packages/PuppeteerSharp.Contrib.PageObjects)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](https://github.com/hardkoded/puppeteer-sharp) is a .NET port of the official Node.JS Puppeteer API.
>
> [Puppeteer](https://github.com/puppeteer/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](#introduction)
- [PuppeteerSharp.Contrib.Extensions](#puppeteersharpcontribextensions)
- [PuppeteerSharp.Contrib.Should](#puppeteersharpcontribshould)
- [PuppeteerSharp.Contrib.PageObjects](#puppeteersharpcontribpageobjects)
- [Samples](#samples)
- [Upgrading](#upgrading)
- [Attribution](#attribution)## 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`These libraries contains _extension methods_ to the Puppeteer Sharp API and they are test framework agnostic.
## PuppeteerSharp.Contrib.Extensions
[![NuGet](https://buildstats.info/nuget/PuppeteerSharp.Contrib.Extensions)](https://www.nuget.org/packages/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)
## PuppeteerSharp.Contrib.Should
[![NuGet](https://buildstats.info/nuget/PuppeteerSharp.Contrib.Should)](https://www.nuget.org/packages/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)
## PuppeteerSharp.Contrib.PageObjects
[![NuGet](https://buildstats.info/nuget/PuppeteerSharp.Contrib.PageObjects)](https://www.nuget.org/packages/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)
## Samples
Sample projects are located in the [`samples`](/samples/) folder.
Examples are written with these test frameworks:
- Machine.Specifications
- MSTest
- NUnit
- SpecFlow
- XunitThis is an example with `NUnit` and `PuppeteerSharp.Contrib.Should`:
```csharp
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using PuppeteerSharp.Contrib.Extensions;
using PuppeteerSharp.Contrib.Should;
using PuppeteerSharp.Input;namespace PuppeteerSharp.Contrib.Sample
{
public class PuppeteerSharpRepoTests
{
private IBrowser Browser { get; set; }[SetUp]
public async Task SetUp()
{
await new BrowserFetcher().DownloadAsync();
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 heading = await page.QuerySelectorAsync("main h1");
await heading.ShouldHaveContentAsync("Letβs build");var input = await page.QuerySelectorAsync("#query-builder-test");
if (await input.IsHiddenAsync())
{
await page.ClickAsync("[aria-label=\"Toggle navigation\"][data-view-component=\"true\"]");
await page.ClickAsync("[data-target=\"qbsearch-input.inputButtonText\"]");
}
await input.TypeAsync("Puppeteer Sharp");
await page.Keyboard.PressAsync(Key.Enter);
await page.WaitForSelectorAsync("[data-testid=\"results-list\"]");var repositories = await page.QuerySelectorAllAsync("[data-testid=\"results-list\"] > div");
Assert.IsNotEmpty(repositories);
var repository = repositories.First();
await repository.ShouldHaveContentAsync("hardkoded/puppeteer-sharp");
var text = await repository.QuerySelectorAsync("h3 + div");
await text.ShouldHaveContentAsync("Headless Chrome .NET API");
var link = await repository.QuerySelectorAsync("a");
await link.ClickAsync();
await page.WaitForSelectorAsync("article > h1");heading = await page.QuerySelectorAsync("article > h1");
await heading.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");
await page.ClickAsync("#actions-tab");
await page.WaitForSelectorAsync("#partial-actions-workflow-runs");var status = await page.QuerySelectorAsync(".checks-list-item-icon svg");
var label = await status.GetAttributeAsync("aria-label");
Assert.AreEqual("completed successfully", label);
}[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/puppeteer/puppeteer");
var puppeteerVersion = await GetLatestReleaseVersion();Assert.AreEqual(puppeteerVersion, puppeteerSharpVersion);
async Task GetLatestReleaseVersion()
{
var latest = await page.QuerySelectorWithContentAsync("a[href*='releases'] span", @"v?\d+\.\d\.\d");
var version = await latest.TextContentAsync();
return version.Substring(version.LastIndexOf('v') + 1);
}
}
}
}
```This is an example with `NUnit` and `PuppeteerSharp.Contrib.PageObjects`:
```csharp
using System.Threading.Tasks;
using PuppeteerSharp.Contrib.Extensions;
using PuppeteerSharp.Contrib.PageObjects;
using PuppeteerSharp.Input;namespace PuppeteerSharp.Contrib.Sample
{
public class GitHubStartPage : PageObject
{
[Selector("main h1")]
public virtual Task Heading { get; }[Selector("header")]
public virtual Task Header { get; }public async Task SearchAsync(string text)
{
var task = Page.WaitForNavigationAsync();
await (await Header).SearchAsync(text);
return await task;
}
}public class GitHubHeader : ElementObject
{
[Selector("#query-builder-test")]
public virtual Task SearchInput { get; }public async Task SearchAsync(string text)
{
var input = await SearchInput;
if (await input.IsHiddenAsync())
{
await Page.ClickAsync("[aria-label=\"Toggle navigation\"][data-view-component=\"true\"]");
await Page.ClickAsync("[data-target=\"qbsearch-input.inputButtonText\"]");
}
await input.TypeAsync(text);
await input.PressAsync(Key.Enter);
await Page.WaitForSelectorAsync("[data-testid=\"results-list\"]");
}
}public class GitHubSearchPage : PageObject
{
[Selector("[data-testid=\"results-list\"] > div")]
public virtual Task RepoListItems { get; }public async Task GotoAsync(GitHubRepoListItem repo)
{
var task = Page.WaitForNavigationAsync();
await (await repo.Link).ClickAsync();
await Page.WaitForSelectorAsync("article > h1");
return await task;
}
}public class GitHubRepoListItem : ElementObject
{
[Selector("a")]
public virtual Task Link { get; }[Selector("h3 + div")]
public virtual Task Text { get; }
}public class GitHubRepoPage : PageObject
{
[Selector("article > h1")]
public virtual Task Heading { get; }[Selector("#actions-tab")]
public virtual Task Actions { get; }public async Task GotoActionsAsync()
{
var task = Page.WaitForNavigationAsync();
await (await Actions).ClickAsync();
await Page.WaitForSelectorAsync("#partial-actions-workflow-runs");
return await task;
}public async Task GetLatestReleaseVersionAsync()
{
var latest = await Page.QuerySelectorWithContentAsync("a[href*='releases'] span", @"v?\d+\.\d\.\d");
var version = await latest.TextContentAsync();
return version.Substring(version.LastIndexOf('v') + 1);
}
}public class GitHubActionsPage : PageObject
{
public async Task GetLatestWorkflowRunStatusAsync()
{
var status = await Page.QuerySelectorAsync(".checks-list-item-icon svg");
return await status.GetAttributeAsync("aria-label");
}
}
}
``````csharp
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 IBrowser Browser { get; set; }[SetUp]
public async Task SetUp()
{
await new BrowserFetcher().DownloadAsync();
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("https://github.com/");
var heading = await startPage.Heading;
await heading.ShouldHaveContentAsync("Letβs build");var searchPage = await startPage.SearchAsync("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");var repoPage = await searchPage.GotoAsync(repository);
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("https://github.com/hardkoded/puppeteer-sharp");
var actionsPage = await repoPage.GotoActionsAsync();
var status = await actionsPage.GetLatestWorkflowRunStatusAsync();
Assert.AreEqual("completed successfully", status);
}[Test]
public async Task Should_be_up_to_date_with_the_Puppeteer_version()
{
var page = await Browser.NewPageAsync();var repoPage = await page.GoToAsync("https://github.com/puppeteer/puppeteer");
var puppeteerVersion = await repoPage.GetLatestReleaseVersionAsync();repoPage = await page.GoToAsync("https://github.com/hardkoded/puppeteer-sharp");
var puppeteerSharpVersion = await repoPage.GetLatestReleaseVersionAsync();Assert.AreEqual(puppeteerVersion, puppeteerSharpVersion);
}
}
}
```## Upgrading
> :arrow_up: Upgrading from version `5.0.0` to `6.0.0`
Since [v8.0.0](https://github.com/hardkoded/puppeteer-sharp/releases/tag/v8.0.0) of PuppeteerSharp, the public API relies on interfaces.
Therefor, change the use of:
- ~~`Page`~~ to `IPage`
- ~~`ElementHandle`~~ to `IElementHandle`**PuppeteerSharp.Contrib.PageObjects**
Since [v10.1.0](https://github.com/hardkoded/puppeteer-sharp/releases/tag/v10.1.0) of PuppeteerSharp, the `XPathAsync` and `WaitForXPathAsync` methods were replaced in favor of the `xpath/` selector handler.
Therefor, change the use of:
- ~~`[XPath]`~~ attribute to `[Selector]`
- ~~`XPathAsync`~~ method to `QuerySelectorAsync`
- ~~`WaitForXPathAsync`~~ method to `WaitForSelectorAsync`## 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