Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(playwright)/new api: .clear(), .blur(), .focus() #3665

Merged
merged 35 commits into from
Jun 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4052000
chore: bringing the name of options to a general form
EgorBodnar Apr 21, 2023
eddae7c
FEATURE(new playwright api): add .blur()
EgorBodnar May 15, 2023
6d05ec9
DOC(new playwright api): add .blur()
EgorBodnar May 15, 2023
3393c76
FEATURE(new playwright api): add .blur()
EgorBodnar May 15, 2023
2436345
FEATURE(new playwright api): add .focus()
EgorBodnar May 15, 2023
1a48bd4
DOC(new playwright api): add .focus()
EgorBodnar May 15, 2023
30cff4c
FEATURE(new playwright api): add .blur()
EgorBodnar May 15, 2023
0e3e49b
chore: .focus() example extend
EgorBodnar May 15, 2023
c1bfe27
TEST(new playwright api): focus(), blur()
EgorBodnar May 15, 2023
e2e15ee
bump playwright version
EgorBodnar May 15, 2023
b1f6297
FEATURE(new playwright api): add .clear()
EgorBodnar May 15, 2023
24a2f29
DOC(new playwright api): add .clear()
EgorBodnar May 15, 2023
219eb7b
TEST(new playwright api): add .clear()
EgorBodnar May 15, 2023
1cffaee
chore: change promises in tests to async/await style
EgorBodnar May 17, 2023
2eafe60
FEATURE(new playwright api):generate both regular and promise-based h…
EgorBodnar May 17, 2023
b3ba843
FIX: use `findFields()` instead of default `_locate()` to locate an i…
EgorBodnar May 17, 2023
c358ccb
chore: replacing the old clearField method by new one
EgorBodnar May 29, 2023
bde4a38
chore: run docs script to regenerate doc. | Previous pool requests we…
EgorBodnar May 29, 2023
18a49a4
chore: remove definitions due to moving functionality to the old clea…
EgorBodnar May 29, 2023
882bfcd
CI: playwright install
EgorBodnar May 29, 2023
895d155
CI: playwright install
EgorBodnar May 29, 2023
78f35c3
FEATURE(new playwright api): add .clear() support of the old function…
EgorBodnar Jun 2, 2023
27a3eb0
TEST(new playwright api): add support of the old functionality in cas…
EgorBodnar Jun 2, 2023
c307db6
install [email protected]
kobenguyent Jun 2, 2023
9d6daa9
install [email protected]
kobenguyent Jun 2, 2023
8523d87
TEST(new playwright api): check div editable text
EgorBodnar Jun 4, 2023
2e66487
TEST(new playwright api): add support of the old functionality in cas…
EgorBodnar Jun 4, 2023
7d0d27c
FIX(new playwright api): add .clear() support of the old functionalit…
EgorBodnar Jun 4, 2023
772fb36
FEATURE(new playwright api): add .focus()
EgorBodnar Jun 4, 2023
4fd4d60
FEATURE(new playwright api): add .blur()
EgorBodnar Jun 4, 2023
0572369
DOC(new playwright api): add .focus()
EgorBodnar Jun 4, 2023
b4b06cc
TEST(new playwright api): skip test if using old version of the playw…
EgorBodnar Jun 4, 2023
fef31e5
chore: generate doc after merging
EgorBodnar Jun 4, 2023
c4efd65
FEATURE(playwright new api): temporary migrate solution before full m…
EgorBodnar Jun 4, 2023
12ac202
FEATURE(playwright new api): temporary migrate solution before full m…
EgorBodnar Jun 4, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
env:
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true
- name: Install deps
run: npx playwright install-deps
run: npm install [email protected] & npx playwright install-deps
- name: start a server
run: "php -S 127.0.0.1:8000 -t test/data/app &"
- name: run chromium tests
Expand Down
1 change: 0 additions & 1 deletion docs/helpers/Appium.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ Launch the daemon: `appium`

This helper should be configured in codecept.conf.ts or codecept.conf.js

- `appiumV2`: set this to true if you want to run with Appiumv2
- `app`: Application path. Local path or remote URL to an .ipa or .apk file, or a .zip containing one of these. Alias to desiredCapabilities.appPackage
- `host`: (default: 'localhost') Appium host
- `port`: (default: '4723') Appium port
Expand Down
335 changes: 185 additions & 150 deletions docs/helpers/Playwright.md

Large diffs are not rendered by default.

131 changes: 119 additions & 12 deletions lib/helper/Playwright.js
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,58 @@ class Playwright extends Helper {
return this._waitForAction();
}

/**
* Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the matching element.
* @param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
* @param {any} [options] [Additional options](https://playwright.dev/docs/api/class-locator#locator-focus) for available options object as 2nd argument.
*
* Examples:
*
* ```js
* I.dontSee('#add-to-cart-btn');
* I.focus('#product-tile')
* I.see('#add-to-cart-bnt');
* ```
*
*/
async focus(locator, options = {}) {
const els = await this._locate(locator);
assertElementExists(els, locator, 'Element to focus');
const el = els[0];

await el.focus(options);
return this._waitForAction();
}

/**
* Remove focus from a text input, button, etc
* Calls [blur](https://playwright.dev/docs/api/class-locator#locator-blur) on the element.
* @param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
* @param {any} [options] [Additional options](https://playwright.dev/docs/api/class-locator#locator-blur) for available options object as 2nd argument.
*
* Examples:
*
* ```js
* I.blur('.text-area')
* ```
* ```js
* //element `#product-tile` is focused
* I.see('#add-to-cart-btn');
* I.blur('#product-tile')
* I.dontSee('#add-to-cart-btn');
* ```
*
*/
async blur(locator, options = {}) {
const els = await this._locate(locator);
assertElementExists(els, locator, 'Element to blur');
// TODO: locator change required after #3677 implementation
const elXpath = await getXPathForElement(els[0]);

await this.page.locator(elXpath).blur(options);
return this._waitForAction();
}

/**
* {{> dragAndDrop }}
*
Expand Down Expand Up @@ -1285,7 +1337,7 @@ class Playwright extends Helper {
/**
* {{> click }}
*
* @param {any} [opts] [Additional options](https://playwright.dev/docs/api/class-page#page-click) for click available as 3rd argument.
* @param {any} [options] [Additional options](https://playwright.dev/docs/api/class-page#page-click) for click available as 3rd argument.
*
* Examples:
*
Expand All @@ -1298,8 +1350,8 @@ class Playwright extends Helper {
* ```
*
*/
async click(locator, context = null, opts = {}) {
return proceedClick.call(this, locator, context, opts);
async click(locator, context = null, options = {}) {
return proceedClick.call(this, locator, context, options);
}

/**
Expand Down Expand Up @@ -1480,10 +1532,35 @@ class Playwright extends Helper {
}

/**
* {{> clearField }}
* Clear the <input>, <textarea> or [contenteditable] .
* @param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
* @param {any} [options] [Additional options](https://playwright.dev/docs/api/class-locator#locator-clear) for available options object as 2nd argument.
*
* Examples:
*
* ```js
* I.clearField('.text-area')
* ```
* ```js
* I.clearField('#submit', { force: true }) // force to bypass the [actionability](https://playwright.dev/docs/actionability) checks.
* ```
*/
async clearField(field) {
return this.fillField(field, '');
async clearField(locator, options = {}) {
let result;
const isNewClearMethodPresent = typeof this.page.locator().clear === 'function';

if (isNewClearMethodPresent) {
const els = await findFields.call(this, locator);
assertElementExists(els, locator, 'Field to clear');
// TODO: locator change required after #3677 implementation
const elXpath = await getXPathForElement(els[0]);

await this.page.locator(elXpath).clear(options);
result = await this._waitForAction();
} else {
result = await this.fillField(locator, '');
}
return result;
}

/**
Expand Down Expand Up @@ -2461,15 +2538,15 @@ class Playwright extends Helper {
*
* See [Playwright's reference](https://playwright.dev/docs/api/class-page?_highlight=waitfornavi#pagewaitfornavigationoptions)
*
* @param {*} opts
* @param {*} options
*/
async waitForNavigation(opts = {}) {
opts = {
async waitForNavigation(options = {}) {
options = {
timeout: this.options.getPageTimeout,
waitUntil: this.options.waitForNavigation,
...opts,
...options,
};
return this.page.waitForNavigation(opts);
return this.page.waitForNavigation(options);
}

async waitUntilExists(locator, sec) {
Expand Down Expand Up @@ -2562,11 +2639,41 @@ function buildLocatorString(locator) {
if (locator.isCustom()) {
return `${locator.type}=${locator.value}`;
} if (locator.isXPath()) {
// dont rely on heuristics of playwright for figuring out xpath
return `xpath=${locator.value}`;
}
return locator.simplify();
}
// TODO: locator change required after #3677 implementation. Temporary solution before migration. Should be deleted after #3677 implementation
async function getXPathForElement(elementHandle) {
function calculateIndex(node) {
let index = 1;
let sibling = node.previousElementSibling;
while (sibling) {
if (sibling.tagName === node.tagName) {
index++;
}
sibling = sibling.previousElementSibling;
}
return index;
}

function generateXPath(node) {
const segments = [];
while (node && node.nodeType === Node.ELEMENT_NODE) {
if (node.hasAttribute('id')) {
segments.unshift(`*[@id="${node.getAttribute('id')}"]`);
break;
} else {
const index = calculateIndex(node);
segments.unshift(`${node.localName}[${index}]`);
node = node.parentNode;
}
}
return `//${segments.join('/')}`;
}

return elementHandle.evaluate(generateXPath);
}

async function findElements(matcher, locator) {
if (locator.react) return findReact(matcher, locator);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
"json-server": "^0.10.1",
"nightmare": "^3.0.2",
"nodemon": "^1.19.4",
"playwright": "^1.23.2",
"playwright": "^1.32.3",
"puppeteer": "^10.4.0",
"qrcode-terminal": "^0.12.0",
"rosie": "^1.6.0",
Expand Down
19 changes: 19 additions & 0 deletions test/data/app/view/form/contenteditable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>Content Editable</title>
<style>
#contenteditableDiv {
border: 1px solid #000;
padding: 10px;
width: 200px;
height: 200px;
}
</style>
</head>
<body>

<div id="contenteditableDiv" contenteditable="true">This is editable. Click here to edit this text.</div>

</body>
</html>
54 changes: 54 additions & 0 deletions test/data/app/view/form/focus_blur_elements.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<title>Test Focus and Blur</title>
<style>
.message {
display: inline-block;
margin-left: 10px;
}
</style>
</head>
<body>

<button id="button">Button</button>
<span id="buttonMessage" class="message">Button not focused</span>

<br/><br/>

<input type="text" id="field" placeholder="Type Here">
<span id="fieldMessage" class="message">Input field not focused</span>

<br/><br/>

<textarea id="textarea" placeholder="Write Here"></textarea>
<span id="textareaMessage" class="message">Textarea not focused</span>

<script>
document.getElementById('button').addEventListener('focus', function() {
document.getElementById('buttonMessage').innerText = 'Button is focused';
});

document.getElementById('button').addEventListener('blur', function() {
document.getElementById('buttonMessage').innerText = 'Button not focused';
});

document.getElementById('field').addEventListener('focus', function() {
document.getElementById('fieldMessage').innerText = 'Input field is focused';
});

document.getElementById('field').addEventListener('blur', function() {
document.getElementById('fieldMessage').innerText = 'Input field not focused';
});

document.getElementById('textarea').addEventListener('focus', function() {
document.getElementById('textareaMessage').innerText = 'Textarea is focused';
});

document.getElementById('textarea').addEventListener('blur', function() {
document.getElementById('textareaMessage').innerText = 'Textarea not focused';
});
</script>

</body>
</html>
66 changes: 66 additions & 0 deletions test/helper/Playwright_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,43 @@ describe('Playwright', function () {
.then(() => I.dontSee('Hovered', '#show')));
});

describe('#focus, #blur', () => {
it('should focus a button, field and textarea', async () => {
await I.amOnPage('/form/focus_blur_elements');

await I.focus('#button');
await I.see('Button is focused', '#buttonMessage');

await I.focus('#field');
await I.see('Button not focused', '#buttonMessage');
await I.see('Input field is focused', '#fieldMessage');

await I.focus('#textarea');
await I.see('Button not focused', '#buttonMessage');
await I.see('Input field not focused', '#fieldMessage');
await I.see('Textarea is focused', '#textareaMessage');
});

it('should blur focused button, field and textarea', async () => {
await I.amOnPage('/form/focus_blur_elements');

await I.focus('#button');
await I.see('Button is focused', '#buttonMessage');
await I.blur('#button');
await I.see('Button not focused', '#buttonMessage');

await I.focus('#field');
await I.see('Input field is focused', '#fieldMessage');
await I.blur('#field');
await I.see('Input field not focused', '#fieldMessage');

await I.focus('#textarea');
await I.see('Textarea is focused', '#textareaMessage');
await I.blur('#textarea');
await I.see('Textarea not focused', '#textareaMessage');
});
});

describe('#switchToNextTab, #switchToPreviousTab, #openNewTab, #closeCurrentTab, #closeOtherTabs, #grabNumberOfOpenTabs', () => {
it('should only have 1 tab open when the browser starts and navigates to the first page', () => I.amOnPage('/')
.then(() => I.wait(1))
Expand Down Expand Up @@ -454,6 +491,35 @@ describe('Playwright', function () {
});
});

describe('#clearField', () => {
it('should clear input', async () => {
await I.amOnPage('/form/field');
await I.fillField('Name', 'value that is cleared using I.clearField()');
await I.clearField('Name');
await I.dontSeeInField('Name', 'value that is cleared using I.clearField()');
});

it('should clear textarea', async () => {
await I.amOnPage('/form/textarea');
await I.fillField('#description', 'value that is cleared using I.clearField()');
await I.clearField('#description');
await I.dontSeeInField('#description', 'value that is cleared using I.clearField()');
});

it('should clear contenteditable', async () => {
const isClearMethodPresent = await I.usePlaywrightTo('check if new Playwright .clear() method present', async ({ page }) => {
return typeof page.locator().clear === 'function';
});
if (!isClearMethodPresent) {
this.skip();
}

await I.amOnPage('/form/contenteditable');
await I.clearField('#contenteditableDiv');
await I.dontSee('This is editable. Click here to edit this text.', '#contenteditableDiv');
});
});

describe('#pressKey, #pressKeyDown, #pressKeyUp', () => {
it('should be able to send special keys to element', async () => {
await I.amOnPage('/form/field');
Expand Down
2 changes: 2 additions & 0 deletions typings/tests/helpers/Playwright.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ playwright.click(str, str); // $ExpectType void
playwright.click(str, null, { position }); // $ExpectType void
playwright.clickLink(); // $ExpectType void
playwright.forceClick(str); // $ExpectType void
playwright.focus(str); // $ExpectType void
playwright.blur(str); // $ExpectType void
playwright.doubleClick(str); // $ExpectType void
playwright.rightClick(str); // $ExpectType void
playwright.checkOption(str); // $ExpectType void
Expand Down
2 changes: 2 additions & 0 deletions typings/tests/helpers/PlaywrightTs.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ playwright.click(str, str); // $ExpectType Promise<any>
playwright.click(str, null, { position }); // $ExpectType Promise<any>
playwright.clickLink(); // $ExpectType Promise<any>
playwright.forceClick(str); // $ExpectType Promise<any>
playwright.focus(str); // $ExpectType Promise<any>
playwright.blur(str); // $ExpectType Promise<any>
playwright.doubleClick(str); // $ExpectType Promise<any>
playwright.rightClick(str); // $ExpectType Promise<any>
playwright.checkOption(str); // $ExpectType Promise<any>
Expand Down