Skip to content

Commit

Permalink
Use handle instead of globals (#2184)
Browse files Browse the repository at this point in the history
* some progress

* tiny refactors

* Refactor SetContext

* Injected: Use handle instead of globals

* fix add script code

* Rename puppeteer util

* Fix query objects tests

* Remove extra GetDocument function

* Remove unused class

* remove unused usings

* Fix another test

* code factor
  • Loading branch information
kblok committed Apr 16, 2023
1 parent 2cee48d commit 5018c83
Show file tree
Hide file tree
Showing 16 changed files with 245 additions and 512 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
</ItemGroup>
<Target Name="testCertCheck" BeforeTargets="BeforeBuild" Condition="!Exists('testCert.cer')" >
<Target Name="testCertCheck" BeforeTargets="BeforeBuild" Condition="!Exists('testCert.cer')">
<Error Text="Follow https://github.com/hardkoded/puppeteer-sharp/blob/master/CONTRIBUTING.md#getting-setup to setup a development certificate." />
</Target>
</Project>
2 changes: 1 addition & 1 deletion lib/PuppeteerSharp.Tests/ClickTests/ClickTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public async Task ShouldGracefullyFailWhenPageCloses()
newPage.Mouse.ClickAsync(1, 2));
}

[PuppeteerTest("click.spec.ts", "Page.click", "should click the button after navigation ")]
[PuppeteerTest("click.spec.ts", "Page.click", "should click the button after navigation")]
[PuppeteerFact]
public async Task ShouldClickTheButtonAfterNavigation()
{
Expand Down
4 changes: 2 additions & 2 deletions lib/PuppeteerSharp.Tests/EvaluationTests/PageEvaluateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ public async Task ShouldThrowIfUnderlyingElementWasDisposed()
var element = await Page.QuerySelectorAsync("section");
Assert.NotNull(element);
await element.DisposeAsync();
var exception = await Assert.ThrowsAsync<EvaluationFailedException>(()
var exception = await Assert.ThrowsAnyAsync<PuppeteerException>(()
=> Page.EvaluateFunctionAsync<string>("e => e.textContent", element));
Assert.Contains("JSHandle is disposed", exception.Message);
}
Expand All @@ -290,7 +290,7 @@ public async Task ShouldThrowIfElementHandlesAreFromOtherFrames()
{
await FrameUtils.AttachFrameAsync(Page, "frame1", TestConstants.EmptyPage);
var bodyHandle = await Page.FirstChildFrame().QuerySelectorAsync("body");
var exception = await Assert.ThrowsAsync<EvaluationFailedException>(()
var exception = await Assert.ThrowsAnyAsync<PuppeteerException>(()
=> Page.EvaluateFunctionAsync<string>("body => body.innerHTML", bodyHandle));
Assert.Contains("JSHandles can be evaluated only in the context they were created", exception.Message);
}
Expand Down
27 changes: 22 additions & 5 deletions lib/PuppeteerSharp.Tests/InjectedTests/InjectedTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Reflection.Metadata;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using PuppeteerSharp.Tests.Attributes;
using PuppeteerSharp.Xunit;
using Xunit;
Expand All @@ -14,15 +15,31 @@ public InjectedTests(ITestOutputHelper output) : base(output)
{
}

[PuppeteerTest("injected.spec.ts", "InjectedUtil tests", "should work")]
[PuppeteerTest("injected.spec.ts", "PuppeteerUtil tests", "should work")]
[PuppeteerFact]
public async Task ShouldWork()
{
var result = await (Page.MainFrame as Frame)
.SecondaryWorld.EvaluateFunctionAsync<bool>(@"() => {
return typeof InjectedUtil === 'object';
}");
var world = (Page.MainFrame as Frame).PuppeteerWorld;
var result = await world.EvaluateFunctionAsync<bool>(@"
PuppeteerUtil => {
return typeof PuppeteerUtil === 'object';
}",
await world.GetPuppeteerUtilAsync());
Assert.True(result);
}

[PuppeteerTest("injected.spec.ts", "createFunction tests", "should work")]
[PuppeteerFact]
public async Task CreateFunctionShouldWork()
{
var world = (Page.MainFrame as Frame).PuppeteerWorld;
var result = await (Page.MainFrame as Frame)
.PuppeteerWorld.EvaluateFunctionAsync<int>(@"({createFunction}, fnString) => {
return createFunction(fnString)(4);
}",
await world.GetPuppeteerUtilAsync(),
"() => 4");
Assert.Equal(4, result);
}
}
}
6 changes: 3 additions & 3 deletions lib/PuppeteerSharp.Tests/PageTests/AddStyleTagTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ public async Task ShouldWorkWithAUrl()
public async Task ShouldThrowAnErrorIfLoadingFromUrlFail()
{
await Page.GoToAsync(TestConstants.EmptyPage);
var exception = await Assert.ThrowsAsync<PuppeteerException>(()
var exception = await Assert.ThrowsAnyAsync<PuppeteerException>(()
=> Page.AddStyleTagAsync(new AddTagOptions { Url = "/nonexistfile.js" }));
Assert.Equal("Loading style from /nonexistfile.js failed", exception.Message);
Assert.Contains("Could not load style", exception.Message);
}

[PuppeteerTest("page.spec.ts", "Page.addStyleTag", "should work with a path")]
Expand Down Expand Up @@ -99,7 +99,7 @@ public async Task ShouldThrowWhenAddedWithContentToTheCSPPage()
public async Task ShouldThrowWhenAddedWithURLToTheCSPPage()
{
await Page.GoToAsync(TestConstants.ServerUrl + "/csp.html");
var exception = await Assert.ThrowsAsync<PuppeteerException>(
var exception = await Assert.ThrowsAnyAsync<PuppeteerException>(
() => Page.AddStyleTagAsync(new AddTagOptions
{
Url = TestConstants.CrossProcessUrl + "/injectedstyle.css"
Expand Down
43 changes: 24 additions & 19 deletions lib/PuppeteerSharp.Tests/PageTests/QueryObjectsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,32 @@ public QueryObjectsTests(ITestOutputHelper output) : base(output)
[SkipBrowserFact(skipFirefox: true)]
public async Task ShouldWork()
{
// Instantiate an object
await Page.EvaluateExpressionAsync("window.set = new Set(['hello', 'world'])");
var prototypeHandle = await Page.EvaluateExpressionHandleAsync("Set.prototype");
var objectsHandle = await Page.QueryObjectsAsync(prototypeHandle);
var count = await Page.EvaluateFunctionAsync<int>("objects => objects.length", objectsHandle);
Assert.Equal(1, count);
var values = await Page.EvaluateFunctionAsync<string[]>("objects => Array.from(objects[0].values())", objectsHandle);
Assert.Equal(new[] { "hello", "world" }, values);
}
// Create a custom class
var classHandle = await Page.EvaluateFunctionHandleAsync(@"() => {
return class CustomClass { };
}");

// Create an instance.
await Page.EvaluateFunctionAsync(@"CustomClass => {
self.customClass = new CustomClass();
}", classHandle);

// Validate only one has been added.
var prototypeHandle = await Page.EvaluateFunctionHandleAsync(@"CustomClass => {
return CustomClass.prototype;
}", classHandle);

[PuppeteerTest("page.spec.ts", "ExecutionContext.queryObjects", "should work for non-blank page")]
[SkipBrowserFact(skipFirefox: true)]
public async Task ShouldWorkForNonBlankPage()
{
// Instantiate an object
await Page.GoToAsync(TestConstants.EmptyPage);
await Page.EvaluateFunctionAsync("() => window.set = new Set(['hello', 'world'])");
var prototypeHandle = await Page.EvaluateFunctionHandleAsync("() => Set.prototype");
var objectsHandle = await Page.QueryObjectsAsync(prototypeHandle);
var count = await Page.EvaluateFunctionAsync<int>("objects => objects.length", objectsHandle);
Assert.Equal(1, count);
Assert.Equal(
1,
await Page.EvaluateFunctionAsync(@"objects => {
return objects.length;
}", objectsHandle));

// Check that instances.
Assert.True(await Page.EvaluateFunctionAsync<bool>(@"objects => {
return objects[0] === self.customClass;
}", objectsHandle));
}

[PuppeteerTest("page.spec.ts", "ExecutionContext.queryObjects", "should fail for disposed handles")]
Expand Down
4 changes: 2 additions & 2 deletions lib/PuppeteerSharp.Tests/PuppeteerSharp.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<Folder Include="EmulationTests\" />
<Folder Include="FixturesTests\" />
<Folder Include="HeadfulTests\" />
<Folder Include="InjectedTests\" />
<Folder Include="PuppeteerUtilTests\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PuppeteerSharp.TestServer\PuppeteerSharp.TestServer.csproj" />
Expand All @@ -53,6 +53,6 @@
<None Remove="Emulation\" />
<None Remove="FixturesTests\" />
<None Remove="HeadfulTests\" />
<None Remove="InjectedTests\" />
<None Remove="PuppeteerUtilTests\" />
</ItemGroup>
</Project>
6 changes: 3 additions & 3 deletions lib/PuppeteerSharp/AriaQueryHandlerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ async Task<IElementHandle> QueryOne(IElementHandle element, string selector)
return null;
}

return await ((ElementHandle)element).Frame.SecondaryWorld.AdoptBackendNodeAsync(id).ConfigureAwait(false);
return await ((ElementHandle)element).Frame.PuppeteerWorld.AdoptBackendNodeAsync(id).ConfigureAwait(false);
}

async Task<IElementHandle> WaitFor(IElementHandle root, string selector, WaitForSelectorOptions options)
{
var frame = ((ElementHandle)root).Frame;
var element = (await frame.SecondaryWorld.AdoptHandleAsync(root).ConfigureAwait(false)) as IElementHandle;
var element = (await frame.PuppeteerWorld.AdoptHandleAsync(root).ConfigureAwait(false)) as IElementHandle;

Task<IElementHandle> Func(string selector) => QueryOne(element, selector);

Expand All @@ -50,7 +50,7 @@ async Task<IElementHandle> WaitFor(IElementHandle root, string selector, WaitFor
Function = (Func<string, Task<IElementHandle>>)Func,
};

return await frame.SecondaryWorld.WaitForSelectorInPageAsync(
return await frame.PuppeteerWorld.WaitForSelectorInPageAsync(
@"(_, selector) => globalThis.ariaQuerySelector(selector)",
selector,
options,
Expand Down
8 changes: 1 addition & 7 deletions lib/PuppeteerSharp/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,8 @@ namespace PuppeteerSharp
/// </summary>
public class Connection : IDisposable, ICDPConnection
{
/// <summary>
/// Gets default web socket factory implementation.
/// </summary>
[Obsolete("Use " + nameof(WebSocketTransport) + "." + nameof(WebSocketTransport.DefaultWebSocketFactory) + " instead")]
public static readonly WebSocketFactory DefaultWebSocketFactory = WebSocketTransport.DefaultWebSocketFactory;

private readonly ILogger _logger;
private readonly TaskQueue _callbackQueue = new TaskQueue();
private readonly TaskQueue _callbackQueue = new();

private readonly ConcurrentDictionary<int, MessageTask> _callbacks;
private readonly ConcurrentDictionary<string, CDPSession> _sessions;
Expand Down
4 changes: 2 additions & 2 deletions lib/PuppeteerSharp/CustomQueriesManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ private static InternalQueryHandler MakeQueryHandler(CustomQueryHandler handler)
internalHandler.WaitFor = async (IElementHandle root, string selector, WaitForSelectorOptions options) =>
{
var frame = (root as ElementHandle).Frame;
var element = await frame.SecondaryWorld.AdoptHandleAsync(root).ConfigureAwait(false);
var element = await frame.PuppeteerWorld.AdoptHandleAsync(root).ConfigureAwait(false);
return await frame.SecondaryWorld.WaitForSelectorInPageAsync(handler.QueryOne, selector, options).ConfigureAwait(false);
return await frame.PuppeteerWorld.WaitForSelectorInPageAsync(handler.QueryOne, selector, options).ConfigureAwait(false);
};
}

Expand Down
4 changes: 2 additions & 2 deletions lib/PuppeteerSharp/ElementHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ public async Task<Stream> ScreenshotStreamAsync(ScreenshotOptions options)
public async Task<IElementHandle> WaitForSelectorAsync(string selector, WaitForSelectorOptions options = null)
{
var frame = (Frame)ExecutionContext.Frame;
var secondaryContext = await frame.SecondaryWorld.GetExecutionContextAsync().ConfigureAwait(false);
var secondaryContext = await frame.PuppeteerWorld.GetExecutionContextAsync().ConfigureAwait(false);
var adoptedRoot = await secondaryContext.AdoptElementHandleAsync(this).ConfigureAwait(false);
options ??= new WaitForSelectorOptions();
options.Root = adoptedRoot;

var handle = await frame.SecondaryWorld.WaitForSelectorAsync(selector, options).ConfigureAwait(false);
var handle = await frame.PuppeteerWorld.WaitForSelectorAsync(selector, options).ConfigureAwait(false);
await adoptedRoot.DisposeAsync().ConfigureAwait(false);
if (handle == null)
{
Expand Down
20 changes: 15 additions & 5 deletions lib/PuppeteerSharp/ExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,16 @@ private Task<RemoteObject> EvaluateExpressionInternalAsync(bool returnByValue, s
["userGesture"] = true,
});

private Task<RemoteObject> EvaluateFunctionInternalAsync(bool returnByValue, string script, params object[] args)
=> ExecuteEvaluationAsync("Runtime.callFunctionOn", new RuntimeCallFunctionOnRequest
private async Task<RemoteObject> EvaluateFunctionInternalAsync(bool returnByValue, string script, params object[] args)
=> await ExecuteEvaluationAsync("Runtime.callFunctionOn", new RuntimeCallFunctionOnRequest
{
FunctionDeclaration = $"{script}\n{_evaluationScriptSuffix}\n",
ExecutionContextId = ContextId,
Arguments = args.Select(FormatArgument),
Arguments = await Task.WhenAll(args.Select(FormatArgumentAsync).ToArray()).ConfigureAwait(false),
ReturnByValue = returnByValue,
AwaitPromise = true,
UserGesture = true,
});
}).ConfigureAwait(false);

private async Task<RemoteObject> ExecuteEvaluationAsync(string method, object args)
{
Expand Down Expand Up @@ -194,8 +194,18 @@ private async Task<RemoteObject> ExecuteEvaluationAsync(string method, object ar
}
}

private object FormatArgument(object arg)
private async Task<object> FormatArgumentAsync(object arg)
{
if (arg is TaskCompletionSource<object> tcs)
{
arg = await tcs.Task.ConfigureAwait(false);
}

if (arg is LazyArg lazyArg)
{
arg = await lazyArg(this).ConfigureAwait(false);
}

switch (arg)
{
case BigInteger big:
Expand Down
Loading

0 comments on commit 5018c83

Please sign in to comment.