Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce protocol in launch options #2578

Merged
merged 5 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 34 additions & 2 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ env:

jobs:
build:
name: build-${{ matrix.browser }}-${{ matrix.mode }}-${{ matrix.os }}
name: build-${{ matrix.browser }}-${{ matrix.mode }}-${{ matrix.os }}-${{ matrix.protocol }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
Expand All @@ -29,33 +29,63 @@ jobs:
- os: ubuntu-latest
browser: CHROME
mode: headless
protocol: cdp
- os: ubuntu-latest
browser: CHROME
mode: headful
protocol: cdp
- os: ubuntu-latest
browser: CHROME
mode: headless-shell
protocol: cdp
- os: ubuntu-latest
browser: FIREFOX
mode: headless
protocol: cdp
- os: ubuntu-latest
browser: FIREFOX
mode: headful
protocol: cdp
- os: windows-latest
browser: CHROME
mode: headless
protocol: cdp
- os: windows-latest
browser: CHROME
mode: headful
protocol: cdp
- os: windows-latest
browser: CHROME
mode: headless-shell
protocol: cdp
- os: windows-latest
browser: FIREFOX
mode: headless
protocol: cdp
- os: windows-latest
browser: FIREFOX
mode: headful
protocol: cdp
- os: ubuntu-latest
browser: CHROME
mode: headless
protocol: webdriverbidi
- os: ubuntu-latest
browser: CHROME
mode: headful
protocol: webdriverbidi
- os: ubuntu-latest
browser: CHROME
mode: headless-shell
protocol: webdriverbidi
- os: ubuntu-latest
browser: FIREFOX
mode: headless
protocol: webdriverbidi
- os: ubuntu-latest
browser: FIREFOX
mode: headful
protocol: webdriverbidi
steps:
- uses: actions/checkout@v3
- name: Setup .NET Core
Expand Down Expand Up @@ -83,7 +113,7 @@ jobs:
New-SelfSignedCertificate -Subject "localhost" -FriendlyName "Puppeteer" -CertStoreLocation "cert:\CurrentUser\My"
Get-ChildItem -Path cert:\CurrentUSer\my | where { $_.friendlyname -eq "Puppeteer" } | Export-Certificate -FilePath $env:GITHUB_WORKSPACE\lib\PuppeteerSharp.TestServer\testCert.cer
- name: Check formatting
if: ${{ matrix.os == 'ubuntu-latest' && matrix.browser == 'CHROME' && matrix.mode == 'headless' }}
if: ${{ matrix.os == 'ubuntu-latest' && matrix.browser == 'CHROME' && matrix.mode == 'headless' && matrix.protocol == 'cdp' }}
run: dotnet format ./lib/PuppeteerSharp.sln --verify-no-changes
- name: Build
working-directory: lib
Expand All @@ -93,6 +123,7 @@ jobs:
env:
PRODUCT: ${{ matrix.browser }}
HEADLESS_MODE: ${{ matrix.mode }}
PROTOCOL: ${{ matrix.protocol }}
run: |
Xvfb :1 -screen 5 1024x768x8 &
export DISPLAY=:1.5
Expand All @@ -103,6 +134,7 @@ jobs:
env:
PRODUCT: ${{ matrix.browser }}
HEADLESS_MODE: ${{ matrix.mode }}
PROTOCOL: ${{ matrix.protocol }}
run: |
cd .\lib\PuppeteerSharp.Tests
dotnet test -s test.runsettings --blame-hang-timeout 300000
Expand Down
3 changes: 1 addition & 2 deletions lib/PuppeteerSharp.Nunit/PuppeteerTestAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ public class PuppeteerTestAttribute : NUnitAttribute, IApplyToTest
private static TestExpectation[] _localExpectations;
private static TestExpectation[] _upstreamExpectations;
public static readonly bool IsChrome = Environment.GetEnvironmentVariable("PRODUCT") != "FIREFOX";
// TODO: Change implementation when we implement Webdriver Bidi
public static readonly bool IsCdp = true;
public static readonly bool IsCdp = Environment.GetEnvironmentVariable("PROTOCOL") != "webdriverbidi";
public static readonly HeadlessMode Headless =
string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HEADLESS_MODE")) ?
(System.Diagnostics.Debugger.IsAttached ? HeadlessMode.False : HeadlessMode.True) :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ public async Task ShouldWorkAcrossSessions()

var remoteBrowser = await Puppeteer.ConnectAsync(new ConnectOptions
{
BrowserWSEndpoint = Browser.WebSocketEndpoint
BrowserWSEndpoint = Browser.WebSocketEndpoint,
Protocol = ((Browser)Browser).Protocol,
});
var contexts = remoteBrowser.BrowserContexts();
Assert.AreEqual(2, contexts.Length);
Expand Down
7 changes: 2 additions & 5 deletions lib/PuppeteerSharp.Tests/BrowserTests/IsConnectedTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@ namespace PuppeteerSharp.Tests.BrowserTests
{
public class IsConnectedTests : PuppeteerBrowserBaseTest
{
public IsConnectedTests() : base()
{
}

[Test, Retry(2), PuppeteerTest("browser.spec", "Browser.isConnected", "should set the browser connected state")]
public async Task ShouldSetTheBrowserConnectedState()
{
var newBrowser = await Puppeteer.ConnectAsync(new ConnectOptions
{
BrowserWSEndpoint = Browser.WebSocketEndpoint
BrowserWSEndpoint = Browser.WebSocketEndpoint,
Protocol = ((Browser)Browser).Protocol,
});
Assert.True(newBrowser.IsConnected);
newBrowser.Disconnect();
Expand Down
9 changes: 6 additions & 3 deletions lib/PuppeteerSharp.Tests/BrowserTests/ProcessTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ namespace PuppeteerSharp.Tests.BrowserTests
{
public class ProcessTests : PuppeteerBrowserBaseTest
{
public ProcessTests() : base() { }

[Test, Retry(2), PuppeteerTest("browser.spec", "Browser.process", "should return child_process instance")]
public void ShouldReturnProcessInstance()
{
Expand All @@ -20,7 +18,12 @@ public async Task ShouldNotReturnChildProcessForRemoteBrowser()
{
var browserWSEndpoint = Browser.WebSocketEndpoint;
var remoteBrowser = await Puppeteer.ConnectAsync(
new ConnectOptions { BrowserWSEndpoint = browserWSEndpoint }, TestConstants.LoggerFactory);
new ConnectOptions
{
BrowserWSEndpoint = browserWSEndpoint,
Protocol = ((Browser)Browser).Protocol,
},
TestConstants.LoggerFactory);
Assert.Null(remoteBrowser.Process);
remoteBrowser.Disconnect();
}
Expand Down
32 changes: 15 additions & 17 deletions lib/PuppeteerSharp.Tests/LauncherTests/BrowserCloseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,28 @@ namespace PuppeteerSharp.Tests.BrowserTests.Events
{
public class BrowserCloseTests : PuppeteerBrowserBaseTest
{
public BrowserCloseTests() : base()
{
}

[Test, Retry(2), PuppeteerTest("launcher.spec", "Launcher specs Browser.close", "should terminate network waiters")]
public async Task ShouldTerminateNetworkWaiters()
{
await using (var browser = await Puppeteer.LaunchAsync(TestConstants.DefaultBrowserOptions()))
await using (var remote = await Puppeteer.ConnectAsync(new ConnectOptions { BrowserWSEndpoint = browser.WebSocketEndpoint }))
await using var browser = await Puppeteer.LaunchAsync(TestConstants.DefaultBrowserOptions());
await using var remote = await Puppeteer.ConnectAsync(new ConnectOptions
{
var newPage = await remote.NewPageAsync();
var requestTask = newPage.WaitForRequestAsync(TestConstants.EmptyPage);
var responseTask = newPage.WaitForResponseAsync(TestConstants.EmptyPage);
BrowserWSEndpoint = browser.WebSocketEndpoint,
Protocol = ((Browser)browser).Protocol,
});
var newPage = await remote.NewPageAsync();
var requestTask = newPage.WaitForRequestAsync(TestConstants.EmptyPage);
var responseTask = newPage.WaitForResponseAsync(TestConstants.EmptyPage);

await browser.CloseAsync();
await browser.CloseAsync();

var exception = Assert.ThrowsAsync<TargetClosedException>(() => requestTask);
StringAssert.Contains("Target closed", exception.Message);
StringAssert.DoesNotContain("Timeout", exception.Message);
var exception = Assert.ThrowsAsync<TargetClosedException>(() => requestTask);
StringAssert.Contains("Target closed", exception.Message);
StringAssert.DoesNotContain("Timeout", exception.Message);

exception = Assert.ThrowsAsync<TargetClosedException>(() => responseTask);
StringAssert.Contains("Target closed", exception.Message);
StringAssert.DoesNotContain("Timeout", exception.Message);
}
exception = Assert.ThrowsAsync<TargetClosedException>(() => responseTask);
StringAssert.Contains("Target closed", exception.Message);
StringAssert.DoesNotContain("Timeout", exception.Message);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public async Task ShouldRejectNavigationWhenBrowserCloses()
await using var browser = await Puppeteer.LaunchAsync(TestConstants.DefaultBrowserOptions());
var remote = await Puppeteer.ConnectAsync(new ConnectOptions
{
BrowserWSEndpoint = browser.WebSocketEndpoint
BrowserWSEndpoint = browser.WebSocketEndpoint,
Protocol = ((Browser)Browser).Protocol,
});
var page = await remote.NewPageAsync();
var navigationTask = page.GoToAsync(TestConstants.ServerUrl + "/one-style.html", new NavigationOptions
Expand All @@ -41,7 +42,8 @@ public async Task ShouldRejectWaitForSelectorWhenBrowserCloses()
await using var browser = await Puppeteer.LaunchAsync(TestConstants.DefaultBrowserOptions());
var remote = await Puppeteer.ConnectAsync(new ConnectOptions
{
BrowserWSEndpoint = browser.WebSocketEndpoint
BrowserWSEndpoint = browser.WebSocketEndpoint,
Protocol = ((Browser)browser).Protocol,
});
var page = await remote.NewPageAsync();
var watchdog = page.WaitForSelectorAsync("div", new WaitForSelectorOptions { Timeout = 60000 });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ public BrowserEventsDisconnectedTests() : base()
public async Task ShouldEmittedWhenBrowserGetsClosedDisconnectedOrUnderlyingWebsocketGetsClosed()
{
var originalBrowser = await Puppeteer.LaunchAsync(TestConstants.DefaultBrowserOptions(), TestConstants.LoggerFactory);
var connectOptions = new ConnectOptions { BrowserWSEndpoint = originalBrowser.WebSocketEndpoint };
var connectOptions = new ConnectOptions
{
BrowserWSEndpoint = originalBrowser.WebSocketEndpoint,
Protocol = ((Browser)Browser).Protocol,
};
var remoteBrowser1 = await Puppeteer.ConnectAsync(connectOptions, TestConstants.LoggerFactory);
var remoteBrowser2 = await Puppeteer.ConnectAsync(connectOptions, TestConstants.LoggerFactory);

Expand Down
65 changes: 32 additions & 33 deletions lib/PuppeteerSharp.Tests/LauncherTests/PuppeteerConnectTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections.Features;
Expand All @@ -11,16 +10,13 @@ namespace PuppeteerSharp.Tests.LauncherTests
{
public class PuppeteerConnectTests : PuppeteerBrowserBaseTest
{
public PuppeteerConnectTests() : base()
{
}

[Test, Retry(2), PuppeteerTest("launcher.spec", "Launcher specs Puppeteer Puppeteer.connect", "should be able to connect multiple times to the same browser")]
public async Task ShouldBeAbleToConnectMultipleTimesToSameBrowser()
{
var options = new ConnectOptions()
{
BrowserWSEndpoint = Browser.WebSocketEndpoint
BrowserWSEndpoint = Browser.WebSocketEndpoint,
Protocol = ((Browser)Browser).Protocol,
};
var browser = await Puppeteer.ConnectAsync(options, TestConstants.LoggerFactory);
await using (var page = await browser.NewPageAsync())
Expand Down Expand Up @@ -55,30 +51,29 @@ public async Task ShouldBeAbleToCloseRemoteBrowser()
[Test, Retry(2), PuppeteerTest("launcher.spec", "Launcher specs Puppeteer Puppeteer.connect", "should support ignoreHTTPSErrors option")]
public async Task ShouldSupportIgnoreHTTPSErrorsOption()
{
await using (var originalBrowser = await Puppeteer.LaunchAsync(TestConstants.DefaultBrowserOptions()))
await using (var browser = await Puppeteer.ConnectAsync(new ConnectOptions
await using var originalBrowser = await Puppeteer.LaunchAsync(TestConstants.DefaultBrowserOptions());
await using var browser = await Puppeteer.ConnectAsync(new ConnectOptions
{
BrowserWSEndpoint = originalBrowser.WebSocketEndpoint,
IgnoreHTTPSErrors = true
}))
await using (var page = await browser.NewPageAsync())
{
var requestTask = HttpsServer.WaitForRequest(
"/empty.html",
request => request?.HttpContext?.Features?.Get<ITlsHandshakeFeature>()?.Protocol);
var responseTask = page.GoToAsync(TestConstants.HttpsPrefix + "/empty.html");

await Task.WhenAll(
requestTask,
responseTask).WithTimeout(Puppeteer.DefaultTimeout);

var response = responseTask.Result;
Assert.True(response.Ok);
Assert.NotNull(response.SecurityDetails);
Assert.AreEqual(
TestUtils.CurateProtocol(requestTask.Result.ToString()),
TestUtils.CurateProtocol(response.SecurityDetails.Protocol));
}
IgnoreHTTPSErrors = true,
Protocol = ((Browser)Browser).Protocol,
});
await using var page = await browser.NewPageAsync();
var requestTask = HttpsServer.WaitForRequest(
"/empty.html",
request => request?.HttpContext.Features.Get<ITlsHandshakeFeature>()?.Protocol);
var responseTask = page.GoToAsync(TestConstants.HttpsPrefix + "/empty.html");

await Task.WhenAll(
requestTask,
responseTask).WithTimeout(Puppeteer.DefaultTimeout);

var response = responseTask.Result;
Assert.True(response.Ok);
Assert.NotNull(response.SecurityDetails);
Assert.AreEqual(
TestUtils.CurateProtocol(requestTask.Result.ToString()),
TestUtils.CurateProtocol(response.SecurityDetails.Protocol));
}

[Test, Retry(2), PuppeteerTest("launcher.spec", "Launcher specs Puppeteer Puppeteer.connect", "should support targetFilter option")]
Expand All @@ -94,7 +89,8 @@ public async Task ShouldSupportTargetFilter()
var remoteBrowser = await Puppeteer.ConnectAsync(new ConnectOptions
{
BrowserWSEndpoint = browser.WebSocketEndpoint,
TargetFilter = (Target target) => !target.Url.Contains("should-be-ignored"),
TargetFilter = target => !target.Url.Contains("should-be-ignored"),
Protocol = ((Browser)browser).Protocol,
}, TestConstants.LoggerFactory);

var pages = await remoteBrowser.PagesAsync();
Expand All @@ -105,7 +101,7 @@ public async Task ShouldSupportTargetFilter()
"about:blank",
TestConstants.EmptyPage
},
pages.Select((IPage p) => p.Url).OrderBy(t => t));
pages.Select(p => p.Url).OrderBy(t => t));

await page2.CloseAsync();
await page1.CloseAsync();
Expand Down Expand Up @@ -136,7 +132,8 @@ public async Task ShouldBeAbleToReconnectToADisconnectedBrowser()
{
var options = new ConnectOptions()
{
BrowserWSEndpoint = Browser.WebSocketEndpoint
BrowserWSEndpoint = Browser.WebSocketEndpoint,
Protocol = ((Browser)Browser).Protocol,
};

var url = TestConstants.ServerUrl + "/frames/nested-frames.html";
Expand All @@ -161,7 +158,8 @@ public async Task ShouldBeAbleToConnectToTheSamePageSimultaneously()
var browserOne = await Puppeteer.LaunchAsync(new LaunchOptions());
var browserTwo = await Puppeteer.ConnectAsync(new ConnectOptions
{
BrowserWSEndpoint = browserOne.WebSocketEndpoint
BrowserWSEndpoint = browserOne.WebSocketEndpoint,
Protocol = ((Browser)browserOne).Protocol,
});
var tcs = new TaskCompletionSource<IPage>();
async void TargetCreated(object sender, TargetChangedArgs e)
Expand Down Expand Up @@ -192,7 +190,8 @@ public async Task ShouldBeAbleToReconnect()

var browserTwo = await Puppeteer.ConnectAsync(new ConnectOptions
{
BrowserWSEndpoint = browserWSEndpoint
BrowserWSEndpoint = browserWSEndpoint,
Protocol = ((Browser)browserOne).Protocol,
});

var pages = await browserTwo.PagesAsync();
Expand Down
2 changes: 2 additions & 0 deletions lib/PuppeteerSharp.Tests/TestConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static LaunchOptions DefaultBrowserOptions() => new()
EnqueueAsyncMessages = Convert.ToBoolean(Environment.GetEnvironmentVariable("ENQUEUE_ASYNC_MESSAGES") ?? "false"),
Timeout = 0,
LogProcess = true,
Protocol = PuppeteerTestAttribute.IsCdp ? ProtocolType.Cdp : ProtocolType.WebdriverBiDi,
#if NETCOREAPP
EnqueueTransportMessages = false
#else
Expand All @@ -58,6 +59,7 @@ public static LaunchOptions DefaultBrowserOptions() => new()
public static LaunchOptions BrowserWithExtensionOptions() => new()
{
Headless = false,
Protocol = PuppeteerTestAttribute.IsCdp ? ProtocolType.Cdp : ProtocolType.WebdriverBiDi,
Args = new[]
{
$"--disable-extensions-except={ExtensionPath.Quote()}",
Expand Down
4 changes: 3 additions & 1 deletion lib/PuppeteerSharp/Browser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Threading.Tasks;
using PuppeteerSharp.Cdp;
using PuppeteerSharp.Cdp.Messaging;
using PuppeteerSharp.Helpers;

namespace PuppeteerSharp
Expand Down Expand Up @@ -67,6 +67,8 @@ public abstract class Browser : IBrowser

internal Func<Target, bool> IsPageTargetFunc { get; set; }

internal abstract ProtocolType Protocol { get; }

/// <inheritdoc/>
public abstract Task<IPage> NewPageAsync();

Expand Down