diff --git a/Examples/quick-start.robot b/Examples/quick-start.robot index 2658dce..fc1e28b 100644 --- a/Examples/quick-start.robot +++ b/Examples/quick-start.robot @@ -11,7 +11,9 @@ Example login form submit Maximize Browser Window Input text id:username_field demo Input text id:password_field mode - Click Element id:login_button + Run Async Keywords + ... Click Element id:login_button AND + ... Wait For Response Url http://127.0.0.1:7272/welcome.html Wait Until Page Contains Login succeeded # Logout and wait for homepage loaded Run Async Keywords diff --git a/PuppeteerLibrary/__init__.py b/PuppeteerLibrary/__init__.py index 24b9b42..076a324 100644 --- a/PuppeteerLibrary/__init__.py +++ b/PuppeteerLibrary/__init__.py @@ -36,9 +36,6 @@ class PuppeteerLibrary(DynamicCore): | link | Exact text a link has. | ``link:Home page`` | | partial link | Partial link text | ``partial link:Home`` | - == Asynchronous Handler == - Core functionality for - """ ROBOT_LIBRARY_SCOPE = 'GLOBAL' diff --git a/PuppeteerLibrary/keywords/browsermanagement.py b/PuppeteerLibrary/keywords/browsermanagement.py index 059ead4..2ba8c46 100644 --- a/PuppeteerLibrary/keywords/browsermanagement.py +++ b/PuppeteerLibrary/keywords/browsermanagement.py @@ -26,8 +26,8 @@ def open_browser(self, url, browser="chrome", alias=None, options=None): Example: - | &{options} = | create dictionary | headless=${False} | - | Open browser | https://www.w3schools.com/html/html_forms.asp | options=${options} | + | &{options} = | create dictionary | headless=${False} | + | `Open browser` | https://www.w3schools.com/html/html_forms.asp | options=${options} | """ async def open_browser_async(): @@ -104,6 +104,14 @@ async def reload_page_async(): @keyword def wait_for_new_window_open(self, timeout=5): + """ + Waits until new page or tab opens. + + Example: + + | Run Async Keywords | Click Element | id:view_conditions | AND | + | ... | `Wait For New Window Open` | | | + """ async def wait_for_new_page_open_async(): pages = await self.ctx.get_browser().pages() await pages[-1].title() # workaround for force pages re-cache diff --git a/PuppeteerLibrary/keywords/element.py b/PuppeteerLibrary/keywords/element.py index 240fd6b..f9e059a 100644 --- a/PuppeteerLibrary/keywords/element.py +++ b/PuppeteerLibrary/keywords/element.py @@ -10,25 +10,61 @@ def __init__(self, ctx): self.async_func = ElementKeywordsAsync(self.ctx) @keyword - def click_element(self, selenium_locator): - return self.loop.run_until_complete(self.async_func.click_element_async(selenium_locator)) + def click_element(self, locator): + """Clicks element identified by ``locator``. + + Example: + + | `Click Element` | id:register | + """ + return self.loop.run_until_complete(self.async_func.click_element_async(locator)) @keyword - def click_link(self, selenium_locator): - return self.loop.run_until_complete(self.async_func.click_link_async(selenium_locator)) + def click_link(self, locator): + """Clicks link identified by ``locator``. + + Example: + + | `Click Link` | id:view_more | + """ + return self.loop.run_until_complete(self.async_func.click_link_async(locator)) @keyword - def click_button(self, selenium_locator): - self.loop.run_until_complete(self.async_func.click_button_async(selenium_locator)) + def click_button(self, locator): + """Clicks button identified by ``locator``. + + Example: + + | `Click Button` | id:submit | + """ + self.loop.run_until_complete(self.async_func.click_button_async(locator)) @keyword - def click_image(self, selenium_locator): - self.loop.run_until_complete(self.async_func.click_image_async(selenium_locator)) + def click_image(self, locator): + """Clicks image identified by ``locator``. + + Example: + + | `Click Image` | id:cat_image | + """ + self.loop.run_until_complete(self.async_func.click_image_async(locator)) @keyword - def get_text(self, selenium_locator): - return self.loop.run_until_complete(self.async_func.get_text_async(selenium_locator)) + def get_text(self, locator): + """Returns text value of element identified by ``locator``. + + Example: + + | ${text} | `Get Text` | id:username | + """ + return self.loop.run_until_complete(self.async_func.get_text_async(locator)) @keyword - def get_value(self, selenium_locator): - return self.loop.run_until_complete(self.async_func.get_text_async(selenium_locator)) + def get_value(self, locator): + """Returns specific attribute value of element identified by ``locator``. + + Example: + + | ${value} | `Get Value` | id:comment | + """ + return self.loop.run_until_complete(self.async_func.get_text_async(locator)) diff --git a/PuppeteerLibrary/keywords/formelement.py b/PuppeteerLibrary/keywords/formelement.py index 5002f60..8191f59 100644 --- a/PuppeteerLibrary/keywords/formelement.py +++ b/PuppeteerLibrary/keywords/formelement.py @@ -10,10 +10,26 @@ def __init__(self, ctx): self.async_func = FormElementKeywordsAsync(self.ctx) @keyword - def input_text(self, selenium_locator, text, clear=True): - self.loop.run_until_complete(self.async_func.input_text_async(selenium_locator, text, clear)) + def input_text(self, locator, text, clear=True): + """Types the given text into text field identified by ``locator``. + + If clear is true, the input element will be cleared before the text is typed into the element. + On the other hand clear is false, the previous text will not be cleared from the element. + + Examples: + | `Input Text` | id:name | John Doe | | + | `Input Text` | id:username | john | True | + + """ + self.loop.run_until_complete(self.async_func.input_text_async(locator, text, clear)) @keyword - def clear_element_text(self, selenium_locator): - self.loop.run_until_complete(self.async_func.clear_element_text_async(selenium_locator)) + def clear_element_text(self, locator): + """Clears value of text field identified by ``locator``. + + Example: + + | `Clear Element Text` | id:name | + """ + self.loop.run_until_complete(self.async_func.clear_element_text_async(locator)) diff --git a/PuppeteerLibrary/keywords/utility.py b/PuppeteerLibrary/keywords/utility.py index 8359cd6..48f4c59 100644 --- a/PuppeteerLibrary/keywords/utility.py +++ b/PuppeteerLibrary/keywords/utility.py @@ -9,9 +9,17 @@ class UtilityKeywords(LibraryComponent): @keyword def run_async_keywords(self, *keywords): # Ensure that script load async keywords before run async keywords function + """Executes all the given keywords in a asynchronous and wait until all keyword is completed + + Example: + + | Open browser | ${HOME_PAGE_URL} | options=${options} | | + | `Run Async Keywords` | Click Element | id:login_button | AND | + | ... | Wait for response url | ${HOME_PAGE_URL}/home.html | | + + """ self.ctx.load_async_keywords() run_keyword = _RunKeyword() - """Executes all the given keywords in a asynchronous and wait until all keyword complete""" self.loop.run_until_complete( self._run_async_keywords(run_keyword._split_run_keywords(list(keywords))) ) async def _run_async_keywords(self, iterable): diff --git a/PuppeteerLibrary/keywords/waiting.py b/PuppeteerLibrary/keywords/waiting.py index 2e06e83..6d970b4 100644 --- a/PuppeteerLibrary/keywords/waiting.py +++ b/PuppeteerLibrary/keywords/waiting.py @@ -11,54 +11,169 @@ def __init__(self, ctx): @keyword def wait_for_request_url(self, url, method='GET', timeout=None): - """Wait for request url""" + """ + Wait until web application sent request to ``url``. + + The ``url`` is request url. + + The ``method`` is HTTP Request Methods: + - GET (default) + - POST + - PUT + - HEAD + - DELETE + - PATCH + + Example: + + | Open browser | ${HOME_PAGE_URL} | options=${options} | | + | Input Text | id:username | foo | | + | Input Text | id:password | bar | | + | Run Async Keywords | Click Element | id:login_button | AND | + | ... | `Wait For Request Url` | ${URL_API}/login | POST | + + """ return self.loop.run_until_complete(self.async_func.wait_for_request_url_async(url, method , timeout)) @keyword def wait_for_response_url(self, url, status=200, timeout=None): - """Wait for response url""" + """ + Wait until web application received response from ``url``. + + The ``url`` is response url. + + The ``status`` is HTTP Status Codes: + - 200 (default) + - 201 + - 204 + - 400 + - 401 + - 404 + - 500 + Reference:[https://restfulapi.net/http-status-codes/|https://restfulapi.net/http-status-codes/] + + Example: + + | Open browser | ${HOME_PAGE_URL} | options=${options} | | + | Input Text | id:username | foo | | + | Input Text | id:password | bar | | + | Run Async Keywords | Click Element | id:login_button | AND | + | ... | `Wait For Response Url` | ${URL_API}/login | 200 | + + """ return self.loop.run_until_complete(self.async_func.wait_for_response_url_async(url, status, timeout)) @keyword def wait_for_function(self, page_function): - """Wait for page trigger function""" + """ + Waits until web application executes java script function. + + The ``page_function`` is java script function. + + """ return self.loop.run_until_complete(self.async_func.wait_for_function_async(page_function)) @keyword def wait_for_navigation(self): - """Wait for navigation from any redirect""" + """ + Waits until web page navigates to new url or reloads. + + Example: + + | Open browser | ${HOME_PAGE_URL} | options=${options} | | + | Input Text | id:username | foo | | + | Input Text | id:password | bar | | + | Run Async Keywords | Click Element | id:login_button | AND | + | ... | `Wait For Navigation` | | | + """ return self.loop.run_until_complete(self.async_func.wait_for_navigation_async()) @keyword def wait_until_page_contains_element(self, locator, timeout=None): - """Wait until page contains element within specific timeout""" + """ + Waits until ``locator`` element appears on current page. + + Example: + + | Open browser | ${HOME_PAGE_URL} | options=${options} | + | `Wait Until Page Contains Element` | id:username | | + """ return self.loop.run_until_complete(self.async_func.wait_for_selenium_selector(locator, timeout)) @keyword def wait_until_element_is_hidden(self, locator, timeout=None): + """ + Waits until ``locator`` element is hide or removed from web page. + + Example: + + | Run Async Keywords | Click Element | id:login_button | AND | + | ... | Wait For Navigation | | | + | `Wait Until Element Is Hidden` | id:login_button | | | + """ return self.loop.run_until_complete(self.async_func.wait_until_element_is_hidden_async(locator, timeout)) @keyword def wait_until_element_is_visible(self, locator, timeout=None): + """ + Waits until ``locator`` element is displayed on web page. + + Example: + + | Run Async Keywords | Click Element | id:login_button | AND | + | ... | Wait For Navigation | | | + | `Wait Until Element Is Visible` | id:welcome | | | + """ return self.loop.run_until_complete(self.async_func.wait_until_element_is_visible_async(locator, timeout)) @keyword def wait_until_page_contains(self, text, timeout=None): - """Waits until ``text`` appears on the current page""" + """ + Waits until ``text`` appears on current page. + + Example: + + | Run Async Keywords | Click Element | id:login_button | AND | + | ... | Wait For Navigation | | | + | `Wait Until Page Contains` | Invalid user name or password | | | + """ return self.loop.run_until_complete(self.async_func.wait_until_page_contains_async(text, timeout)) @keyword def wait_until_page_does_not_contains(self, text, timeout=None): - """Waits until ``text`` appears on the current page""" + """ + Waits until ``text`` disappears on current page. + + Example: + + | Run Async Keywords | Click Element | id:login_button | AND | + | ... | Wait For Navigation | | | + | `Wait Until Page Does Not Contains` | Please input your user name | | | + """ return self.loop.run_until_complete(self.async_func.wait_until_page_does_not_contains_async(text, timeout)) @keyword - def wait_until_element_contains(self, selenium_locator, text, timeout=None): - """Waits until the ``element`` contains ``text``.""" - return self.loop.run_until_complete(self.async_func.wait_until_element_contains_async(selenium_locator, text, timeout)) + def wait_until_element_contains(self, locator, text, timeout=None): + """ + Waits until ``locator`` element contains ``text``. + + Example: + + | Open browser | ${HOME_PAGE_URL} | options=${options} | + | `Wait Until Element Contains` | css:#container p | Please input your user name | + """ + return self.loop.run_until_complete(self.async_func.wait_until_element_contains_async(locator, text, timeout)) @keyword - def wait_until_element_does_not_contains(self, selenium_locator, text, timeout=None): - """Waits until the ``element`` does not contains ``text``.""" - return self.loop.run_until_complete(self.async_func.wait_until_element_does_not_contains_async(selenium_locator, text, timeout)) + def wait_until_element_does_not_contains(self, locator, text, timeout=None): + """ + Waits until ``locator`` element does not contains ``text``. + + Example: + + | Run Async Keywords | Click Element | id:login_button | AND | + | ... | Wait For Navigation | | | + | `Wait Until Element Does Not Contains` | css:#container p | Please input your user name | | + """ + return self.loop.run_until_complete(self.async_func.wait_until_element_does_not_contains_async(locator, text, timeout)) diff --git a/README.md b/README.md index 94fcb1d..43ea2b5 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,14 @@ # robotframework-puppeteer -Puppeteer with robotframework. This project connect between robotframework and puppeteer using [pyppeteer](https://github.com/pyppeteer/pyppeteer). +Robot Framework Puppeteer Library powered by [Pyppeteer](https://github.com/pyppeteer/pyppeteer). +Improve automated web testing with native functionality from [Puppeteer](https://github.com/puppeteer/puppeteer) by Google. -We aim for provide keyword similar to robotframework-seleniumlibrary and add core puppeteer functionality that will improve test experiences +We aim to provide keywords similar to robotframework-seleniumlibrary and add core puppeteer functionality that will improve test experiences. +Example: +- _Handle HTTP Request_ +- _Handle HTTP Response_ +- _Intercepter Http_ +- _Intercepter javascript function_ -Example: _Handle HTTP Request_, _Handle HTTP Response_ or _Intercepter http request & response_ Keyword documentation --------------------- diff --git a/contributing.md b/contributing.md index 7130ed3..8bc2baf 100644 --- a/contributing.md +++ b/contributing.md @@ -6,14 +6,16 @@ or slack with the owners of this repository before making a change. Please note we have a code of conduct, please follow it in all your interactions with the project. ## Pull Request Process -In progress +In progress ... ## Code of Conduct -In progress +In progress ... ## Release steps Please follow the release steps for ensure that all release will be in the same standard and pattern. +In progress ... + ### Step for regenerate keywords documents Command for Generate document diff --git a/docs/PuppeteerLibrary.html b/docs/PuppeteerLibrary.html index 81d95e8..28328e2 100644 --- a/docs/PuppeteerLibrary.html +++ b/docs/PuppeteerLibrary.html @@ -547,7 +547,7 @@ jQuery.extend({highlight:function(e,t,n,r){if(e.nodeType===3){var i=e.data.match(t);if(i){var s=document.createElement(n||"span");s.className=r||"highlight";var o=e.splitText(i.index);o.splitText(i[0].length);var u=o.cloneNode(true);s.appendChild(u);o.parentNode.replaceChild(s,o);return 1}}else if(e.nodeType===1&&e.childNodes&&!/(script|style)/i.test(e.tagName)&&!(e.tagName===n.toUpperCase()&&e.className===r)){for(var a=0;a