Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
7
Merge Requests
7
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
Jérome Perrin
erp5
Commits
88265a2a
Commit
88265a2a
authored
Jun 24, 2021
by
Jérome Perrin
Browse files
Options
Browse Files
Download
Plain Diff
Support seleniumserver in ERP5 tests
See merge request
nexedi/erp5!1447
parents
b8253e6e
beb061d8
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
149 additions
and
80 deletions
+149
-80
product/ERP5Type/tests/ERP5TypeFunctionalTestCase.py
product/ERP5Type/tests/ERP5TypeFunctionalTestCase.py
+149
-80
No files found.
product/ERP5Type/tests/ERP5TypeFunctionalTestCase.py
View file @
88265a2a
...
@@ -34,6 +34,8 @@ import time
...
@@ -34,6 +34,8 @@ import time
import
re
import
re
import
subprocess
import
subprocess
import
shutil
import
shutil
import
json
import
tempfile
import
transaction
import
transaction
import
logging
import
logging
from
ZPublisher.HTTPResponse
import
HTTPResponse
from
ZPublisher.HTTPResponse
import
HTTPResponse
...
@@ -43,10 +45,14 @@ from Products.ERP5Type.tests.runUnitTest import log_directory
...
@@ -43,10 +45,14 @@ from Products.ERP5Type.tests.runUnitTest import log_directory
from
Products.ERP5Type.Utils
import
stopProcess
,
PR_SET_PDEATHSIG
from
Products.ERP5Type.Utils
import
stopProcess
,
PR_SET_PDEATHSIG
from
lxml
import
etree
from
lxml
import
etree
from
lxml.html
import
builder
as
E
from
lxml.html
import
builder
as
E
import
certifi
import
urllib3
from
selenium
import
webdriver
from
selenium
import
webdriver
from
selenium.webdriver.common.by
import
By
from
selenium.webdriver.common.by
import
By
from
selenium.webdriver.support.ui
import
WebDriverWait
from
selenium.webdriver.support.ui
import
WebDriverWait
as
_WebDriverWait
from
selenium.webdriver.support
import
expected_conditions
as
EC
from
selenium.webdriver.support
import
expected_conditions
as
EC
from
selenium.webdriver.remote.remote_connection
import
RemoteConnection
logger
=
logging
.
getLogger
(
__name__
)
logger
=
logging
.
getLogger
(
__name__
)
...
@@ -161,55 +167,95 @@ class Xvfb(Process):
...
@@ -161,55 +167,95 @@ class Xvfb(Process):
logger
.
debug
(
'Xvfb : %d'
,
self
.
process
.
pid
)
logger
.
debug
(
'Xvfb : %d'
,
self
.
process
.
pid
)
logger
.
debug
(
'Take screenshots using xwud -in %s/Xvfb_screen0'
,
self
.
fbdir
)
logger
.
debug
(
'Take screenshots using xwud -in %s/Xvfb_screen0'
,
self
.
fbdir
)
class
WebDriverWait
(
_WebDriverWait
):
"""Wrapper for WebDriverWait which dumps the test page and take a
screenshot in case of error.
"""
def
until
(
self
,
*
args
):
try
:
return
super
(
WebDriverWait
,
self
).
until
(
*
args
)
except
:
logger
.
exception
(
"unable to find login field, dumping the page"
)
try
:
with
open
(
os
.
path
.
join
(
log_directory
,
'page.html'
),
'w'
)
as
f
:
f
.
write
(
self
.
_driver
.
execute_script
(
"return document.getElementById('testSuiteFrame').contentDocument.querySelector('html').innerHTML"
))
except
:
logger
.
exception
(
"error when dumping page"
)
try
:
with
open
(
os
.
path
.
join
(
log_directory
,
'page-screenshot.png'
),
'wb'
)
as
f
:
f
.
write
(
self
.
_driver
.
get_screenshot_as_png
())
except
:
logger
.
exception
(
"error when taking screenshot"
)
raise
class
FunctionalTestRunner
:
class
FunctionalTestRunner
:
# There is no test that can take more than 6 hours
# There is no test that can take more than 6 hours
timeout
=
6.0
*
3600
timeout
=
6.0
*
3600
def
__init__
(
self
,
host
,
port
,
portal
,
run_only
=
''
):
def
__init__
(
self
,
host
,
port
,
testcase
):
self
.
instance_home
=
os
.
environ
[
'INSTANCE_HOME'
]
self
.
instance_home
=
os
.
environ
[
'INSTANCE_HOME'
]
# Such information should be automatically loaded
# Such information should be automatically loaded
self
.
user
=
'ERP5TypeTestCase'
self
.
user
=
'ERP5TypeTestCase'
self
.
password
=
''
self
.
password
=
''
self
.
run_only
=
run_only
self
.
testcase
=
testcase
profile_dir
=
os
.
path
.
join
(
self
.
instance_home
,
'profile'
)
profile_dir
=
os
.
path
.
join
(
self
.
instance_home
,
'profile'
)
self
.
portal
=
portal
def
getStatus
(
self
):
def
getStatus
(
self
):
transaction
.
begin
()
transaction
.
begin
()
return
self
.
portal
.
portal_tests
.
TestTool_getResults
(
self
.
run_only
)
return
self
.
testcase
.
portal
.
portal_tests
.
TestTool_getResults
(
self
.
testcase
.
run_only
)
def
_getTestBaseURL
(
self
):
def
_getTestBaseURL
(
self
):
# Access the https proxy in front of runUnitTest's zserver
# Access the https proxy in front of runUnitTest's zserver
base_url
=
os
.
getenv
(
'zserver_frontend_url'
)
base_url
=
os
.
getenv
(
'zserver_frontend_url'
)
if
base_url
:
if
base_url
:
return
'%s%s'
%
(
base_url
,
self
.
portal
.
getId
())
return
'%s%s'
%
(
base_url
,
self
.
testcase
.
portal
.
getId
())
return
self
.
portal
.
portal_url
()
return
self
.
testcase
.
portal_url
()
def
_getTestURL
(
self
):
def
_getTestURL
(
self
):
return
ZELENIUM_BASE_URL
%
(
return
ZELENIUM_BASE_URL
%
(
self
.
_getTestBaseURL
(),
self
.
_getTestBaseURL
(),
self
.
run_only
,
self
.
testcase
.
run_only
,
)
)
def
test
(
self
,
debug
=
0
):
def
test
(
self
,
debug
=
0
):
xvfb
=
Xvfb
(
self
.
instance_home
)
xvfb
=
None
try
:
use_local_firefox
=
True
# options for firefox
options
=
webdriver
.
FirefoxOptions
()
# Service workers are disabled on Firefox 52 ESR:
# https://bugzilla.mozilla.org/show_bug.cgi?id=1338144
options
.
set_preference
(
'dom.serviceWorkers.enabled'
,
True
)
# output javascript console and errors on stdout to help diagnosing failures
options
.
set_preference
(
'devtools.console.stdout.content'
,
True
)
selenium_test_runner_configuration
=
{}
test_runner_configuration_file
=
os
.
environ
.
get
(
'ERP5_TEST_RUNNER_CONFIGURATION'
)
if
test_runner_configuration_file
and
os
.
path
.
exists
(
test_runner_configuration_file
):
with
open
(
test_runner_configuration_file
)
as
f
:
test_runner_configuration
=
json
.
load
(
f
)
selenium_test_runner_configuration
=
test_runner_configuration
.
get
(
'selenium'
,
{})
use_local_firefox
=
selenium_test_runner_configuration
.
get
(
'server-url'
)
is
None
if
'firefox'
not
in
selenium_test_runner_configuration
.
get
(
'desired-capabilities'
,
{}).
get
(
'browserName'
).
lower
():
options
=
None
if
use_local_firefox
:
xvfb
=
Xvfb
(
self
.
instance_home
)
self
.
testcase
.
addCleanup
(
xvfb
.
quit
)
if
not
(
debug
and
os
.
getenv
(
'DISPLAY'
)):
if
not
(
debug
and
os
.
getenv
(
'DISPLAY'
)):
logger
.
debug
(
"You can set 'erp5_debug_mode' environment variable to 1 to use your existing display instead of Xvfb."
)
logger
.
debug
(
"You can set 'erp5_debug_mode' environment variable to 1 to use your existing display instead of Xvfb."
)
xvfb
.
run
()
xvfb
.
run
()
capabilities
=
webdriver
.
common
.
desired_capabilities
\
capabilities
=
webdriver
.
common
.
desired_capabilities
\
.
DesiredCapabilities
.
FIREFOX
.
copy
()
.
DesiredCapabilities
.
FIREFOX
.
copy
()
capabilities
[
'marionette'
]
=
True
capabilities
[
'marionette'
]
=
True
# Zope is accessed through
apache
with a certificate not trusted by firefox
# Zope is accessed through
haproxy
with a certificate not trusted by firefox
capabilities
[
'acceptInsecureCerts'
]
=
True
capabilities
[
'acceptInsecureCerts'
]
=
True
# Service workers are disabled on Firefox 52 ESR:
# https://bugzilla.mozilla.org/show_bug.cgi?id=1338144
options
=
webdriver
.
FirefoxOptions
()
options
.
set_preference
(
'dom.serviceWorkers.enabled'
,
True
)
# output javascript console and errors on stdout to help diagnosing failures
options
.
set_preference
(
'devtools.console.stdout.content'
,
True
)
kw
=
dict
(
capabilities
=
capabilities
,
options
=
options
)
kw
=
dict
(
capabilities
=
capabilities
,
options
=
options
)
firefox_bin
=
os
.
environ
.
get
(
'firefox_bin'
)
firefox_bin
=
os
.
environ
.
get
(
'firefox_bin'
)
if
firefox_bin
:
if
firefox_bin
:
...
@@ -221,73 +267,96 @@ class FunctionalTestRunner:
...
@@ -221,73 +267,96 @@ class FunctionalTestRunner:
log_path
=
os
.
path
.
join
(
log_directory
,
'geckodriver.log'
),
log_path
=
os
.
path
.
join
(
log_directory
,
'geckodriver.log'
),
# service_log_path=os.path.join(log_directory, 'geckodriver.log'),
# service_log_path=os.path.join(log_directory, 'geckodriver.log'),
)
)
browser
=
webdriver
.
Firefox
(
**
kw
)
browser
=
webdriver
.
Firefox
(
**
kw
)
start_time
=
time
.
time
()
else
:
logger
.
info
(
"Running with browser: %s"
,
browser
)
executor
=
RemoteConnection
(
logger
.
info
(
"Reported user agent: %s"
,
browser
.
execute_script
(
"return navigator.userAgent"
))
selenium_test_runner_configuration
[
'server-url'
],
logger
.
info
(
keep_alive
=
True
)
"Reported screen information: %s"
,
cert_reqs
=
'CERT_REQUIRED'
browser
.
execute_script
(
ca_certs
=
certifi
.
where
()
'''
if
not
selenium_test_runner_configuration
.
get
(
'verify-server-certificate'
,
True
):
return JSON.stringify({
cert_reqs
=
'CERT_NONE'
'screen.width': window.screen.width,
ca_certs
=
None
'screen.height': window.screen.height,
if
selenium_test_runner_configuration
.
get
(
'server-ca-certificate'
):
'screen.pixelDepth': window.screen.pixelDepth,
ca_certs_tempfile
=
tempfile
.
NamedTemporaryFile
(
'innerWidth': window.innerWidth,
suffix
=
"-cacerts.pem"
,
'innerHeight': window.innerHeight
mode
=
"w"
,
})'''
))
delete
=
False
)
ca_certs
=
ca_certs_tempfile
.
name
browser
.
get
(
self
.
_getTestBaseURL
()
+
'/login_form'
)
self
.
testcase
.
addCleanup
(
os
.
unlink
,
ca_certs_tempfile
)
login_field
=
WebDriverWait
(
browser
,
10
).
until
(
with
open
(
ca_certs
,
'w'
)
as
f
:
EC
.
presence_of_element_located
((
By
.
ID
,
'name'
)),
f
.
write
(
selenium_test_runner_configuration
[
'server-ca-certificate'
])
executor
.
_conn
=
urllib3
.
PoolManager
(
cert_reqs
=
cert_reqs
,
ca_certs
=
ca_certs
)
browser
=
webdriver
.
Remote
(
command_executor
=
executor
,
desired_capabilities
=
selenium_test_runner_configuration
[
'desired-capabilities'
],
options
=
options
)
)
login_field
.
clear
()
self
.
testcase
.
addCleanup
(
browser
.
quit
)
login_field
.
send_keys
(
self
.
user
)
password_field
=
browser
.
find_element_by_id
(
'password'
)
start_time
=
time
.
time
()
password_field
.
clear
()
logger
.
info
(
"Running with browser: %s"
,
browser
)
password_field
.
send_keys
(
self
.
password
)
logger
.
info
(
"Reported user agent: %s"
,
browser
.
execute_script
(
"return navigator.userAgent"
))
login_form_url
=
browser
.
current_url
logger
.
info
(
password_field
.
submit
()
"Reported screen information: %s"
,
WebDriverWait
(
browser
,
10
).
until
(
EC
.
url_changes
(
login_form_url
))
browser
.
execute_script
(
WebDriverWait
(
browser
,
10
).
until
(
EC
.
presence_of_element_located
((
By
.
TAG_NAME
,
'body'
)))
'''
browser
.
get
(
self
.
_getTestURL
())
return JSON.stringify({
'screen.width': window.screen.width,
WebDriverWait
(
browser
,
10
).
until
(
EC
.
presence_of_element_located
((
'screen.height': window.screen.height,
By
.
XPATH
,
'//iframe[@id="testSuiteFrame"]'
'screen.pixelDepth': window.screen.pixelDepth,
)))
'innerWidth': window.innerWidth,
# XXX No idea how to wait for the iframe content to be loaded
'innerHeight': window.innerHeight
time
.
sleep
(
5
)
})'''
))
# Count number of tests to be executed
test_count
=
browser
.
execute_script
(
# login to get an authentication cookie
"return document.getElementById('testSuiteFrame').contentDocument.querySelector('tbody').children.length"
browser
.
get
(
self
.
_getTestBaseURL
()
+
'/login_form'
)
)
-
1
login_field
=
WebDriverWait
(
browser
,
10
).
until
(
# Wait for tests to end
EC
.
presence_of_element_located
((
By
.
ID
,
'name'
)),
WebDriverWait
(
browser
,
self
.
timeout
).
until
(
EC
.
presence_of_element_located
((
)
By
.
XPATH
,
'//td[@id="testRuns" and contains(text(), "%i")]'
%
test_count
login_field
.
clear
()
)))
login_field
.
send_keys
(
self
.
user
)
# At the end of each test, updateSuiteWithResultOfPreviousTest updates
password_field
=
browser
.
find_element_by_id
(
'password'
)
# testSuiteFrame iframe with hidden div containing the test results table.
password_field
.
clear
()
# We will inspect these tables to know which tests have failed. First we
password_field
.
send_keys
(
self
.
password
)
# need to wait a bit more, because at the end of test ( testComplete ),
login_form_url
=
browser
.
current_url
# updateSuiteWithResultOfPreviousTest is called by setTimeout. We want to
password_field
.
submit
()
# wait for the last test (which is the last td) result table to be present
WebDriverWait
(
browser
,
10
).
until
(
EC
.
url_changes
(
login_form_url
))
browser
.
switch_to_frame
(
'testSuiteFrame'
)
WebDriverWait
(
browser
,
10
).
until
(
EC
.
presence_of_element_located
((
By
.
TAG_NAME
,
'body'
)))
WebDriverWait
(
browser
,
10
).
until
(
EC
.
presence_of_element_located
((
By
.
XPATH
,
'//table/tbody/tr/td[last()]//table'
browser
.
get
(
self
.
_getTestURL
())
)))
WebDriverWait
(
browser
,
10
).
until
(
EC
.
presence_of_element_located
((
browser
.
switch_to_default_content
()
By
.
XPATH
,
'//iframe[@id="testSuiteFrame"]'
self
.
execution_duration
=
round
(
time
.
time
()
-
start_time
,
2
)
)))
html_parser
=
etree
.
HTMLParser
(
recover
=
True
)
# XXX No idea how to wait for the iframe content to be loaded
iframe
=
etree
.
fromstring
(
time
.
sleep
(
5
)
# Count number of tests to be executed
test_count
=
browser
.
execute_script
(
"return document.getElementById('testSuiteFrame').contentDocument.querySelector('tbody').children.length"
)
-
1
# Wait for tests to end
WebDriverWait
(
browser
,
self
.
timeout
).
until
(
EC
.
presence_of_element_located
((
By
.
XPATH
,
'//td[@id="testRuns" and contains(text(), "%i")]'
%
test_count
)))
# At the end of each test, updateSuiteWithResultOfPreviousTest updates
# testSuiteFrame iframe with hidden div containing the test results table.
# We will inspect these tables to know which tests have failed. First we
# need to wait a bit more, because at the end of test ( testComplete ),
# updateSuiteWithResultOfPreviousTest is called by setTimeout. We want to
# wait for the last test (which is the last td) result table to be present
browser
.
switch_to_frame
(
'testSuiteFrame'
)
WebDriverWait
(
browser
,
10
).
until
(
EC
.
presence_of_element_located
((
By
.
XPATH
,
'//table/tbody/tr/td[last()]//table'
)))
browser
.
switch_to_default_content
()
self
.
execution_duration
=
round
(
time
.
time
()
-
start_time
,
2
)
html_parser
=
etree
.
HTMLParser
(
recover
=
True
)
iframe
=
etree
.
fromstring
(
browser
.
execute_script
(
browser
.
execute_script
(
"return document.getElementById('testSuiteFrame').contentDocument.querySelector('html').innerHTML"
"return document.getElementById('testSuiteFrame').contentDocument.querySelector('html').innerHTML"
).
encode
(
'UTF-8'
),
).
encode
(
'UTF-8'
),
html_parser
html_parser
)
)
browser
.
quit
()
finally
:
xvfb
.
quit
()
return
iframe
return
iframe
def
processResult
(
self
,
iframe
):
def
processResult
(
self
,
iframe
):
...
@@ -330,6 +399,7 @@ class FunctionalTestRunner:
...
@@ -330,6 +399,7 @@ class FunctionalTestRunner:
return
detail
,
sucess_amount
,
failure_amount
,
expected_failure_amount
,
\
return
detail
,
sucess_amount
,
failure_amount
,
expected_failure_amount
,
\
error_title_list
error_title_list
class
ERP5TypeFunctionalTestCase
(
ERP5TypeTestCase
):
class
ERP5TypeFunctionalTestCase
(
ERP5TypeTestCase
):
run_only
=
""
run_only
=
""
foreground
=
0
foreground
=
0
...
@@ -350,8 +420,7 @@ class ERP5TypeFunctionalTestCase(ERP5TypeTestCase):
...
@@ -350,8 +420,7 @@ class ERP5TypeFunctionalTestCase(ERP5TypeTestCase):
self
.
portal
.
portal_tests
.
TestTool_cleanUpTestResults
(
self
.
run_only
or
None
)
self
.
portal
.
portal_tests
.
TestTool_cleanUpTestResults
(
self
.
run_only
or
None
)
self
.
tic
()
self
.
tic
()
host
,
port
=
self
.
startZServer
()
host
,
port
=
self
.
startZServer
()
self
.
runner
=
FunctionalTestRunner
(
host
,
port
,
self
.
runner
=
FunctionalTestRunner
(
host
,
port
,
self
)
self
.
portal
,
self
.
run_only
)
def
setSystemPreference
(
self
):
def
setSystemPreference
(
self
):
self
.
portal
.
Zuite_setPreference
(
self
.
portal
.
Zuite_setPreference
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment