Skip to content

Commit 5998539

Browse files
authored
Reuse authentication state (#144)
* Init state storage support * Add support delete storage * Archive test report * Update test steps Co-authored-by: DESKTOP\atthaboon.s <atthaboon.s@qahive.com>
1 parent 1184084 commit 5998539

File tree

8 files changed

+125
-1
lines changed

8 files changed

+125
-1
lines changed

.github/workflows/pythonpackage.yml

+8-1
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,11 @@ jobs:
7979
uses: GabrielBB/xvfb-action@v1
8080
with:
8181
working-directory: ./ #optional
82-
run: robot -v BROWSER:${{ matrix.browser }} -e IgnoreORIgnore_${{ matrix.browser }} Examples
82+
run: robot --outputdir test-report -v BROWSER:${{ matrix.browser }} -e IgnoreORIgnore_${{ matrix.browser }} Examples
83+
- name: Upload a test report
84+
if: always()
85+
uses: actions/upload-artifact@v2.2.4
86+
with:
87+
# Artifact name
88+
name: test-report
89+
path: test-report

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@ Examples/test.csv
2929
.venv/
3030
Examples/trace.zip
3131
Examples/traces/
32+
Examples/state-admin.json
33+
Examples/test-report/
34+
test-report/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
*** Settings ***
2+
Library PuppeteerLibrary
3+
Test Setup Open browser to test page
4+
Test Teardown Close All Browser
5+
Suite Teardown Close Puppeteer
6+
7+
8+
*** Variables ***
9+
${DEFAULT_BROWSER} chrome
10+
${HOME_PAGE_URL} https://demoqa.com/login
11+
${PROFILE_PAGE_URL} https://demoqa.com/profile
12+
${USERNAME} qahive
13+
${PASSWORD} Welcome1!
14+
15+
16+
*** Test Cases ***
17+
Save and Reuse browser authen state into json file
18+
[Tags] Ignore_ptchrome
19+
Input Text id=userName ${USERNAME}
20+
Input Password id=password ${PASSWORD}
21+
Click Element id=login
22+
Wait Until Page Contains Element id=userName-value
23+
Wait Until Page Contains ${USERNAME}
24+
Save Browser Storage State admin
25+
Close All Browser
26+
############################################################
27+
# Reopen and bypass login by using state ref
28+
############################################################
29+
${BROWSER} = Get variable value ${BROWSER} ${DEFAULT_BROWSER}
30+
${HEADLESS} Get variable value ${HEADLESS} ${False}
31+
&{options} = create dictionary headless=${HEADLESS} state_ref=admin
32+
Open browser ${PROFILE_PAGE_URL} browser=${BROWSER} options=${options}
33+
Wait Until Page Contains ${USERNAME}
34+
Delete Browser Storage State admin
35+
Delete All Browser Storage States
36+
37+
*** Keywords ***
38+
Open browser to test page
39+
${BROWSER} = Get variable value ${BROWSER} ${DEFAULT_BROWSER}
40+
${HEADLESS} Get variable value ${HEADLESS} ${False}
41+
&{options} = create dictionary headless=${HEADLESS}
42+
Open browser ${HOME_PAGE_URL} browser=${BROWSER} options=${options}
43+
Set Screenshot Directory test-report
44+
Capture Page Screenshot

PuppeteerLibrary/ikeywords/ibrowsermanagement_async.py

+6
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,9 @@ async def add_cookie(self, name: str, value: str):
7171

7272
async def delete_all_cookies(self):
7373
pass
74+
75+
##############################
76+
# State
77+
##############################
78+
async def save_browser_storage_state(self, state_folder: str, ref='user'):
79+
pass

PuppeteerLibrary/keywords/browsermanagement.py

+48
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import os
2+
import shutil
13
from PuppeteerLibrary.base.librarycomponent import LibraryComponent
24
from PuppeteerLibrary.base.robotlibcore import keyword
35
from PuppeteerLibrary.ikeywords.ibrowsermanagement_async import iBrowserManagementAsync
@@ -8,6 +10,7 @@ class BrowserManagementKeywords(LibraryComponent):
810

911
def __init__(self, ctx):
1012
super().__init__(ctx)
13+
self.STATES_FOLDER = './states'
1114

1215
def get_async_keyword_group(self) -> iBrowserManagementAsync:
1316
return self.ctx.get_current_library_context().get_async_keyword_group(type(self).__name__)
@@ -32,6 +35,7 @@ def open_browser(self, url, browser="chrome", alias=None, options={}):
3235
| width | default 1366 |
3336
| height | default 768 |
3437
| emulate | iPhone 11 |
38+
| state_ref | State Reference |
3539
3640
**Other options**
3741
pwchrome, webkit and firefox please visit: https://playwright.dev/python/docs/api/class-browser?_highlight=new_page#browsernew_pagekwargs
@@ -234,3 +238,47 @@ def delete_all_cookies(self):
234238
""" Deletes all cookies.
235239
"""
236240
return self.loop.run_until_complete(self.get_async_keyword_group().delete_all_cookies())
241+
242+
##############################
243+
# State
244+
##############################
245+
@keyword
246+
def save_browser_storage_state(self, ref='user'):
247+
""" Save browser storage state that can resue Authentication state
248+
249+
*ref* : reference state name
250+
251+
*Limitation* only support Playwright browser
252+
"""
253+
self.info('Save storate state for ' + ref)
254+
try:
255+
os.mkdir(self.STATES_FOLDER)
256+
except:
257+
self.info('states folder already exists.')
258+
return self.loop.run_until_complete(self.get_async_keyword_group().save_browser_storage_state(self.STATES_FOLDER, ref))
259+
260+
@keyword
261+
def delete_browser_storage_state(self, ref):
262+
""" Delete browser storage state
263+
264+
*ref* : reference state name
265+
266+
*Limitation* only support Playwright browser
267+
"""
268+
file_path = self.STATES_FOLDER +'/state-'+ ref + '.json'
269+
if os.path.exists(file_path):
270+
os.remove(file_path)
271+
else:
272+
self.warn('Can not delete the storate '+ref+' as it doesn\'t exists')
273+
274+
@keyword
275+
def delete_all_browser_storage_states(self):
276+
""" Delete all browser storage state
277+
278+
*Limitation* only support Playwright browser
279+
"""
280+
try:
281+
shutil.rmtree(self.STATES_FOLDER)
282+
except OSError as e:
283+
self.warn("Error: %s - %s." % (e.filename, e.strerror))
284+

PuppeteerLibrary/playwright/async_keywords/playwright_browsermanagement.py

+7
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,10 @@ async def add_cookie(self, name: str, value: str):
137137

138138
async def delete_all_cookies(self):
139139
await self.library_ctx.get_browser_context().contexts[0].clear_cookies()
140+
141+
##############################
142+
# State
143+
##############################
144+
async def save_browser_storage_state(self, state_folder, ref='user'):
145+
storage = await self.library_ctx.get_browser_context().contexts[0].storage_state(path=state_folder+"/state-"+ ref +".json")
146+
return storage

PuppeteerLibrary/playwright/playwright_context.py

+3
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ async def create_new_page(self, options: dict={}) -> BasePage:
113113
if 'emulate' in options:
114114
device_options = self.playwright.devices[options['emulate']]
115115

116+
if 'state_ref' in options:
117+
device_options['storage_state'] = './states/state-'+ options['state_ref'] + '.json'
118+
116119
new_page = await self.browser.new_page(**device_options)
117120
self.current_page = PlaywrightPage(new_page)
118121
return self.current_page

PuppeteerLibrary/puppeteer/async_keywords/puppeteer_browsermanagement.py

+6
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,9 @@ async def delete_all_cookies(self):
144144
cookies = await self.library_ctx.get_current_page().get_page().cookies()
145145
for cookie in cookies:
146146
await self.library_ctx.get_current_page().get_page().deleteCookie(cookie)
147+
148+
##############################
149+
# State
150+
##############################
151+
async def save_browser_storage_state(self, state_folder, ref='user'):
152+
raise Exception('Not support for puppeteer browser')

0 commit comments

Comments
 (0)