Skip to content

Commit 6c9306a

Browse files
authoredJul 19, 2023
feat: add autofill support (#10565)
1 parent c14f9b6 commit 6c9306a

10 files changed

+241
-0
lines changed
 

‎docs/api/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ sidebar_label: API
6767
| Interface | Description |
6868
| --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
6969
| [ActionOptions](./puppeteer.actionoptions.md) | |
70+
| [AutofillData](./puppeteer.autofilldata.md) | |
7071
| [BoundingBox](./puppeteer.boundingbox.md) | |
7172
| [BoxModel](./puppeteer.boxmodel.md) | |
7273
| [BrowserConnectOptions](./puppeteer.browserconnectoptions.md) | Generic browser options that can be passed when launching any browser or when connecting to an existing browser instance. |

‎docs/api/puppeteer.autofilldata.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
sidebar_label: AutofillData
3+
---
4+
5+
# AutofillData interface
6+
7+
#### Signature:
8+
9+
```typescript
10+
export interface AutofillData
11+
```
12+
13+
## Properties
14+
15+
| Property | Modifiers | Type | Description | Default |
16+
| ---------- | --------- | --------------------------------------------------------------------------------------- | ----------- | ------- |
17+
| creditCard | | { number: string; name: string; expiryMonth: string; expiryYear: string; cvc: string; } | | |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
sidebar_label: ElementHandle.autofill
3+
---
4+
5+
# ElementHandle.autofill() method
6+
7+
If the element is a form input, you can use [ElementHandle.autofill()](./puppeteer.elementhandle.autofill.md) to test if the form is compatible with the browser's autofill implementation. Throws an error if the form cannot be autofilled.
8+
9+
#### Signature:
10+
11+
```typescript
12+
class ElementHandle {
13+
autofill(data: AutofillData): Promise<void>;
14+
}
15+
```
16+
17+
## Parameters
18+
19+
| Parameter | Type | Description |
20+
| --------- | ------------------------------------------- | ----------- |
21+
| data | [AutofillData](./puppeteer.autofilldata.md) | |
22+
23+
**Returns:**
24+
25+
Promise&lt;void&gt;
26+
27+
## Remarks
28+
29+
Currently, Puppeteer supports auto-filling credit card information only and in Chrome in the new headless and headful modes only.
30+
31+
```ts
32+
// Select an input on the credit card form.
33+
const name = await page.waitForSelector('form #name');
34+
// Trigger autofill with the desired data.
35+
await name.autofill({
36+
creditCard: {
37+
number: '4444444444444444',
38+
name: 'John Smith',
39+
expiryMonth: '01',
40+
expiryYear: '2030',
41+
cvc: '123',
42+
},
43+
});
44+
```

‎docs/api/puppeteer.elementhandle.md

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ The constructor for this class is marked as internal. Third-party code should no
5555
| [$eval(selector, pageFunction, args)](./puppeteer.elementhandle._eval.md) | | <p>Runs the given function on the first element matching the given selector in the current element.</p><p>If the given function returns a promise, then this method will wait till the promise resolves.</p> |
5656
| [$x(expression)](./puppeteer.elementhandle._x.md) | | |
5757
| [asElement()](./puppeteer.elementhandle.aselement.md) | | |
58+
| [autofill(data)](./puppeteer.elementhandle.autofill.md) | | If the element is a form input, you can use [ElementHandle.autofill()](./puppeteer.elementhandle.autofill.md) to test if the form is compatible with the browser's autofill implementation. Throws an error if the form cannot be autofilled. |
5859
| [boundingBox()](./puppeteer.elementhandle.boundingbox.md) | | This method returns the bounding box of the element (relative to the main frame), or <code>null</code> if the element is not visible. |
5960
| [boxModel()](./puppeteer.elementhandle.boxmodel.md) | | This method returns boxes of the element, or <code>null</code> if the element is not visible. |
6061
| [click(this, options)](./puppeteer.elementhandle.click.md) | | This method scrolls element into view if needed, and then uses [Page.mouse](./puppeteer.page.md) to click in the center of the element. If the element is detached from DOM, the method throws an error. |

‎packages/puppeteer-core/src/api/ElementHandle.ts

+41
Original file line numberDiff line numberDiff line change
@@ -1042,4 +1042,45 @@ export class ElementHandle<
10421042
assertElementHasWorld(): asserts this {
10431043
assert(this.executionContext()._world);
10441044
}
1045+
1046+
/**
1047+
* If the element is a form input, you can use {@link ElementHandle.autofill}
1048+
* to test if the form is compatible with the browser's autofill
1049+
* implementation. Throws an error if the form cannot be autofilled.
1050+
*
1051+
* @remarks
1052+
*
1053+
* Currently, Puppeteer supports auto-filling credit card information only and
1054+
* in Chrome in the new headless and headful modes only.
1055+
*
1056+
* ```ts
1057+
* // Select an input on the credit card form.
1058+
* const name = await page.waitForSelector('form #name');
1059+
* // Trigger autofill with the desired data.
1060+
* await name.autofill({
1061+
* creditCard: {
1062+
* number: '4444444444444444',
1063+
* name: 'John Smith',
1064+
* expiryMonth: '01',
1065+
* expiryYear: '2030',
1066+
* cvc: '123',
1067+
* },
1068+
* });
1069+
* ```
1070+
*/
1071+
autofill(data: AutofillData): Promise<void>;
1072+
autofill(): Promise<void> {
1073+
throw new Error('Not implemented');
1074+
}
1075+
}
1076+
1077+
export interface AutofillData {
1078+
creditCard: {
1079+
// See https://chromedevtools.github.io/devtools-protocol/tot/Autofill/#type-CreditCard.
1080+
number: string;
1081+
name: string;
1082+
expiryMonth: string;
1083+
expiryYear: string;
1084+
cvc: string;
1085+
};
10451086
}

‎packages/puppeteer-core/src/common/ElementHandle.ts

+14
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import {Protocol} from 'devtools-protocol';
1818

1919
import {
20+
AutofillData,
2021
BoundingBox,
2122
BoxModel,
2223
ClickOptions,
@@ -571,6 +572,19 @@ export class CDPElementHandle<
571572

572573
return imageData;
573574
}
575+
576+
override async autofill(data: AutofillData): Promise<void> {
577+
const nodeInfo = await this.client.send('DOM.describeNode', {
578+
objectId: this.handle.id,
579+
});
580+
const fieldId = nodeInfo.node.backendNodeId;
581+
const frameId = this.#frame._id;
582+
await this.client.send('Autofill.trigger', {
583+
fieldId,
584+
frameId,
585+
card: data.creditCard,
586+
});
587+
}
574588
}
575589

576590
function computeQuadArea(quad: Point[]): number {

‎packages/puppeteer-core/src/common/bidi/ElementHandle.ts

+15
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
1818

1919
import {
20+
AutofillData,
2021
ElementHandle as BaseElementHandle,
2122
ClickOptions,
2223
} from '../../api/ElementHandle.js';
@@ -70,6 +71,20 @@ export class ElementHandle<
7071
return;
7172
}
7273

74+
override async autofill(data: AutofillData): Promise<void> {
75+
const client = this.#frame.context().cdpSession;
76+
const nodeInfo = await client.send('DOM.describeNode', {
77+
objectId: this.handle.id,
78+
});
79+
const fieldId = nodeInfo.node.backendNodeId;
80+
const frameId = this.#frame._id;
81+
await client.send('Autofill.trigger', {
82+
fieldId,
83+
frameId,
84+
card: data.creditCard,
85+
});
86+
}
87+
7388
// ///////////////////
7489
// // Input methods //
7590
// ///////////////////

‎test/TestExpectations.json

+18
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@
55
"parameters": ["webDriverBiDi"],
66
"expectations": ["SKIP", "TIMEOUT"]
77
},
8+
{
9+
"testIdPattern": "[autofill.spec] *",
10+
"platforms": ["darwin", "linux", "win32"],
11+
"parameters": ["chrome"],
12+
"expectations": ["PASS"]
13+
},
14+
{
15+
"testIdPattern": "[autofill.spec] *",
16+
"platforms": ["darwin", "linux", "win32"],
17+
"parameters": ["firefox"],
18+
"expectations": ["FAIL"]
19+
},
820
{
921
"testIdPattern": "[chromiumonly.spec] Chromium-Specific Launcher tests *",
1022
"platforms": ["darwin", "linux", "win32"],
@@ -299,6 +311,12 @@
299311
"parameters": ["webDriverBiDi"],
300312
"expectations": ["PASS"]
301313
},
314+
{
315+
"testIdPattern": "[autofill.spec] *",
316+
"platforms": ["darwin", "linux", "win32"],
317+
"parameters": ["chrome", "headless"],
318+
"expectations": ["FAIL"]
319+
},
302320
{
303321
"testIdPattern": "[browser.spec] Browser specs Browser.isConnected should set the browser connected state",
304322
"platforms": ["darwin", "linux", "win32"],

‎test/assets/credit-card.html

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6+
</head>
7+
8+
<body>
9+
<form id="testform" method="post">
10+
<table>
11+
<tbody>
12+
<tr>
13+
<td>
14+
<label for="name">Name on Card</label>
15+
</td>
16+
<td>
17+
<input size="40" id="name" />
18+
</td>
19+
</tr>
20+
<tr>
21+
<td>
22+
<label for="number">Card Number</label>
23+
</td>
24+
<td>
25+
<input size="40" id="number" name="card_number" />
26+
</td>
27+
</tr>
28+
<tr>
29+
<td>
30+
<label>Expiration Date</label>
31+
</td>
32+
<td>
33+
<input size="2" id="expiration_month" name="ccmonth"> <input size="4" id="expiration_year"
34+
name="ccyear" />
35+
</td>
36+
</tr>
37+
</tbody>
38+
</table>
39+
<input type="submit" value="Submit">
40+
</form>
41+
</body>
42+
</html>

‎test/src/autofill.spec.ts

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Copyright 2023 Google Inc. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import expect from 'expect';
18+
19+
import {getTestState, setupTestBrowserHooks} from './mocha-utils.js';
20+
21+
describe('Autofill', function () {
22+
setupTestBrowserHooks();
23+
describe('ElementHandle.autofill', () => {
24+
it('should fill out a credit card', async () => {
25+
const {page, server} = await getTestState();
26+
await page.goto(server.PREFIX + '/credit-card.html');
27+
const name = await page.waitForSelector('#name');
28+
await name!.autofill({
29+
creditCard: {
30+
number: '4444444444444444',
31+
name: 'John Smith',
32+
expiryMonth: '01',
33+
expiryYear: '2030',
34+
cvc: '123',
35+
},
36+
});
37+
expect(
38+
await page.evaluate(() => {
39+
const result = [];
40+
for (const el of document.querySelectorAll('input')) {
41+
result.push(el.value);
42+
}
43+
return result.join(',');
44+
})
45+
).toBe('John Smith,4444444444444444,01,2030,Submit');
46+
});
47+
});
48+
});

0 commit comments

Comments
 (0)
Please sign in to comment.