diff --git a/lib/CLAUDE.md b/lib/CLAUDE.md index 864b18161..754ff46cc 100644 --- a/lib/CLAUDE.md +++ b/lib/CLAUDE.md @@ -14,6 +14,12 @@ You are allowed to run git commands to update these repositories locally. - Original Puppeteer repository: ../../puppeteer/puppeteer. Every time "upstream" is mentioned we are referring to this code. - Bidi Driver: ../../webdriverbidi-net/webdriverbidi-net +## Upstream code structure + +- Code in upstream puppeteer-core/src/api/* are our abstract class. For instance our public abstract class Frame. +- Code in upstream puppeteer-core/src/bidi/* are our Bidi* classes. +- Code in upstream puppeteer-core/src/cdp/* are our Cdp* classes. + ## Project Structure ``` @@ -380,6 +386,7 @@ Test directory structure demonstrates comprehensive coverage: - Headless mode variations (headless, headful, headless-shell) - Local and upstream expectation merging - Tests should always match the code in upstream. Tests should never be changed to match the code local code. + #### Test Server (`PuppeteerSharp.TestServer/`) - ASP.NET Core server for hosting test pages - wwwroot directory with test fixtures diff --git a/lib/PuppeteerSharp.Nunit/TestExpectations/TestExpectations.local.json b/lib/PuppeteerSharp.Nunit/TestExpectations/TestExpectations.local.json index c93e1c722..fc49af62c 100644 --- a/lib/PuppeteerSharp.Nunit/TestExpectations/TestExpectations.local.json +++ b/lib/PuppeteerSharp.Nunit/TestExpectations/TestExpectations.local.json @@ -900,21 +900,6 @@ "FAIL" ] }, - { - "comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one", - "testIdPattern": "[page.spec] *Page.addScriptTag*", - "platforms": [ - "darwin", - "linux", - "win32" - ], - "parameters": [ - "webDriverBiDi" - ], - "expectations": [ - "FAIL" - ] - }, { "comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one", "testIdPattern": "[page.spec] *addStyleTag*", diff --git a/lib/PuppeteerSharp.Tests/PageTests/AddScriptTagTests.cs b/lib/PuppeteerSharp.Tests/PageTests/AddScriptTagTests.cs index 80f805533..6a71a0010 100644 --- a/lib/PuppeteerSharp.Tests/PageTests/AddScriptTagTests.cs +++ b/lib/PuppeteerSharp.Tests/PageTests/AddScriptTagTests.cs @@ -8,10 +8,6 @@ namespace PuppeteerSharp.Tests.PageTests { public class AddScriptTagTests : PuppeteerPageBaseTest { - public AddScriptTagTests() : base() - { - } - [Test, PuppeteerTest("page.spec", "Page Page.addScriptTag", "should throw an error if no options are provided")] public void ShouldThrowAnErrorIfNoOptionsAreProvided() { diff --git a/lib/PuppeteerSharp/Bidi/BidiFrame.cs b/lib/PuppeteerSharp/Bidi/BidiFrame.cs index bc98d2e54..55884a540 100644 --- a/lib/PuppeteerSharp/Bidi/BidiFrame.cs +++ b/lib/PuppeteerSharp/Bidi/BidiFrame.cs @@ -97,9 +97,6 @@ internal BidiPage BidiPage /// public override Task AddStyleTagAsync(AddTagOptions options) => throw new System.NotImplementedException(); - /// - public override Task AddScriptTagAsync(AddTagOptions options) => throw new System.NotImplementedException(); - /// public override async Task SetContentAsync(string html, NavigationOptions options = null) { diff --git a/lib/PuppeteerSharp/Bidi/BidiRealm.cs b/lib/PuppeteerSharp/Bidi/BidiRealm.cs index ef316647d..6c503bcb1 100644 --- a/lib/PuppeteerSharp/Bidi/BidiRealm.cs +++ b/lib/PuppeteerSharp/Bidi/BidiRealm.cs @@ -214,7 +214,7 @@ private async Task EvaluateAsync(bool returnByValue, bool if (result.ResultType == EvaluateResultType.Exception) { // TODO: Improve text details - throw new EvaluateException(((EvaluateResultException)result).ExceptionDetails.Text); + throw new EvaluationFailedException(((EvaluateResultException)result).ExceptionDetails.Text); } return result as EvaluateResultSuccess; diff --git a/lib/PuppeteerSharp/Cdp/CdpFrame.cs b/lib/PuppeteerSharp/Cdp/CdpFrame.cs index e84c47d1a..d25032bef 100644 --- a/lib/PuppeteerSharp/Cdp/CdpFrame.cs +++ b/lib/PuppeteerSharp/Cdp/CdpFrame.cs @@ -243,72 +243,6 @@ public override async Task AddStyleTagAsync(AddTagOptions option return (await MainRealm.TransferHandleAsync(handle).ConfigureAwait(false)) as IElementHandle; } - /// - public override async Task AddScriptTagAsync(AddTagOptions options) - { - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - - if (string.IsNullOrEmpty(options.Url) && string.IsNullOrEmpty(options.Path) && - string.IsNullOrEmpty(options.Content)) - { - throw new ArgumentException("Provide options with a `Url`, `Path` or `Content` property"); - } - - var content = options.Content; - - if (!string.IsNullOrEmpty(options.Path)) - { - content = await AsyncFileHelper.ReadAllText(options.Path).ConfigureAwait(false); - content += "//# sourceURL=" + options.Path.Replace("\n", string.Empty); - } - - var handle = await IsolatedRealm.EvaluateFunctionHandleAsync( - @"async (puppeteerUtil, url, id, type, content) => { - const createDeferredPromise = puppeteerUtil.createDeferredPromise; - const promise = createDeferredPromise(); - const script = document.createElement('script'); - script.type = type; - script.text = content; - if (url) { - script.src = url; - script.addEventListener( - 'load', - () => { - return promise.resolve(); - }, - {once: true} - ); - script.addEventListener( - 'error', - event => { - promise.reject( - new Error(event.message ?? 'Could not load script') - ); - }, - {once: true} - ); - } else { - promise.resolve(); - } - if (id) { - script.id = id; - } - document.head.appendChild(script); - await promise; - return script; - }", - new LazyArg(async context => await context.GetPuppeteerUtilAsync().ConfigureAwait(false)), - options.Url, - options.Id, - options.Type, - content).ConfigureAwait(false); - - return (await MainRealm.TransferHandleAsync(handle).ConfigureAwait(false)) as IElementHandle; - } - internal void UpdateClient(CDPSession client, bool keepWorlds = false) { Client = client; diff --git a/lib/PuppeteerSharp/Frame.cs b/lib/PuppeteerSharp/Frame.cs index c96b3d6f6..273cae4fc 100644 --- a/lib/PuppeteerSharp/Frame.cs +++ b/lib/PuppeteerSharp/Frame.cs @@ -3,6 +3,7 @@ using System.Text.Json; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using PuppeteerSharp.Helpers; using PuppeteerSharp.Input; using PuppeteerSharp.QueryHandlers; @@ -196,14 +197,77 @@ public async Task XPathAsync(string expression) } /// - public Task WaitForDevicePromptAsync(WaitForOptions options = default) + public Task WaitForDevicePromptAsync(WaitForOptions options = null) => GetDeviceRequestPromptManager().WaitForDevicePromptAsync(options); /// public abstract Task AddStyleTagAsync(AddTagOptions options); /// - public abstract Task AddScriptTagAsync(AddTagOptions options); + public async Task AddScriptTagAsync(AddTagOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + if (string.IsNullOrEmpty(options.Url) && string.IsNullOrEmpty(options.Path) && + string.IsNullOrEmpty(options.Content)) + { + throw new ArgumentException("Provide options with a `Url`, `Path` or `Content` property"); + } + + var content = options.Content; + + if (!string.IsNullOrEmpty(options.Path)) + { + content = await AsyncFileHelper.ReadAllText(options.Path).ConfigureAwait(false); + content += "//# sourceURL=" + options.Path.Replace("\n", string.Empty); + } + + var handle = await IsolatedRealm.EvaluateFunctionHandleAsync( + @"async (puppeteerUtil, url, id, type, content) => { + const createDeferredPromise = puppeteerUtil.createDeferredPromise; + const promise = createDeferredPromise(); + const script = document.createElement('script'); + script.type = type; + script.text = content; + if (url) { + script.src = url; + script.addEventListener( + 'load', + () => { + return promise.resolve(); + }, + {once: true} + ); + script.addEventListener( + 'error', + event => { + promise.reject( + new Error(event.message ?? 'Could not load script') + ); + }, + {once: true} + ); + } else { + promise.resolve(); + } + if (id) { + script.id = id; + } + document.head.appendChild(script); + await promise; + return script; + }", + new LazyArg(async context => await context.GetPuppeteerUtilAsync().ConfigureAwait(false)), + options.Url, + options.Id, + options.Type, + content).ConfigureAwait(false); + + return (await MainRealm.TransferHandleAsync(handle).ConfigureAwait(false)) as IElementHandle; + } /// public Task GetContentAsync(GetContentOptions options = null)