Skip to content

Commit

Permalink
feat: use Puppeteer's select to change select elements (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
OrKoN committed Mar 28, 2022
1 parent ca6abad commit 0b4e052
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 50 deletions.
4 changes: 2 additions & 2 deletions docs/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ ___

#### Defined in

[SchemaUtils.ts:452](https://github.com/puppeteer/replay/blob/main/src/SchemaUtils.ts#L452)
[SchemaUtils.ts:451](https://github.com/puppeteer/replay/blob/main/src/SchemaUtils.ts#L451)

___

Expand All @@ -88,7 +88,7 @@ ___

#### Defined in

[SchemaUtils.ts:380](https://github.com/puppeteer/replay/blob/main/src/SchemaUtils.ts#L380)
[SchemaUtils.ts:379](https://github.com/puppeteer/replay/blob/main/src/SchemaUtils.ts#L379)

___

Expand Down
4 changes: 2 additions & 2 deletions docs/api/classes/PuppeteerRunnerExtension.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

#### Defined in

[PuppeteerRunnerExtension.ts:29](https://github.com/puppeteer/replay/blob/main/src/PuppeteerRunnerExtension.ts#L29)
[PuppeteerRunnerExtension.ts:36](https://github.com/puppeteer/replay/blob/main/src/PuppeteerRunnerExtension.ts#L36)

## Methods

Expand Down Expand Up @@ -166,4 +166,4 @@ ___

#### Defined in

[PuppeteerRunnerExtension.ts:50](https://github.com/puppeteer/replay/blob/main/src/PuppeteerRunnerExtension.ts#L50)
[PuppeteerRunnerExtension.ts:57](https://github.com/puppeteer/replay/blob/main/src/PuppeteerRunnerExtension.ts#L57)
107 changes: 67 additions & 40 deletions src/PuppeteerRunnerExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@
*/

import { RunnerExtension } from './RunnerExtension.js';
import { UserFlow, Step, WaitForElementStep, Selector, Key } from './Schema.js';
import {
UserFlow,
Step,
WaitForElementStep,
Selector,
Key,
ChangeStep,
} from './Schema.js';
import {
assertAllStepTypesAreHandled,
typeableInputTypes,
Expand Down Expand Up @@ -159,46 +166,12 @@ export class PuppeteerRunnerExtension extends RunnerExtension {
(el: Element) => (el as HTMLInputElement).type
);
startWaitingForEvents();
if (typeableInputTypes.has(inputType)) {
const textToType = await element.evaluate(
(el: Element, newValue: string) => {
/* c8 ignore next 13 */
const input = el as HTMLInputElement;
if (
newValue.length <= input.value.length ||
!newValue.startsWith(input.value)
) {
input.value = '';
return newValue;
}
const originalValue = input.value;
// Move cursor to the end of the common prefix.
input.value = '';
input.value = originalValue;
return newValue.substring(originalValue.length);
},
step.value
);
await element.type(textToType);
// If we type into a select element, blur and re-focus the
// element to make sure that the previously opened select
// dropdown is closed.
await element.evaluateHandle((el: Element) => {
const htmlEl = el as HTMLElement;
if (htmlEl.tagName === 'SELECT') {
htmlEl.blur();
htmlEl.focus();
}
});
if (inputType === 'select-one') {
await this.changeSelectElement(step, element);
} else if (typeableInputTypes.has(inputType)) {
await this.typeIntoElement(step, element);
} else {
await element.focus();
await element.evaluate((el: Element, value: string) => {
/* c8 ignore next 4 */
const input = el as HTMLInputElement;
input.value = value;
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
}, step.value);
await this.changeElementValue(step, element);
}
await element.dispose();
}
Expand Down Expand Up @@ -278,6 +251,59 @@ export class PuppeteerRunnerExtension extends RunnerExtension {

await assertedEventsPromise;
}

/**
* @internal
*/
async typeIntoElement(step: ChangeStep, element: ElementHandle<Element>) {
const textToType = await element.evaluate(
(el: Element, newValue: string) => {
/* c8 ignore next 13 */
const input = el as HTMLInputElement;
if (
newValue.length <= input.value.length ||
!newValue.startsWith(input.value)
) {
input.value = '';
return newValue;
}
const originalValue = input.value;
// Move cursor to the end of the common prefix.
input.value = '';
input.value = originalValue;
return newValue.substring(originalValue.length);
},
step.value
);
await element.type(textToType);
}

/**
* @internal
*/
async changeElementValue(step: ChangeStep, element: ElementHandle<Element>) {
await element.focus();
await element.evaluate((el: Element, value: string) => {
/* c8 ignore next 4 */
const input = el as HTMLInputElement;
input.value = value;
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
}, step.value);
}

/**
* @internal
*/
async changeSelectElement(step: ChangeStep, element: ElementHandle<Element>) {
await element.select(step.value);
await element.evaluateHandle((el: Element) => {
/* c8 ignore next 3 */
const htmlEl = el as HTMLElement;
htmlEl.blur();
htmlEl.focus();
});
}
}

export class PuppeteerRunnerOwningBrowserExtension extends PuppeteerRunnerExtension {
Expand Down Expand Up @@ -650,6 +676,7 @@ interface ElementHandle<ElementType extends Element>
}
): Promise<ElementHandle<Element> | null>;
asElement(): ElementHandle<ElementType> | null;
select(...args: string[]): Promise<unknown>;
}

interface CDPSession {
Expand Down
6 changes: 5 additions & 1 deletion src/PuppeteerStringifyExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,12 @@ export class PuppeteerStringifyExtension extends StringifyExtension {
#appendChangeStep(out: LineWriter, step: ChangeStep): void {
this.#appendWaitForSelector(out, step);
out.appendLine('const type = await element.evaluate(el => el.type);');
out.appendLine(`if (["select-one"].includes(type)) {`);
out.appendLine(` await element.select(${formatAsJSLiteral(step.value)});`);
out.appendLine(
`if (${JSON.stringify(Array.from(typeableInputTypes))}.includes(type)) {`
`} else if (${JSON.stringify(
Array.from(typeableInputTypes)
)}.includes(type)) {`
);
out.appendLine(` await element.type(${formatAsJSLiteral(step.value)});`);
out.appendLine('} else {');
Expand Down
1 change: 0 additions & 1 deletion src/SchemaUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export function assertAllStepTypesAreHandled(s: Step): never {

export const typeableInputTypes = new Set([
'textarea',
'select-one',
'text',
'url',
'tel',
Expand Down
8 changes: 6 additions & 2 deletions test/PuppeteerStringifyExtension_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ describe('PuppeteerStringifyExtension', () => {
const element = await waitForSelectors(["aria/Test"], targetPage, { timeout, visible: true });
await scrollIntoViewIfNeeded(element, timeout);
const type = await element.evaluate(el => el.type);
if (["textarea","select-one","text","url","tel","search","password","number","email"].includes(type)) {
if (["select-one"].includes(type)) {
await element.select("Hello World");
} else if (["textarea","text","url","tel","search","password","number","email"].includes(type)) {
await element.type("Hello World");
} else {
await element.focus();
Expand Down Expand Up @@ -148,7 +150,9 @@ describe('PuppeteerStringifyExtension', () => {
const element = await waitForSelectors(["aria/Test"], targetPage, { timeout, visible: true });
await scrollIntoViewIfNeeded(element, timeout);
const type = await element.evaluate(el => el.type);
if (["textarea","select-one","text","url","tel","search","password","number","email"].includes(type)) {
if (["select-one"].includes(type)) {
await element.select("#333333");
} else if (["textarea","text","url","tel","search","password","number","email"].includes(type)) {
await element.type("#333333");
} else {
await element.focus();
Expand Down
4 changes: 2 additions & 2 deletions test/resources/select.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<label>
Select
<select id="select">
<option value="O1">O1</option>
<option value="O2">O2</option>
<option value="O1">select option 1</option>
<option value="O2">select option 2</option>
</select>
</label>

0 comments on commit 0b4e052

Please sign in to comment.