From ec93189c61f19abfe9e016e4ca3205d8e96dcef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo=20Kondratiuk?= Date: Mon, 23 Feb 2026 20:15:25 -0300 Subject: [PATCH] New Feature: Add autofill support (#10565) Port upstream Puppeteer PR #10565 which adds the ability to trigger browser autofill for testing form compatibility. Adds AutofillAsync method to ElementHandle that uses the CDP Autofill.trigger command. Currently supports credit card autofill in Chrome (new headless and headful modes only). Co-Authored-By: Claude Opus 4.6 --- .../wwwroot/credit-card.html | 42 +++++++++++++++++++ .../AutofillTests/AutofillTests.cs | 36 ++++++++++++++++ lib/PuppeteerSharp/AutofillData.cs | 13 ++++++ lib/PuppeteerSharp/Bidi/BidiElementHandle.cs | 3 ++ lib/PuppeteerSharp/Cdp/CdpElementHandle.cs | 23 ++++++++++ .../Cdp/Messaging/AutofillTriggerRequest.cs | 11 +++++ lib/PuppeteerSharp/CreditCardData.cs | 34 +++++++++++++++ lib/PuppeteerSharp/ElementHandle.cs | 3 ++ lib/PuppeteerSharp/IElementHandle.cs | 13 ++++++ 9 files changed, 178 insertions(+) create mode 100644 lib/PuppeteerSharp.TestServer/wwwroot/credit-card.html create mode 100644 lib/PuppeteerSharp.Tests/AutofillTests/AutofillTests.cs create mode 100644 lib/PuppeteerSharp/AutofillData.cs create mode 100644 lib/PuppeteerSharp/Cdp/Messaging/AutofillTriggerRequest.cs create mode 100644 lib/PuppeteerSharp/CreditCardData.cs diff --git a/lib/PuppeteerSharp.TestServer/wwwroot/credit-card.html b/lib/PuppeteerSharp.TestServer/wwwroot/credit-card.html new file mode 100644 index 000000000..775326ba6 --- /dev/null +++ b/lib/PuppeteerSharp.TestServer/wwwroot/credit-card.html @@ -0,0 +1,42 @@ + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ +
+ + diff --git a/lib/PuppeteerSharp.Tests/AutofillTests/AutofillTests.cs b/lib/PuppeteerSharp.Tests/AutofillTests/AutofillTests.cs new file mode 100644 index 000000000..b1c10cfd1 --- /dev/null +++ b/lib/PuppeteerSharp.Tests/AutofillTests/AutofillTests.cs @@ -0,0 +1,36 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using PuppeteerSharp.Nunit; + +namespace PuppeteerSharp.Tests.AutofillTests +{ + public class AutofillTests : PuppeteerPageBaseTest + { + [Test, PuppeteerTest("autofill.spec", "ElementHandle.autofill", "should fill out a credit card")] + public async Task ShouldFillOutACreditCard() + { + await Page.GoToAsync(TestConstants.ServerUrl + "/credit-card.html"); + var name = await Page.WaitForSelectorAsync("#name"); + await name.AutofillAsync(new AutofillData + { + CreditCard = new CreditCardData + { + Number = "4444444444444444", + Name = "John Smith", + ExpiryMonth = "01", + ExpiryYear = "2030", + Cvc = "123", + }, + }); + + var result = await Page.EvaluateFunctionAsync(@"() => { + const result = []; + for (const el of document.querySelectorAll('input')) { + result.push(el.value); + } + return result.join(','); + }"); + Assert.That(result, Is.EqualTo("John Smith,4444444444444444,01,2030,Submit")); + } + } +} diff --git a/lib/PuppeteerSharp/AutofillData.cs b/lib/PuppeteerSharp/AutofillData.cs new file mode 100644 index 000000000..8182862b9 --- /dev/null +++ b/lib/PuppeteerSharp/AutofillData.cs @@ -0,0 +1,13 @@ +namespace PuppeteerSharp +{ + /// + /// Data for autofilling form fields. + /// + public class AutofillData + { + /// + /// Gets or sets the credit card data. + /// + public CreditCardData CreditCard { get; set; } + } +} diff --git a/lib/PuppeteerSharp/Bidi/BidiElementHandle.cs b/lib/PuppeteerSharp/Bidi/BidiElementHandle.cs index dc021c61d..05e0753f6 100644 --- a/lib/PuppeteerSharp/Bidi/BidiElementHandle.cs +++ b/lib/PuppeteerSharp/Bidi/BidiElementHandle.cs @@ -113,6 +113,9 @@ public override Task BackendNodeIdAsync() throw new PuppeteerException("BackendNodeId is not supported in the current configuration."); } + public override Task AutofillAsync(AutofillData data) + => throw new PuppeteerException("Autofill is not supported in the current configuration."); + internal async IAsyncEnumerable QueryAXTreeAsync(string name, string role) { var locator = new AccessibilityLocator { Name = name, Role = role }; diff --git a/lib/PuppeteerSharp/Cdp/CdpElementHandle.cs b/lib/PuppeteerSharp/Cdp/CdpElementHandle.cs index f4e480528..636f9d378 100644 --- a/lib/PuppeteerSharp/Cdp/CdpElementHandle.cs +++ b/lib/PuppeteerSharp/Cdp/CdpElementHandle.cs @@ -179,6 +179,29 @@ public override async Task BackendNodeIdAsync() return _backendNodeId.Value; } + /// + public override async Task AutofillAsync(AutofillData data) + { + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } + + var nodeInfo = await Client + .SendAsync("DOM.describeNode", new DomDescribeNodeRequest { ObjectId = Id, }) + .ConfigureAwait(false); + var fieldId = nodeInfo.Node.BackendNodeId.GetInt32(); + var frameId = _cdpFrame.Id; + await Client.SendAsync( + "Autofill.trigger", + new AutofillTriggerRequest + { + FieldId = fieldId, + FrameId = frameId, + Card = data.CreditCard, + }).ConfigureAwait(false); + } + /// public override string ToString() => Handle.ToString(); diff --git a/lib/PuppeteerSharp/Cdp/Messaging/AutofillTriggerRequest.cs b/lib/PuppeteerSharp/Cdp/Messaging/AutofillTriggerRequest.cs new file mode 100644 index 000000000..18a66e552 --- /dev/null +++ b/lib/PuppeteerSharp/Cdp/Messaging/AutofillTriggerRequest.cs @@ -0,0 +1,11 @@ +namespace PuppeteerSharp.Cdp.Messaging +{ + internal class AutofillTriggerRequest + { + public int FieldId { get; set; } + + public string FrameId { get; set; } + + public CreditCardData Card { get; set; } + } +} diff --git a/lib/PuppeteerSharp/CreditCardData.cs b/lib/PuppeteerSharp/CreditCardData.cs new file mode 100644 index 000000000..9758d5d39 --- /dev/null +++ b/lib/PuppeteerSharp/CreditCardData.cs @@ -0,0 +1,34 @@ +namespace PuppeteerSharp +{ + /// + /// Credit card data for autofilling. + /// See https://chromedevtools.github.io/devtools-protocol/tot/Autofill/#type-CreditCard. + /// + public class CreditCardData + { + /// + /// Gets or sets the credit card number. + /// + public string Number { get; set; } + + /// + /// Gets or sets the name on the credit card. + /// + public string Name { get; set; } + + /// + /// Gets or sets the expiry month. + /// + public string ExpiryMonth { get; set; } + + /// + /// Gets or sets the expiry year. + /// + public string ExpiryYear { get; set; } + + /// + /// Gets or sets the CVC. + /// + public string Cvc { get; set; } + } +} diff --git a/lib/PuppeteerSharp/ElementHandle.cs b/lib/PuppeteerSharp/ElementHandle.cs index 3c18cdca1..8e4404a5e 100644 --- a/lib/PuppeteerSharp/ElementHandle.cs +++ b/lib/PuppeteerSharp/ElementHandle.cs @@ -709,6 +709,9 @@ public override async ValueTask DisposeAsync() /// public abstract Task BackendNodeIdAsync(); + /// + public abstract Task AutofillAsync(AutofillData data); + /// public virtual Task ScrollIntoViewAsync() => BindIsolatedHandleAsync(handle diff --git a/lib/PuppeteerSharp/IElementHandle.cs b/lib/PuppeteerSharp/IElementHandle.cs index 41acd612f..2b5b16697 100644 --- a/lib/PuppeteerSharp/IElementHandle.cs +++ b/lib/PuppeteerSharp/IElementHandle.cs @@ -342,5 +342,18 @@ public interface IElementHandle : IJSHandle /// /// A task that resolves to the backend node ID. Task BackendNodeIdAsync(); + + /// + /// If the element is a form input, you can use + /// to test if the form is compatible with the browser's autofill implementation. + /// Throws an error if the form cannot be autofilled. + /// + /// The autofill data. + /// A task that resolves when the autofill is complete. + /// + /// Currently, Puppeteer supports auto-filling credit card information only and + /// in Chrome in the new headless and headful modes only. + /// + Task AutofillAsync(AutofillData data); } }