Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 27 additions & 4 deletions Examples/browser-demo.robot
Original file line number Diff line number Diff line change
@@ -1,18 +1,41 @@
*** Settings ***
Library PuppeteerLibrary
Test Teardown Close Browser


*** Test Cases ***
Example switch browser and browser title
Example switch window and check window title
[Teardown] Close Browser
${HEADLESS} Get variable value ${HEADLESS} ${False}
&{options} = create dictionary headless=${HEADLESS}
Open browser http://127.0.0.1:7272 options=${options}
Maximize Browser Window
${title} = Get title
${location} = Get location
Click Element xpath://a[@href="docs.html"]
Wait for new window open
Run Async Keywords
... Wait for new window open AND
... Click Element xpath://a[@href="docs.html"]
Switch Window NEW
${Title} = Get Title
should be equal as strings Docs Page ${Title}

Example open multiple browser
[Teardown] Close All Browser
${HEADLESS} Get variable value ${HEADLESS} ${False}
&{options} = create dictionary headless=${HEADLESS}
Open browser http://127.0.0.1:7272 options=${options} alias=Browser 1
Click Element id=login_button
Open browser http://127.0.0.1:7272 options=${options} alias=Browser 2
Click Element id=get_ajax
Switch Browser Browser 1
Wait Until Page Contains Error Page
Switch Browser Browser 2
Wait Until Page Contains products

Example close puppeteer browser
[Teardown] Close All Browser
${HEADLESS} Get variable value ${HEADLESS} ${False}
&{options} = create dictionary headless=${HEADLESS}
Open browser http://127.0.0.1:7272 options=${options} alias=Browser 1
Close Puppeteer
Open browser http://127.0.0.1:7272 options=${options} alias=Browser 2
Click Element id=get_ajax
64 changes: 59 additions & 5 deletions PuppeteerLibrary/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import asyncio
from robot.api.deco import not_keyword
from robot.api import logger
from pyppeteer.browser import Browser
from pyppeteer.browser import Browser, BrowserContext
from robot.libraries.BuiltIn import BuiltIn
from PuppeteerLibrary.custom_elements.SPage import SPage
from PuppeteerLibrary.base.robotlibcore import DynamicCore
from PuppeteerLibrary.keywords import (
AlertKeywords,
AlertKeywordsAsync,
BrowserManagementKeywords,
BrowserManagementKeywordsAsync,
ElementKeywords,
ElementKeywordsAsync,
FormElementKeywords,
Expand Down Expand Up @@ -69,11 +70,14 @@ class PuppeteerLibrary(DynamicCore):
ROBOT_LISTENER_API_VERSION = 3

loop = asyncio.get_event_loop()
browser = None
current_page = None
is_load_async_keywords = False
async_libraries = []

browser = None
contexts = {}
current_context_name = None
current_page = None

def __init__(self):
self.run_on_failure_keyword = 'Capture Page Screenshot'

Expand All @@ -91,6 +95,7 @@ def __init__(self):

self.async_libraries = [
AlertKeywordsAsync(self),
BrowserManagementKeywordsAsync(self),
ElementKeywordsAsync(self),
FormElementKeywordsAsync(self),
JavascriptKeywordsAsync(self),
Expand All @@ -105,6 +110,55 @@ def load_async_keywords(self):
self.add_library_components(self.async_libraries)
self.is_load_async_keywords = True

@not_keyword
def get_browser(self) -> Browser:
return self.browser

@not_keyword
def clear_browser(self):
self.browser = None
self.contexts = {}
self.current_context_name = None
self.current_page = None

@not_keyword
async def create_context_async(self, alias) -> BrowserContext:
context = await self.browser.createIncognitoBrowserContext()
if alias in self.contexts.keys():
await self.contexts[alias].close()
del self.contexts[alias]
self.current_context_name = alias
self.contexts[self.current_context_name] = context
return context

@not_keyword
def get_current_context(self) -> BrowserContext:
return self.contexts[self.current_context_name]

@not_keyword
async def set_current_context(self, context_name) -> BrowserContext:
self.current_context_name = context_name
context = self.get_current_context()
pages = await context.pages()
self.current_page = pages[-1]
return context

@not_keyword
def clear_context(self, context_name):
del self.contexts[context_name]
if self.current_context_name == context_name:
self.current_context_name = None
self.current_page = None

@not_keyword
def clear_current_context(self):
self.clear_context(self.current_context_name)

@not_keyword
async def create_page_async(self) -> SPage:
self.current_page = await self.get_current_context().newPage()
return self.get_current_page()

@not_keyword
def get_current_page(self) -> SPage:
page = self.current_page
Expand All @@ -118,8 +172,8 @@ def set_current_page(self, page) -> SPage:
return self.current_page

@not_keyword
def get_browser(self) -> Browser:
return self.browser
def clear_current_page(self):
self.current_page = None

@not_keyword
def run_keyword(self, name, args, kwargs):
Expand Down
1 change: 1 addition & 0 deletions PuppeteerLibrary/keywords/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .alert import AlertKeywords
from .alert_async import AlertKeywordsAsync
from .browsermanagement import BrowserManagementKeywords
from .browsermanagement_async import BrowserManagementKeywordsAsync
from .element import ElementKeywords
from .element_async import ElementKeywordsAsync
from .formelement import FormElementKeywords
Expand Down
99 changes: 52 additions & 47 deletions PuppeteerLibrary/keywords/browsermanagement.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
from pyppeteer import launch
from PuppeteerLibrary.base.librarycomponent import LibraryComponent
from PuppeteerLibrary.base.robotlibcore import keyword
from PuppeteerLibrary.keywords.browsermanagement_async import BrowserManagementKeywordsAsync


class BrowserManagementKeywords(LibraryComponent):

def __init__(self, ctx):
self.ctx = ctx
self.async_func = BrowserManagementKeywordsAsync(self.ctx)

@keyword
def open_browser(self, url, browser="chrome", alias=None, options=None):
"""Opens a new browser instance to the specific ``url``.
Expand All @@ -33,42 +38,52 @@ def open_browser(self, url, browser="chrome", alias=None, options=None):

"""
async def open_browser_async():
default_args = []
default_options = {
'headless': True,
'width': 1366,
'height': 768
}
merged_options = None
if options is None:
merged_options = default_options
else:
merged_options = {**default_options, **options}

if 'win' not in sys.platform.lower():
default_args = ['--no-sandbox', '--disable-setuid-sandbox']

self.info(('Open browser to ' + url + '\n' +
str(merged_options)))
self.ctx.browser = await launch(
headless=merged_options['headless'],
defaultViewport={
'width': merged_options['width'],
'height': merged_options['height']
},
args=default_args)
self.ctx.current_page = await self.ctx.browser.newPage()
await self.ctx.current_page.goto(url)
await self.ctx.current_page.screenshot({'path': 'example.png'})
if self.ctx.browser is None:
default_args = []
default_options = {
'headless': True,
'width': 1366,
'height': 768
}
merged_options = None
if options is None:
merged_options = default_options
else:
merged_options = {**default_options, **options}

if 'win' not in sys.platform.lower():
default_args = ['--no-sandbox', '--disable-setuid-sandbox']

self.info(('Open browser to ' + url + '\n' +
str(merged_options)))
self.ctx.browser = await launch(
headless=merged_options['headless'],
defaultViewport={
'width': merged_options['width'],
'height': merged_options['height']
},
args=default_args)
await self.ctx.create_context_async(alias)
current_page = await self.ctx.create_page_async()
await current_page.goto(url)
await current_page.screenshot({'path': 'example.png'})
self.loop.run_until_complete(open_browser_async())

@keyword
def close_browser(self):
def close_browser(self, alias=None):
"""Closes the current browser
"""
async def close_browser_async():
await self.ctx.browser.close()
self.loop.run_until_complete(close_browser_async())
self.loop.run_until_complete(self.async_func.close_browser_async(alias))

@keyword
def close_all_browser(self):
"""Close all browser
"""
self.loop.run_until_complete(self.async_func.close_all_browser_async())

@keyword
def close_puppeteer(self):
self.loop.run_until_complete(self.async_func.close_puppeteer_async())

@keyword
def maximize_browser_window(self, width=1366, height=768):
Expand Down Expand Up @@ -145,22 +160,7 @@ def wait_for_new_window_open(self, timeout=None):
| Run Async Keywords | Click Element | id:view_conditions | AND |
| ... | `Wait For New Window Open` | | |
"""
timeout = self.timestr_to_secs_for_default_timeout(timeout)
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
pre_page_len = len(pages)
timer = 0
while timer < timeout:
pages = await self.ctx.get_browser().pages()
await pages[-1].title() # workaround for force pages re-cache
page_len = len(pages)
if page_len > pre_page_len:
return
timer += 1
time.sleep(1)
raise Exception('No new page has been open. pre: '+str(pre_page_len)+' current: '+str(page_len))
self.loop.run_until_complete(wait_for_new_page_open_async())
self.loop.run_until_complete(self.async_func.wait_for_new_window_open_async(timeout))

@keyword
def switch_window(self, locator='MAIN'):
Expand Down Expand Up @@ -193,3 +193,8 @@ async def switch_window_async():
raise Exception('Can\'t find specify page locator.')
self.loop.run_until_complete(switch_window_async())

@keyword
def switch_browser(self, alias):
"""Switch browser context based on alias name
"""
return self.loop.run_until_complete(self.ctx.set_current_context(alias))
45 changes: 45 additions & 0 deletions PuppeteerLibrary/keywords/browsermanagement_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import time
from PuppeteerLibrary.base.robotlibcore import keyword
from PuppeteerLibrary.base.librarycomponent import LibraryComponent


class BrowserManagementKeywordsAsync(LibraryComponent):

@keyword
async def wait_for_new_window_open_async(self, timeout=None):
timeout = self.timestr_to_secs_for_default_timeout(timeout)
pages = await self.ctx.get_browser().pages()
await pages[-1].title() # workaround for force pages re-cache
pre_page_len = len(pages)
timer = 0
while timer < timeout:
pages = await self.ctx.get_browser().pages()
await pages[-1].title() # workaround for force pages re-cache
page_len = len(pages)
if page_len > pre_page_len:
return
timer += 1
time.sleep(1)
raise Exception('No new page has been open. pre: ' + str(pre_page_len) + ' current: ' + str(page_len))

@keyword
async def close_browser_async(self, alias=None):
if alias is None:
alias = self.ctx.current_context_name
await self.ctx.contexts[alias].close()
self.ctx.clear_context(alias)
if len(self.ctx.contexts.keys()) > 0:
await self.ctx.set_current_context(list(self.ctx.contexts.keys())[-1])

@keyword
async def close_all_browser_async(self):
for context in self.ctx.contexts.values():
await context.close()
self.ctx.contexts = {}
self.ctx.current_context_name = None
self.ctx.current_page = None

@keyword
async def close_puppeteer_async(self):
await self.ctx.browser.close()
self.ctx.clear_browser()