Coder Social home page Coder Social logo

puppeteer-sharp-contrib's Introduction

Puppeteer Sharp Contributions

build CodeFactor

PuppeteerSharp.Contrib.Extensions PuppeteerSharp.Contrib.Should 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 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

These libraries contains extension methods to the Puppeteer Sharp API and they are test framework agnostic.

PuppeteerSharp.Contrib.Extensions

NuGet

PuppeteerSharp.Contrib.Extensions is a library with convenient extension methods for writing browser tests with the Puppeteer Sharp API.

๐Ÿ“– README: PuppeteerSharp.Contrib.Extensions.md

PuppeteerSharp.Contrib.Should

NuGet

PuppeteerSharp.Contrib.Should is a should assertion library for the Puppeteer Sharp API.

๐Ÿ“– README: PuppeteerSharp.Contrib.Should.md

PuppeteerSharp.Contrib.PageObjects

NuGet

PuppeteerSharp.Contrib.PageObjects is a library for writing browser tests using the page object pattern with the Puppeteer Sharp API.

๐Ÿ“– README: PuppeteerSharp.Contrib.PageObjects.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;
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<string> 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:

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<IElementHandle> Heading { get; }

        [Selector("header")]
        public virtual Task<GitHubHeader> Header { get; }

        public async Task<GitHubSearchPage> SearchAsync(string text)
        {
            var task = Page.WaitForNavigationAsync<GitHubSearchPage>();
            await (await Header).SearchAsync(text);
            return await task;
        }
    }

    public class GitHubHeader : ElementObject
    {
        [Selector("#query-builder-test")]
        public virtual Task<IElementHandle> 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<GitHubRepoListItem[]> RepoListItems { get; }

        public async Task<GitHubRepoPage> GotoAsync(GitHubRepoListItem repo)
        {
            var task = Page.WaitForNavigationAsync<GitHubRepoPage>();
            await (await repo.Link).ClickAsync();
            await Page.WaitForSelectorAsync("article > h1");
            return await task;
        }
    }

    public class GitHubRepoListItem : ElementObject
    {
        [Selector("a")]
        public virtual Task<IElementHandle> Link { get; }

        [Selector("h3 + div")]
        public virtual Task<IElementHandle> Text { get; }
    }

    public class GitHubRepoPage : PageObject
    {
        [Selector("article > h1")]
        public virtual Task<IElementHandle> Heading { get; }

        [Selector("#actions-tab")]
        public virtual Task<IElementHandle> Actions { get; }

        public async Task<GitHubActionsPage> GotoActionsAsync()
        {
            var task = Page.WaitForNavigationAsync<GitHubActionsPage>();
            await (await Actions).ClickAsync();
            await Page.WaitForSelectorAsync("#partial-actions-workflow-runs");
            return await task;
        }

        public async Task<string> 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<string> GetLatestWorkflowRunStatusAsync()
        {
            var status = await Page.QuerySelectorAsync(".checks-list-item-icon svg");
            return await status.GetAttributeAsync("aria-label");
        }
    }
}
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<GitHubStartPage>("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<GitHubRepoPage>("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<GitHubRepoPage>("https://github.com/puppeteer/puppeteer");
            var puppeteerVersion = await repoPage.GetLatestReleaseVersionAsync();

            repoPage = await page.GoToAsync<GitHubRepoPage>("https://github.com/hardkoded/puppeteer-sharp");
            var puppeteerSharpVersion = await repoPage.GetLatestReleaseVersionAsync();

            Assert.AreEqual(puppeteerVersion, puppeteerSharpVersion);
        }
    }
}

Upgrading

โฌ†๏ธ Upgrading from version 5.0.0 to 6.0.0

Since 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 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

puppeteer-sharp-contrib's People

Contributors

bukowa avatar hlaueriksson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

puppeteer-sharp-contrib's Issues

Installation Guide

Hey it would be nice to add installation guide for guys new to C# (like me) ๐Ÿฅ‡

Why can't my innerText get the text๏ผŸ

Why can't my innerText get the text, run directly, the program is stuck, quick check, prompt: item.InnerText () The function evaluation requires all threads to run. String

await page.EvaluateExpressionAsync("document.querySelector(\"input[name='q']\").value=''");
await page.TypeAsync("input[name='q']", w, new PuppeteerSharp.Input.TypeOptions { Delay = 5 });
await page.Keyboard.PressAsync("Enter");
await page.WaitForNavigationAsync();
await page.WaitForTimeoutAsync(1500);
var list = await page.QuerySelectorAllAsync("#webPhrase>p");

foreach (var item in list)
{
     Console.WriteLine(item.InnerText());
}

Hi, first thank you for this wonderful framework.

To make this a bit more extraordinary, I have this feature request.
In the ElementHandleExtensions class, within JS evaluation of the content regex

  @"(element, selector, regex) => {
                    var elements = element.querySelectorAll(selector);
                    return Array.prototype.find.call(elements, function(element) {
                        return RegExp(regex).test(element.textContent);
                    });
                }",``

Is it possible to extend the with a flag object RegExp(regex,flags), to pass modifiers like case-insensitive into the regex?
Cheers
Till

does not work with PuppeteerSharp 3.0.0

e.g. ElementHandle eh = await page.QuerySelectorWithContentAsync("div> span", "Next");
results in:

error CS0012: The type 'ElementHandle' is defined in an assembly that is not referenced. You must add a reference to assembly 'PuppeteerSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

I had worked before updating PuppeteerSharp to version 3.0.0
Testet in .Net Framework 4.8

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.