Upgrade to Selenium 4
Switches from standalone containers to explicit node+hub config Selenium 4 has some differences in handling stale elements that we should be aware of moving forward closes OUT-4988 flag=none [skip-stages=Flakey Spec Catcher] Test-plan: - make sure screenshots can happen for failures - retrigger a few times and make sure things pass - verify build summaries are intact - verify FSC can still run seleniums - verify local selenium running still works - firefox / chrome / edge where applicable - verify docker selenium running still works - firefox / chrome / edge where applicable Change-Id: I8f2fe5a34d712b5ccd7191bae7a9aeeb6f1f473d Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/284811 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: James Butters <jbutters@instructure.com> QA-Review: Robin Kuss <rkuss@instructure.com> Product-Review: Brian Watson <bwatson@instructure.com>
This commit is contained in:
parent
df7130ba93
commit
2f2a27d91b
|
@ -41,9 +41,9 @@ group :test do
|
||||||
gem "once-ler", "2.0.0"
|
gem "once-ler", "2.0.0"
|
||||||
gem "sauce_whisk", "0.2.2"
|
gem "sauce_whisk", "0.2.2"
|
||||||
|
|
||||||
gem "selenium-webdriver", "3.142.7", require: false
|
gem "selenium-webdriver", "~> 4.1.0", require: false
|
||||||
gem "childprocess", "3.0.0", require: false
|
gem "childprocess", "3.0.0", require: false
|
||||||
gem "webdrivers", "4.2.0", require: false
|
gem "webdrivers", "5.0.0", require: false
|
||||||
gem "testrailtagging", "0.3.8.7", require: false
|
gem "testrailtagging", "0.3.8.7", require: false
|
||||||
|
|
||||||
gem "webmock", "3.8.2", require: false
|
gem "webmock", "3.8.2", require: false
|
||||||
|
|
|
@ -68,7 +68,6 @@ pipeline {
|
||||||
RSPECQ_UPDATE_TIMINGS = "${env.GERRIT_EVENT_TYPE == 'change-merged' ? '1' : '0'}"
|
RSPECQ_UPDATE_TIMINGS = "${env.GERRIT_EVENT_TYPE == 'change-merged' ? '1' : '0'}"
|
||||||
ENABLE_AXE_SELENIUM = "${env.ENABLE_AXE_SELENIUM}"
|
ENABLE_AXE_SELENIUM = "${env.ENABLE_AXE_SELENIUM}"
|
||||||
POSTGRES_PASSWORD = 'sekret'
|
POSTGRES_PASSWORD = 'sekret'
|
||||||
SELENIUM_VERSION = '3.141.59-20210929'
|
|
||||||
RSPECQ_REDIS_URL = redisUrl()
|
RSPECQ_REDIS_URL = redisUrl()
|
||||||
PATCHSET_TAG = getPatchsetTag()
|
PATCHSET_TAG = getPatchsetTag()
|
||||||
CANVAS_ZEITWERK = '1'
|
CANVAS_ZEITWERK = '1'
|
||||||
|
@ -103,18 +102,18 @@ pipeline {
|
||||||
projectName: env.JOB_NAME,
|
projectName: env.JOB_NAME,
|
||||||
selector: specific(env.BUILD_NUMBER),
|
selector: specific(env.BUILD_NUMBER),
|
||||||
)
|
)
|
||||||
|
|
||||||
withEnv(['COMPOSE_FILE=docker-compose.new-jenkins.yml']) {
|
withEnv(['COMPOSE_FILE=docker-compose.new-jenkins.yml']) {
|
||||||
sh """
|
sh """
|
||||||
docker-compose run -v \$(pwd)/\$LOCAL_WORKDIR/tmp/coverage/:/tmp/coverage \
|
docker-compose run -v \$(pwd)/\$LOCAL_WORKDIR/tmp/coverage/:/tmp/coverage \
|
||||||
--name coverage-collator canvas bash -c \
|
--name coverage-collator canvas bash -c \
|
||||||
"bundle install; bundle exec rake coverage:report['/tmp/coverage/canvas__*/**']"
|
"bundle install; bundle exec rake coverage:report['/tmp/coverage/canvas__*/**']"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
sh 'docker cp coverage-collator:/usr/src/app/coverage/ coverage'
|
sh 'docker cp coverage-collator:/usr/src/app/coverage/ coverage'
|
||||||
|
|
||||||
archiveArtifacts allowEmptyArchive: true, artifacts: 'coverage/**'
|
archiveArtifacts allowEmptyArchive: true, artifacts: 'coverage/**'
|
||||||
|
|
||||||
publishHTML target: [
|
publishHTML target: [
|
||||||
allowMissing: false,
|
allowMissing: false,
|
||||||
alwaysLinkToLastBuild: false,
|
alwaysLinkToLastBuild: false,
|
||||||
|
@ -123,7 +122,7 @@ pipeline {
|
||||||
reportFiles: 'index.html',
|
reportFiles: 'index.html',
|
||||||
reportName: 'Ruby Coverage Report'
|
reportName: 'Ruby Coverage Report'
|
||||||
]
|
]
|
||||||
|
|
||||||
uploadCoverage([
|
uploadCoverage([
|
||||||
uploadSource: '/coverage',
|
uploadSource: '/coverage',
|
||||||
uploadDest: env.COVERAGE_LOCATION
|
uploadDest: env.COVERAGE_LOCATION
|
||||||
|
|
|
@ -168,7 +168,6 @@ pipeline {
|
||||||
RSPECQ_UPDATE_TIMINGS = "${env.GERRIT_EVENT_TYPE == 'change-merged' ? '1' : '0'}"
|
RSPECQ_UPDATE_TIMINGS = "${env.GERRIT_EVENT_TYPE == 'change-merged' ? '1' : '0'}"
|
||||||
ENABLE_AXE_SELENIUM = "${env.ENABLE_AXE_SELENIUM}"
|
ENABLE_AXE_SELENIUM = "${env.ENABLE_AXE_SELENIUM}"
|
||||||
POSTGRES_PASSWORD = 'sekret'
|
POSTGRES_PASSWORD = 'sekret'
|
||||||
SELENIUM_VERSION = '3.141.59-20210929'
|
|
||||||
RSPECQ_REDIS_URL = redisUrl()
|
RSPECQ_REDIS_URL = redisUrl()
|
||||||
CANVAS_ZEITWERK = '1'
|
CANVAS_ZEITWERK = '1'
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,7 +189,6 @@ pipeline {
|
||||||
// until we figure out how to run them, we should ignore them
|
// until we figure out how to run them, we should ignore them
|
||||||
FSC_IGNORE_FILES = 'gems/.*/spec/,spec/contracts/,engines/.*/spec/,spec/selenium/performance/'
|
FSC_IGNORE_FILES = 'gems/.*/spec/,spec/contracts/,engines/.*/spec/,spec/selenium/performance/'
|
||||||
POSTGRES_PASSWORD = 'sekret'
|
POSTGRES_PASSWORD = 'sekret'
|
||||||
SELENIUM_VERSION = '3.141.59-20210929'
|
|
||||||
|
|
||||||
// Targeting 10 minutes / node, each node runs RSPEC_PROCESSES threads and
|
// Targeting 10 minutes / node, each node runs RSPEC_PROCESSES threads and
|
||||||
// repeats each test FSC_REPEAT_FACTOR times.
|
// repeats each test FSC_REPEAT_FACTOR times.
|
||||||
|
|
|
@ -37,7 +37,6 @@ pipeline {
|
||||||
RUBY = configuration.ruby()
|
RUBY = configuration.ruby()
|
||||||
RERUNS_RETRY = 0 // no reruns
|
RERUNS_RETRY = 0 // no reruns
|
||||||
POSTGRES_PASSWORD = 'sekret'
|
POSTGRES_PASSWORD = 'sekret'
|
||||||
SELENIUM_VERSION = '3.141.59-20210929'
|
|
||||||
|
|
||||||
CASSANDRA_IMAGE_TAG = imageTag.cassandra()
|
CASSANDRA_IMAGE_TAG = imageTag.cassandra()
|
||||||
DYNAMODB_IMAGE_TAG = imageTag.dynamodb()
|
DYNAMODB_IMAGE_TAG = imageTag.dynamodb()
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
ARG SELENIUM_VERSION=3.141.59-20210929
|
FROM selenium/node-chrome:98.0
|
||||||
FROM selenium/standalone-chrome-debug:$SELENIUM_VERSION
|
|
||||||
|
|
||||||
COPY entry_point.sh /opt/bin/custom_entry_point.sh
|
COPY entry_point.sh /opt/bin/custom_entry_point.sh
|
||||||
USER root
|
USER root
|
||||||
|
|
|
@ -17,7 +17,8 @@ DOCKER_IMAGES=(
|
||||||
$POSTGRES_IMAGE_TAG
|
$POSTGRES_IMAGE_TAG
|
||||||
$REGISTRY_BASE/canvas-rce-api
|
$REGISTRY_BASE/canvas-rce-api
|
||||||
$REGISTRY_BASE/redis:alpine
|
$REGISTRY_BASE/redis:alpine
|
||||||
$REGISTRY_BASE/selenium-chrome:"${SELENIUM_VERSION:-3.141.59-20210929}"
|
$REGISTRY_BASE/selenium-node-chrome:"${CHROME_VERSION:-98.0}"
|
||||||
|
$REGISTRY_BASE/selenium-hub:4.1.2-20220217
|
||||||
)
|
)
|
||||||
|
|
||||||
echo "${DOCKER_IMAGES[@]}" | xargs -P0 -n1 ./build/new-jenkins/docker-with-flakey-network-protection.sh pull &
|
echo "${DOCKER_IMAGES[@]}" | xargs -P0 -n1 ./build/new-jenkins/docker-with-flakey-network-protection.sh pull &
|
||||||
|
|
|
@ -29,8 +29,7 @@ def createDistribution(nestedStages) {
|
||||||
|
|
||||||
def baseEnvVars = [
|
def baseEnvVars = [
|
||||||
"ENABLE_AXE_SELENIUM=${env.ENABLE_AXE_SELENIUM}",
|
"ENABLE_AXE_SELENIUM=${env.ENABLE_AXE_SELENIUM}",
|
||||||
'POSTGRES_PASSWORD=sekret',
|
'POSTGRES_PASSWORD=sekret'
|
||||||
'SELENIUM_VERSION=3.141.59-20210929'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
def rspecqEnvVars = baseEnvVars + [
|
def rspecqEnvVars = baseEnvVars + [
|
||||||
|
@ -70,8 +69,7 @@ def createDistribution(nestedStages) {
|
||||||
def createLegacyDistribution(nestedStages) {
|
def createLegacyDistribution(nestedStages) {
|
||||||
def setupNodeHook = this.&setupNode
|
def setupNodeHook = this.&setupNode
|
||||||
def baseEnvVars = [
|
def baseEnvVars = [
|
||||||
'POSTGRES_PASSWORD=sekret',
|
'POSTGRES_PASSWORD=sekret'
|
||||||
'SELENIUM_VERSION=3.141.59-20210929'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
// Used only for crystalball map generation
|
// Used only for crystalball map generation
|
||||||
|
|
|
@ -245,9 +245,11 @@ To enable Selenium: Add `docker-compose/selenium.override.yml` to your `COMPOSE_
|
||||||
The container used to run the selenium browser is only started when spinning up
|
The container used to run the selenium browser is only started when spinning up
|
||||||
all docker-compose containers, or when specified explicitly. The selenium
|
all docker-compose containers, or when specified explicitly. The selenium
|
||||||
container needs to be started before running any specs that require selenium.
|
container needs to be started before running any specs that require selenium.
|
||||||
|
Select a browser to run in selenium through config/selenium.yml and then ensure
|
||||||
|
that only the corresponding browser is configured in selenium.override.yml.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker-compose up selenium-firefox # or selenium-chrome or selenium-edge
|
docker-compose up -d selenium-hub
|
||||||
```
|
```
|
||||||
|
|
||||||
With the container running, you should be able to open a VNC session:
|
With the container running, you should be able to open a VNC session:
|
||||||
|
|
|
@ -6,20 +6,13 @@ services:
|
||||||
- selenium-chrome
|
- selenium-chrome
|
||||||
- canvasrceapi
|
- canvasrceapi
|
||||||
environment:
|
environment:
|
||||||
remote_url: http://selenium-chrome:4444/wd/hub
|
remote_url: http://selenium-hub:4444/wd/hub
|
||||||
browser: chrome
|
browser: chrome
|
||||||
RCE_HOST: "http://canvasrceapi"
|
RCE_HOST: "http://canvasrceapi"
|
||||||
# these are so we can use prod compiled assets in test environment
|
# these are so we can use prod compiled assets in test environment
|
||||||
USE_OPTIMIZED_JS: 'true'
|
USE_OPTIMIZED_JS: 'true'
|
||||||
SASS_STYLE: 'compressed'
|
SASS_STYLE: 'compressed'
|
||||||
|
|
||||||
selenium-chrome:
|
|
||||||
image: starlord.inscloudgate.net/jenkins/selenium-chrome:$SELENIUM_VERSION
|
|
||||||
environment:
|
|
||||||
SCREEN_WIDTH: 1680
|
|
||||||
SCREEN_HEIGHT: 1050
|
|
||||||
init: true
|
|
||||||
|
|
||||||
canvasrceapi:
|
canvasrceapi:
|
||||||
image: starlord.inscloudgate.net/jenkins/canvas-rce-api
|
image: starlord.inscloudgate.net/jenkins/canvas-rce-api
|
||||||
environment:
|
environment:
|
||||||
|
@ -33,3 +26,53 @@ services:
|
||||||
STATSD_HOST: 127.0.0.1
|
STATSD_HOST: 127.0.0.1
|
||||||
STATSD_PORT: 8125
|
STATSD_PORT: 8125
|
||||||
init: true
|
init: true
|
||||||
|
|
||||||
|
selenium-hub:
|
||||||
|
image: starlord.inscloudgate.net/jenkins/selenium-hub:4.1.2-20220217
|
||||||
|
environment:
|
||||||
|
GRID_MAX_SESSION: 64
|
||||||
|
GRID_BROWSER_TIMEOUT: 3000
|
||||||
|
|
||||||
|
selenium-chrome: &NODE_CHROME
|
||||||
|
image: starlord.inscloudgate.net/jenkins/selenium-node-chrome:98.0
|
||||||
|
environment: &NODE_CHROME_ENV
|
||||||
|
SE_EVENT_BUS_HOST: selenium-hub
|
||||||
|
SE_EVENT_BUS_PUBLISH_PORT: 4442
|
||||||
|
SE_EVENT_BUS_SUBSCRIBE_PORT: 4443
|
||||||
|
HUB_PORT_4444_TCP_ADDR: selenium-hub
|
||||||
|
HUB_PORT_4444_TCP_PORT: 4444
|
||||||
|
SE_NODE_HOST: selenium-chrome
|
||||||
|
JAVA_OPTS: '-Dwebdriver.chrome.whitelistedIps='
|
||||||
|
init: true
|
||||||
|
links:
|
||||||
|
- selenium-hub
|
||||||
|
|
||||||
|
selenium-chrome2:
|
||||||
|
<<: *NODE_CHROME
|
||||||
|
environment:
|
||||||
|
<<: *NODE_CHROME_ENV
|
||||||
|
SE_NODE_HOST: selenium-chrome2
|
||||||
|
|
||||||
|
selenium-chrome3:
|
||||||
|
<<: *NODE_CHROME
|
||||||
|
environment:
|
||||||
|
<<: *NODE_CHROME_ENV
|
||||||
|
SE_NODE_HOST: selenium-chrome3
|
||||||
|
|
||||||
|
selenium-chrome4:
|
||||||
|
<<: *NODE_CHROME
|
||||||
|
environment:
|
||||||
|
<<: *NODE_CHROME_ENV
|
||||||
|
SE_NODE_HOST: selenium-chrome4
|
||||||
|
|
||||||
|
selenium-chrome5:
|
||||||
|
<<: *NODE_CHROME
|
||||||
|
environment:
|
||||||
|
<<: *NODE_CHROME_ENV
|
||||||
|
SE_NODE_HOST: selenium-chrome5
|
||||||
|
|
||||||
|
selenium-chrome6:
|
||||||
|
<<: *NODE_CHROME
|
||||||
|
environment:
|
||||||
|
<<: *NODE_CHROME_ENV
|
||||||
|
SE_NODE_HOST: selenium-chrome6
|
|
@ -1,6 +1,6 @@
|
||||||
test:
|
test:
|
||||||
remote_url_firefox: http://selenium-firefox:4444/wd/hub
|
remote_url_firefox: http://selenium-hub:4444/wd/hub
|
||||||
remote_url_chrome: http://selenium-chrome:4444/wd/hub
|
remote_url_chrome: http://selenium-hub:4444/wd/hub
|
||||||
remote_url_edge: http://selenium-edge:4444/wd/hub
|
remote_url_edge: http://selenium-hub:4444/wd/hub
|
||||||
browser: chrome
|
browser: chrome
|
||||||
# auto_open_devtools: true
|
# auto_open_devtools: true
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
ARG SELENIUM_VERSION=3.141.59-20210929
|
FROM selenium/node-chrome:98.0
|
||||||
FROM selenium/standalone-chrome-debug:$SELENIUM_VERSION
|
|
||||||
|
|
||||||
COPY entry_point.sh /opt/bin/custom_entry_point.sh
|
COPY entry_point.sh /opt/bin/custom_entry_point.sh
|
||||||
USER root
|
USER root
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM selenium/standalone-edge:96.0
|
FROM selenium/node-edge:98.0
|
||||||
|
|
||||||
COPY entry_point.sh /opt/bin/custom_entry_point.sh
|
COPY entry_point.sh /opt/bin/custom_entry_point.sh
|
||||||
USER root
|
USER root
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Keep this image version tag synced with Gemfile.d/test.rb
|
# Keep this image version tag synced with Gemfile.d/test.rb
|
||||||
FROM selenium/standalone-firefox-debug:3.12.0-americium
|
FROM selenium/node-firefox:96.0
|
||||||
|
|
||||||
COPY entry_point.sh /opt/bin/custom_entry_point.sh
|
COPY entry_point.sh /opt/bin/custom_entry_point.sh
|
||||||
USER root
|
USER root
|
||||||
|
|
|
@ -5,35 +5,38 @@ version: '2.3'
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
links:
|
links:
|
||||||
- selenium-chrome
|
- selenium-hub
|
||||||
#- selenium-firefox
|
|
||||||
#- selenium-edge
|
|
||||||
|
|
||||||
selenium-chrome:
|
## We list all of the different standalone containers as `selenium-hub` since Jenkins
|
||||||
build: ./docker-compose/selenium-chrome
|
## will use the actual hub + node configuration instead of a standalone for performance
|
||||||
|
## reasons. Listing all of them as selenium-hub saves a great deal of configuration issues
|
||||||
|
|
||||||
|
## Chrome
|
||||||
|
selenium-hub:
|
||||||
|
image: selenium/standalone-chrome
|
||||||
|
environment:
|
||||||
|
SE_NODE_GRID_URL: selenium-hub:4444/wd/hub
|
||||||
|
VIRTUAL_HOST: seleniumch.docker
|
||||||
|
init: true
|
||||||
ports:
|
ports:
|
||||||
- 5901:5900
|
- 5901:5900
|
||||||
environment:
|
|
||||||
VIRTUAL_HOST: seleniumch.docker
|
|
||||||
remote_url: http://seleniumch.docker/wd/hub
|
|
||||||
browser: chrome
|
|
||||||
|
|
||||||
# selenium-firefox:
|
## Firefox
|
||||||
# build: ./docker-compose/selenium-firefox
|
# selenium-hub:
|
||||||
|
# image: selenium/standalone-firefox
|
||||||
|
# environment:
|
||||||
|
# SE_NODE_GRID_URL: selenium-hub:4444/wd/hub
|
||||||
|
# VIRTUAL_HOST: seleniumff.docker
|
||||||
|
# init: true
|
||||||
# ports:
|
# ports:
|
||||||
# - 5900:5900
|
# - 5900:5900
|
||||||
# environment:
|
|
||||||
# VIRTUAL_HOST: seleniumff.docker
|
|
||||||
# remote_url: http://seleniumff.docker/wd/hub
|
|
||||||
# browser: firefox
|
|
||||||
|
|
||||||
# selenium-edge:
|
## Edge
|
||||||
# build: ./docker-compose/selenium-edge
|
# selenium-hub:
|
||||||
|
# image: selenium/standalone-edge
|
||||||
|
# environment:
|
||||||
|
# SE_NODE_GRID_URL: selenium-hub:4444/wd/hub
|
||||||
|
# VIRTUAL_HOST: seleniumedge.docker
|
||||||
|
# init: true
|
||||||
# ports:
|
# ports:
|
||||||
# - 5902:5900
|
# - 5902:5900
|
||||||
# environment:
|
|
||||||
# VIRTUAL_HOST: seleniumedge.docker
|
|
||||||
# remote_url: http://seleniumedge.docker/wd/hub
|
|
||||||
# browser: edge
|
|
||||||
# shm_size: 2gb
|
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,7 @@ function display_next_steps {
|
||||||
echo ':docker-compose/selenium.override.yml' >> .env
|
echo ':docker-compose/selenium.override.yml' >> .env
|
||||||
|
|
||||||
build the selenium container
|
build the selenium container
|
||||||
${DOCKER_COMMAND} build selenium-chrome
|
${DOCKER_COMMAND} build selenium-hub
|
||||||
|
|
||||||
run selenium
|
run selenium
|
||||||
${DOCKER_COMMAND} run --rm web bundle exec rspec spec/selenium
|
${DOCKER_COMMAND} run --rm web bundle exec rspec spec/selenium
|
||||||
|
|
|
@ -29,26 +29,22 @@ describe "account authentication" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "sso settings" do
|
describe "sso settings" do
|
||||||
let(:login_handle_name) { f("#sso_settings_login_handle_name") }
|
|
||||||
let(:change_password_url) { f("#sso_settings_change_password_url") }
|
|
||||||
let(:auth_discovery_url) { f("#sso_settings_auth_discovery_url") }
|
|
||||||
|
|
||||||
it "saves", priority: "1" do
|
it "saves", priority: "1" do
|
||||||
add_sso_config
|
add_sso_config
|
||||||
expect(login_handle_name).to have_value "login"
|
expect(f("#sso_settings_login_handle_name")).to have_value "login"
|
||||||
expect(change_password_url).to have_value "http://test.example.com"
|
expect(f("#sso_settings_change_password_url")).to have_value "http://test.example.com"
|
||||||
expect(auth_discovery_url).to have_value "http://test.example.com"
|
expect(f("#sso_settings_auth_discovery_url")).to have_value "http://test.example.com"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "updates", priority: "1" do
|
it "updates", priority: "1" do
|
||||||
add_sso_config
|
add_sso_config
|
||||||
login_handle_name.clear
|
f("#sso_settings_login_handle_name").clear
|
||||||
change_password_url.clear
|
f("#sso_settings_change_password_url").clear
|
||||||
auth_discovery_url.clear
|
f("#sso_settings_auth_discovery_url").clear
|
||||||
f("#edit_sso_settings button[type='submit']").click
|
f("#edit_sso_settings button[type='submit']").click
|
||||||
expect(login_handle_name).not_to have_value "login"
|
expect(f("#sso_settings_login_handle_name")).not_to have_value "login"
|
||||||
expect(change_password_url).not_to have_value "http://test.example.com"
|
expect(f("#sso_settings_change_password_url")).not_to have_value "http://test.example.com"
|
||||||
expect(auth_discovery_url).not_to have_value "http://test.example.com"
|
expect(f("#sso_settings_auth_discovery_url")).not_to have_value "http://test.example.com"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -43,10 +43,6 @@ describe "new account user search" do
|
||||||
user_session(@user)
|
user_session(@user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def wait_for_loading_to_disappear
|
|
||||||
expect(f('[data-automation="users list"]')).not_to contain_css("tr:nth-child(2)")
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "with default page visit" do
|
describe "with default page visit" do
|
||||||
before do
|
before do
|
||||||
@user.update_attribute(:name, "Test User")
|
@user.update_attribute(:name, "Test User")
|
||||||
|
@ -95,7 +91,9 @@ describe "new account user search" do
|
||||||
user_with_pseudonym(account: @account, name: "diffrient user")
|
user_with_pseudonym(account: @account, name: "diffrient user")
|
||||||
refresh_page
|
refresh_page
|
||||||
user_search_box.send_keys("Test")
|
user_search_box.send_keys("Test")
|
||||||
wait_for_loading_to_disappear
|
wait_for_ajaximations
|
||||||
|
wait_for(method: nil, timeout: 0.5) { fj("title:contains('Loading')").displayed? }
|
||||||
|
wait_for_no_such_element { fj("title:contains('Loading')") }
|
||||||
expect(results_rows.count).to eq 1
|
expect(results_rows.count).to eq 1
|
||||||
expect(results_rows.first).to include_text("Test")
|
expect(results_rows.first).to include_text("Test")
|
||||||
end
|
end
|
||||||
|
|
|
@ -92,7 +92,7 @@ module AssignmentsIndexPage
|
||||||
end
|
end
|
||||||
|
|
||||||
def bulk_edit_tr_rows
|
def bulk_edit_tr_rows
|
||||||
ff("#bulkEditRoot tbody tr")
|
ff("#bulkEditRoot [role='table'] [role='row']")
|
||||||
end
|
end
|
||||||
|
|
||||||
def bulk_edit_loading_spinner
|
def bulk_edit_loading_spinner
|
||||||
|
|
|
@ -31,7 +31,7 @@ describe "browser" do
|
||||||
|
|
||||||
get("/login")
|
get("/login")
|
||||||
driver.execute_script("window.console.log('#{sample_msg}')")
|
driver.execute_script("window.console.log('#{sample_msg}')")
|
||||||
browser_logs = driver.manage.logs.get(:browser)
|
browser_logs = driver.logs.get(:browser)
|
||||||
|
|
||||||
expect(browser_logs.map(&:message)).to include(a_string_matching(sample_msg))
|
expect(browser_logs.map(&:message)).to include(a_string_matching(sample_msg))
|
||||||
end
|
end
|
||||||
|
|
|
@ -192,7 +192,7 @@ shared_context "in-process server selenium tests" do
|
||||||
example.metadata[:page_html] = document.to_html
|
example.metadata[:page_html] = document.to_html
|
||||||
end
|
end
|
||||||
|
|
||||||
browser_logs = driver.manage.logs.get(:browser) rescue nil
|
browser_logs = driver.logs.get(:browser) rescue nil
|
||||||
|
|
||||||
# log INSTUI deprecation warnings
|
# log INSTUI deprecation warnings
|
||||||
if browser_logs.present?
|
if browser_logs.present?
|
||||||
|
|
|
@ -126,8 +126,8 @@ describe "native canvas conditional release" do
|
||||||
assignment = assignment_model(course: @course, points_possible: 100)
|
assignment = assignment_model(course: @course, points_possible: 100)
|
||||||
get "/courses/#{@course.id}/assignments/#{assignment.id}/edit"
|
get "/courses/#{@course.id}/assignments/#{assignment.id}/edit"
|
||||||
ConditionalReleaseObjects.conditional_release_link.click
|
ConditionalReleaseObjects.conditional_release_link.click
|
||||||
replace_content(ConditionalReleaseObjects.division_cutoff1, "72")
|
ConditionalReleaseObjects.replace_mastery_path_scores(ConditionalReleaseObjects.division_cutoff1, "70", "72")
|
||||||
replace_content(ConditionalReleaseObjects.division_cutoff2, "47")
|
ConditionalReleaseObjects.replace_mastery_path_scores(ConditionalReleaseObjects.division_cutoff2, "40", "47")
|
||||||
ConditionalReleaseObjects.division_cutoff2.send_keys :tab
|
ConditionalReleaseObjects.division_cutoff2.send_keys :tab
|
||||||
|
|
||||||
expect(ConditionalReleaseObjects.division_cutoff1.attribute("value")).to eq("72 pts")
|
expect(ConditionalReleaseObjects.division_cutoff1.attribute("value")).to eq("72 pts")
|
||||||
|
@ -188,10 +188,10 @@ describe "native canvas conditional release" do
|
||||||
get "/courses/#{@course.id}/assignments/#{assignment.id}/edit"
|
get "/courses/#{@course.id}/assignments/#{assignment.id}/edit"
|
||||||
ConditionalReleaseObjects.conditional_release_link.click
|
ConditionalReleaseObjects.conditional_release_link.click
|
||||||
|
|
||||||
replace_content(ConditionalReleaseObjects.division_cutoff1, "")
|
ConditionalReleaseObjects.replace_mastery_path_scores(ConditionalReleaseObjects.division_cutoff1, "70", "")
|
||||||
expect(ConditionalReleaseObjects.must_not_be_empty_exists?).to eq(true)
|
expect(ConditionalReleaseObjects.must_not_be_empty_exists?).to eq(true)
|
||||||
|
|
||||||
replace_content(ConditionalReleaseObjects.division_cutoff1, "35")
|
ConditionalReleaseObjects.replace_mastery_path_scores(ConditionalReleaseObjects.division_cutoff1, "", "35")
|
||||||
expect(ConditionalReleaseObjects.these_scores_are_out_of_order_exists?).to eq(true)
|
expect(ConditionalReleaseObjects.these_scores_are_out_of_order_exists?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,11 @@ class ConditionalReleaseObjects
|
||||||
element_exists?("#conditional_content")
|
element_exists?("#conditional_content")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def replace_mastery_path_scores(element, current_value, new_value)
|
||||||
|
current_value.length.times { element.send_keys(:backspace) }
|
||||||
|
element.send_keys(new_value)
|
||||||
|
end
|
||||||
|
|
||||||
# Assignment Index Page
|
# Assignment Index Page
|
||||||
|
|
||||||
def assignment_kebob(page_title)
|
def assignment_kebob(page_title)
|
||||||
|
|
|
@ -611,7 +611,8 @@ describe "content migrations", :non_parallel do
|
||||||
ff("[name=selective_import]")[0].click
|
ff("[name=selective_import]")[0].click
|
||||||
submit
|
submit
|
||||||
run_jobs
|
run_jobs
|
||||||
expect(f(".migrationProgressItem .progressStatus")).to include_text("Completed")
|
# Wait until the item is imported on the back-end, otherwise the selenium tools will fail the test due to runtime
|
||||||
|
keep_trying_until { ContentMigration.last.workflow_state == "imported" }
|
||||||
@course.reload
|
@course.reload
|
||||||
expect(@course.announcements.last.locked).to be_truthy
|
expect(@course.announcements.last.locked).to be_truthy
|
||||||
expect(@course.lock_all_announcements).to be_truthy
|
expect(@course.lock_all_announcements).to be_truthy
|
||||||
|
@ -631,7 +632,8 @@ describe "content migrations", :non_parallel do
|
||||||
ff("[name=selective_import]")[0].click
|
ff("[name=selective_import]")[0].click
|
||||||
submit
|
submit
|
||||||
run_jobs
|
run_jobs
|
||||||
expect(f(".migrationProgressItem .progressStatus")).to include_text("Completed")
|
# Wait until the item is imported on the back-end, otherwise the selenium tools will fail the test due to runtime
|
||||||
|
keep_trying_until { ContentMigration.last.workflow_state == "imported" }
|
||||||
@course.reload
|
@course.reload
|
||||||
expect(@course.discussion_topics.last.allow_rating).to be_truthy
|
expect(@course.discussion_topics.last.allow_rating).to be_truthy
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,6 +30,11 @@ describe "course copy" do
|
||||||
expect(header.text).to eq @course.course_code
|
expect(header.text).to eq @course.course_code
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def wait_for_migration_to_complete
|
||||||
|
completed_status = fj("div.progressStatus:contains('Completed')")
|
||||||
|
keep_trying_until(5) { completed_status.displayed? == true }
|
||||||
|
end
|
||||||
|
|
||||||
it "copies the course" do
|
it "copies the course" do
|
||||||
course_with_admin_logged_in
|
course_with_admin_logged_in
|
||||||
@course.syllabus_body = "<p>haha</p>"
|
@course.syllabus_body = "<p>haha</p>"
|
||||||
|
@ -37,11 +42,12 @@ describe "course copy" do
|
||||||
@course.default_view = "modules"
|
@course.default_view = "modules"
|
||||||
@course.wiki_pages.create!(title: "hi", body: "Whatever")
|
@course.wiki_pages.create!(title: "hi", body: "Whatever")
|
||||||
@course.save!
|
@course.save!
|
||||||
|
|
||||||
get "/courses/#{@course.id}/copy"
|
get "/courses/#{@course.id}/copy"
|
||||||
expect_new_page_load { f('button[type="submit"]').click }
|
expect_new_page_load { f('button[type="submit"]').click }
|
||||||
|
expect(f("div.progressStatus").text.include?("Queued")).to eq(true)
|
||||||
run_jobs
|
run_jobs
|
||||||
expect(f("div.progressStatus span")).to include_text "Completed"
|
wait_for_ajaximations
|
||||||
|
wait_for_migration_to_complete
|
||||||
|
|
||||||
@new_course = Course.last
|
@new_course = Course.last
|
||||||
expect(@new_course.syllabus_body).to eq @course.syllabus_body
|
expect(@new_course.syllabus_body).to eq @course.syllabus_body
|
||||||
|
@ -123,12 +129,11 @@ describe "course copy" do
|
||||||
get "/courses/#{@course.id}/settings"
|
get "/courses/#{@course.id}/settings"
|
||||||
link = f(".copy_course_link")
|
link = f(".copy_course_link")
|
||||||
expect(link).to be_displayed
|
expect(link).to be_displayed
|
||||||
|
|
||||||
expect_new_page_load { link.click }
|
expect_new_page_load { link.click }
|
||||||
|
|
||||||
expect_new_page_load { f('button[type="submit"]').click }
|
expect_new_page_load { f('button[type="submit"]').click }
|
||||||
run_jobs
|
run_jobs
|
||||||
expect(f("div.progressStatus span")).to include_text "Completed"
|
wait_for_ajaximations
|
||||||
|
wait_for_migration_to_complete
|
||||||
|
|
||||||
@new_course = subaccount.courses.where("id <>?", @course.id).last
|
@new_course = subaccount.courses.where("id <>?", @course.id).last
|
||||||
expect(@new_course.syllabus_body).to eq @course.syllabus_body
|
expect(@new_course.syllabus_body).to eq @course.syllabus_body
|
||||||
|
|
|
@ -589,6 +589,7 @@ describe "context modules" do
|
||||||
expect(button.text).to eq("Expand All")
|
expect(button.text).to eq("Expand All")
|
||||||
refresh_page
|
refresh_page
|
||||||
assert_collapsed
|
assert_collapsed
|
||||||
|
button = f("button#expand_collapse_all")
|
||||||
button.click
|
button.click
|
||||||
wait_for_ajaximations
|
wait_for_ajaximations
|
||||||
assert_expanded
|
assert_expanded
|
||||||
|
|
|
@ -49,7 +49,7 @@ describe "cross-listing" do
|
||||||
# crosslist a valid course
|
# crosslist a valid course
|
||||||
course_id.click
|
course_id.click
|
||||||
course_id.clear
|
course_id.clear
|
||||||
course_id.send_keys([:control, "a"], @course2.id.to_s, "\n")
|
course_id.send_keys(@course2.id.to_s, "\n")
|
||||||
expect(course_name).to include_text(@course2.name)
|
expect(course_name).to include_text(@course2.name)
|
||||||
expect(form.find_element(:id, "course_autocomplete_id")).to have_attribute(:value, @course.id.to_s)
|
expect(form.find_element(:id, "course_autocomplete_id")).to have_attribute(:value, @course.id.to_s)
|
||||||
expect(submit_btn).not_to have_class("disabled")
|
expect(submit_btn).not_to have_class("disabled")
|
||||||
|
@ -107,7 +107,7 @@ describe "cross-listing" do
|
||||||
# k, let's crosslist to the other course
|
# k, let's crosslist to the other course
|
||||||
form.find_element(:css, "#course_id").click
|
form.find_element(:css, "#course_id").click
|
||||||
form.find_element(:css, "#course_id").clear
|
form.find_element(:css, "#course_id").clear
|
||||||
form.find_element(:css, "#course_id").send_keys([:control, "a"], other_course.id.to_s, "\n")
|
form.find_element(:css, "#course_id").send_keys(other_course.id.to_s, "\n")
|
||||||
expect(f("#course_autocomplete_name")).to include_text other_course.name
|
expect(f("#course_autocomplete_name")).to include_text other_course.name
|
||||||
expect(form.find_element(:css, "#course_autocomplete_id")).to have_attribute(:value, other_course.id.to_s)
|
expect(form.find_element(:css, "#course_autocomplete_id")).to have_attribute(:value, other_course.id.to_s)
|
||||||
|
|
||||||
|
|
|
@ -104,8 +104,8 @@ describe "discussions" do
|
||||||
wait_for_ajaximations
|
wait_for_ajaximations
|
||||||
fj(".ic-tokeninput-option:visible:first").click
|
fj(".ic-tokeninput-option:visible:first").click
|
||||||
wait_for_ajaximations
|
wait_for_ajaximations
|
||||||
fj(".datePickerDateField[data-date-type='due_at']:first").send_keys(format_date_for_view(due_at1))
|
fj(".datePickerDateField[data-date-type='due_at']:first").send_keys(format_date_for_view(due_at1), :tab)
|
||||||
|
wait_for_ajaximations
|
||||||
f("#add_due_date").click
|
f("#add_due_date").click
|
||||||
wait_for_ajaximations
|
wait_for_ajaximations
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ describe "Gradebook" do
|
||||||
Gradebook::Cells.open_tray(@student_1, group_assignment)
|
Gradebook::Cells.open_tray(@student_1, group_assignment)
|
||||||
Gradebook::GradeDetailTray.add_new_comment(@comment_text)
|
Gradebook::GradeDetailTray.add_new_comment(@comment_text)
|
||||||
|
|
||||||
Gradebook::GradeDetailTray.close_tray_button.click
|
Gradebook::GradeDetailTray.click_close_tray_button
|
||||||
|
|
||||||
# make sure it's on the other student's submission
|
# make sure it's on the other student's submission
|
||||||
Gradebook::Cells.open_tray(@student_2, group_assignment)
|
Gradebook::Cells.open_tray(@student_2, group_assignment)
|
||||||
|
|
|
@ -412,6 +412,8 @@ describe "Gradebook" do
|
||||||
grading_cell.click
|
grading_cell.click
|
||||||
|
|
||||||
Gradebook::Cells.edit_grade(student, essay_quiz.assignment, 10)
|
Gradebook::Cells.edit_grade(student, essay_quiz.assignment, 10)
|
||||||
|
# Re-select element in case it's gone stale
|
||||||
|
grading_cell = Gradebook::Cells.grading_cell(student, essay_quiz.assignment)
|
||||||
expect(grading_cell).not_to contain_css(".icon-not-graded")
|
expect(grading_cell).not_to contain_css(".icon-not-graded")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,8 +31,8 @@ module Gradebook
|
||||||
f("#SubmissionTray__Content")
|
f("#SubmissionTray__Content")
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.close_tray_button
|
def self.click_close_tray_button
|
||||||
fj("button:contains('Close submission tray')")
|
force_click("button:contains('Close submission tray')")
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.avatar
|
def self.avatar
|
||||||
|
|
|
@ -129,6 +129,7 @@ module AssignmentOverridesSeleniumHelper
|
||||||
last_due_at_element.send_keys(opts.fetch(:due_at, Time.zone.now.advance(days: 5)))
|
last_due_at_element.send_keys(opts.fetch(:due_at, Time.zone.now.advance(days: 5)))
|
||||||
last_unlock_at_element.send_keys(opts.fetch(:unlock_at, Time.zone.now.advance(days: -1)))
|
last_unlock_at_element.send_keys(opts.fetch(:unlock_at, Time.zone.now.advance(days: -1)))
|
||||||
last_lock_at_element.send_keys(opts.fetch(:lock_at, Time.zone.now.advance(days: 5)))
|
last_lock_at_element.send_keys(opts.fetch(:lock_at, Time.zone.now.advance(days: 5)))
|
||||||
|
last_lock_at_element.send_keys(:tab)
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_vdd_time(override_context)
|
def find_vdd_time(override_context)
|
||||||
|
|
|
@ -221,21 +221,8 @@ describe "RCE next tests", ignore_js_errors: true do
|
||||||
body: "<p id='para'><a id='lnk' href='http://example.com'>delete me</a></p>"
|
body: "<p id='para'><a id='lnk' href='http://example.com'>delete me</a></p>"
|
||||||
)
|
)
|
||||||
visit_existing_wiki_edit(@course, "title")
|
visit_existing_wiki_edit(@course, "title")
|
||||||
|
|
||||||
f("##{rce_page_body_ifr_id}").click
|
f("##{rce_page_body_ifr_id}").click
|
||||||
f("##{rce_page_body_ifr_id}").send_keys(
|
f("##{rce_page_body_ifr_id}").send_keys([:control, "a"], :backspace)
|
||||||
%i[shift arrow_left],
|
|
||||||
%i[shift arrow_left],
|
|
||||||
%i[shift arrow_left],
|
|
||||||
%i[shift arrow_left],
|
|
||||||
%i[shift arrow_left],
|
|
||||||
%i[shift arrow_left],
|
|
||||||
%i[shift arrow_left],
|
|
||||||
%i[shift arrow_left],
|
|
||||||
%i[shift arrow_left],
|
|
||||||
%i[shift arrow_left]
|
|
||||||
)
|
|
||||||
f("##{rce_page_body_ifr_id}").send_keys(:enter)
|
|
||||||
|
|
||||||
in_frame rce_page_body_ifr_id do
|
in_frame rce_page_body_ifr_id do
|
||||||
expect(f("#para").text).to eql ""
|
expect(f("#para").text).to eql ""
|
||||||
|
@ -1175,7 +1162,7 @@ describe "RCE next tests", ignore_js_errors: true do
|
||||||
it "opens keyboard shortcut modal with alt-f8" do
|
it "opens keyboard shortcut modal with alt-f8" do
|
||||||
visit_front_page_edit(@course)
|
visit_front_page_edit(@course)
|
||||||
rce = f(".tox-edit-area__iframe")
|
rce = f(".tox-edit-area__iframe")
|
||||||
rce.send_keys %i[alt f8]
|
rce.send_keys(:alt, :f8)
|
||||||
|
|
||||||
expect(keyboard_shortcut_modal).to be_displayed
|
expect(keyboard_shortcut_modal).to be_displayed
|
||||||
end
|
end
|
||||||
|
@ -1184,7 +1171,7 @@ describe "RCE next tests", ignore_js_errors: true do
|
||||||
visit_front_page_edit(@course)
|
visit_front_page_edit(@course)
|
||||||
rce = f(".tox-edit-area__iframe")
|
rce = f(".tox-edit-area__iframe")
|
||||||
expect(f(".tox-menubar")).to be_displayed # always show menubar now
|
expect(f(".tox-menubar")).to be_displayed # always show menubar now
|
||||||
rce.send_keys %i[alt f9]
|
rce.send_keys(:alt, :f9)
|
||||||
|
|
||||||
expect(f(".tox-menubar")).to be_displayed
|
expect(f(".tox-menubar")).to be_displayed
|
||||||
expect(fj('.tox-menubar button:contains("Edit")')).to eq(driver.switch_to.active_element)
|
expect(fj('.tox-menubar button:contains("Edit")')).to eq(driver.switch_to.active_element)
|
||||||
|
@ -1193,7 +1180,7 @@ describe "RCE next tests", ignore_js_errors: true do
|
||||||
it "focuses the toolbar with alt-f10" do
|
it "focuses the toolbar with alt-f10" do
|
||||||
visit_front_page_edit(@course)
|
visit_front_page_edit(@course)
|
||||||
rce = f(".tox-edit-area__iframe")
|
rce = f(".tox-edit-area__iframe")
|
||||||
rce.send_keys %i[alt f10]
|
rce.send_keys(:alt, :f10)
|
||||||
|
|
||||||
expect(fj('.tox-toolbar__primary button:contains("12pt")')).to eq(
|
expect(fj('.tox-toolbar__primary button:contains("12pt")')).to eq(
|
||||||
driver.switch_to.active_element
|
driver.switch_to.active_element
|
||||||
|
|
|
@ -339,8 +339,7 @@ module CustomSeleniumActions
|
||||||
tinymce_element = f("body")
|
tinymce_element = f("body")
|
||||||
until tinymce_element.text.empty?
|
until tinymce_element.text.empty?
|
||||||
tinymce_element.click
|
tinymce_element.click
|
||||||
tinymce_element.send_keys(Array.new(100, :backspace))
|
tinymce_element.send_keys([:control, "a"], :backspace)
|
||||||
tinymce_element = f("body")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -515,14 +514,6 @@ module CustomSeleniumActions
|
||||||
keys = value.to_s.empty? ? [:backspace] : []
|
keys = value.to_s.empty? ? [:backspace] : []
|
||||||
keys << value
|
keys << value
|
||||||
el.send_keys(*keys)
|
el.send_keys(*keys)
|
||||||
count = 0
|
|
||||||
until el["value"] == value.to_s
|
|
||||||
break if count > 1
|
|
||||||
|
|
||||||
count += 1
|
|
||||||
driver.execute_script("arguments[0].select();", el)
|
|
||||||
el.send_keys(*keys)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
el.send_keys(:tab) if options[:tab_out]
|
el.send_keys(:tab) if options[:tab_out]
|
||||||
|
|
|
@ -22,20 +22,15 @@ require "selenium-webdriver"
|
||||||
module Selenium
|
module Selenium
|
||||||
module WebDriver
|
module WebDriver
|
||||||
module Remote
|
module Remote
|
||||||
module W3C
|
class Bridge
|
||||||
class Bridge
|
def log(type)
|
||||||
COMMANDS = remove_const(:COMMANDS).dup
|
command(:get_log, "session/:session_id/log", :post)
|
||||||
COMMANDS[:get_log] = [:post, "session/:session_id/log"]
|
data = execute :get_log, {}, { type: type.to_s }
|
||||||
COMMANDS.freeze
|
|
||||||
|
|
||||||
def log(type)
|
Array(data).map do |l|
|
||||||
data = execute :get_log, {}, { type: type.to_s }
|
LogEntry.new l.fetch("level", "UNKNOWN"), l.fetch("timestamp"), l.fetch("message")
|
||||||
|
rescue KeyError
|
||||||
Array(data).map do |l|
|
next
|
||||||
LogEntry.new l.fetch("level", "UNKNOWN"), l.fetch("timestamp"), l.fetch("message")
|
|
||||||
rescue KeyError
|
|
||||||
next
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,20 +23,6 @@ require_relative "common_helper_methods/custom_alert_actions"
|
||||||
require_relative "common_helper_methods/custom_screen_actions"
|
require_relative "common_helper_methods/custom_screen_actions"
|
||||||
require_relative "patches/selenium/webdriver/remote/w3c/bridge"
|
require_relative "patches/selenium/webdriver/remote/w3c/bridge"
|
||||||
|
|
||||||
# WebDriver uses port 7054 (the "locking port") as a mutex to ensure
|
|
||||||
# that we don't launch two Firefox instances at the same time. Each
|
|
||||||
# new instance you create will wait for the mutex before starting
|
|
||||||
# the browser, then release it as soon as the browser is open.
|
|
||||||
#
|
|
||||||
# The default port mutex wait timeout is 45 seconds.
|
|
||||||
# Bump it to 90 seconds as a stopgap for the recent flood of:
|
|
||||||
# `unable to bind to locking port 7054 within 45 seconds`
|
|
||||||
#
|
|
||||||
# TODO: Investigate why it's taking so long to launch Firefox, or
|
|
||||||
# what process is hogging port 7054.
|
|
||||||
Selenium::WebDriver::Firefox::Launcher.send :remove_const, :SOCKET_LOCK_TIMEOUT
|
|
||||||
Selenium::WebDriver::Firefox::Launcher::SOCKET_LOCK_TIMEOUT = 90
|
|
||||||
|
|
||||||
module SeleniumDriverSetup
|
module SeleniumDriverSetup
|
||||||
CONFIG = ConfigFile.load("selenium") || {}.freeze
|
CONFIG = ConfigFile.load("selenium") || {}.freeze
|
||||||
SECONDS_UNTIL_GIVING_UP = 10
|
SECONDS_UNTIL_GIVING_UP = 10
|
||||||
|
@ -275,9 +261,8 @@ module SeleniumDriverSetup
|
||||||
# by modifying 'chromedriver_version: <version>' for the version you want.
|
# by modifying 'chromedriver_version: <version>' for the version you want.
|
||||||
# otherwise this will use the default version matching what is used in docker.
|
# otherwise this will use the default version matching what is used in docker.
|
||||||
Webdrivers::Chromedriver.required_version = CONFIG[:chromedriver_version]
|
Webdrivers::Chromedriver.required_version = CONFIG[:chromedriver_version]
|
||||||
chrome_options = Selenium::WebDriver::Chrome::Options.new
|
|
||||||
|
|
||||||
Selenium::WebDriver.for :chrome, desired_capabilities: desired_capabilities, options: chrome_options
|
Selenium::WebDriver.for :chrome, capabilities: desired_capabilities
|
||||||
end
|
end
|
||||||
|
|
||||||
def ruby_safari_driver
|
def ruby_safari_driver
|
||||||
|
@ -287,16 +272,16 @@ module SeleniumDriverSetup
|
||||||
|
|
||||||
def ruby_edge_driver
|
def ruby_edge_driver
|
||||||
puts "Thread: provisioning local edge driver"
|
puts "Thread: provisioning local edge driver"
|
||||||
edge_options = Selenium::WebDriver::Edge::Options.new
|
Selenium::WebDriver.for :edge, capabilities: desired_capabilities
|
||||||
Selenium::WebDriver.for :edge, desired_capabilities: desired_capabilities, options: edge_options
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def selenium_remote_driver
|
def selenium_remote_driver
|
||||||
puts "Thread: provisioning remote #{browser} driver"
|
puts "Thread: provisioning remote #{browser} driver"
|
||||||
|
puts "Selenium_Url: #{selenium_url}"
|
||||||
driver = Selenium::WebDriver.for(
|
driver = Selenium::WebDriver.for(
|
||||||
:remote,
|
:remote,
|
||||||
url: selenium_url,
|
url: selenium_url,
|
||||||
desired_capabilities: desired_capabilities
|
capabilities: desired_capabilities
|
||||||
)
|
)
|
||||||
|
|
||||||
driver.file_detector = lambda do |args|
|
driver.file_detector = lambda do |args|
|
||||||
|
@ -311,37 +296,36 @@ module SeleniumDriverSetup
|
||||||
def desired_capabilities
|
def desired_capabilities
|
||||||
case browser
|
case browser
|
||||||
when :firefox
|
when :firefox
|
||||||
caps = Selenium::WebDriver::Remote::Capabilities.firefox
|
options = Selenium::WebDriver::Options.firefox
|
||||||
|
options.log_level = :debug
|
||||||
when :chrome
|
when :chrome
|
||||||
caps = Selenium::WebDriver::Remote::Capabilities.chrome
|
options = Selenium::WebDriver::Options.chrome
|
||||||
caps["goog:chromeOptions"] = {
|
options.add_argument("no-sandbox")
|
||||||
args: %w[disable-dev-shm-usage no-sandbox start-maximized]
|
options.add_argument("start-maximized")
|
||||||
}
|
options.add_argument("disable-dev-shm-usage")
|
||||||
caps["goog:loggingPrefs"] = {
|
options.logging_prefs = {
|
||||||
browser: "ALL"
|
browser: "ALL"
|
||||||
}
|
}
|
||||||
# put `auto_open_devtools: true` in your selenium.yml if you want to have
|
# put `auto_open_devtools: true` in your selenium.yml if you want to have
|
||||||
# the chrome dev tools open by default by selenium
|
# the chrome dev tools open by default by selenium
|
||||||
if CONFIG[:auto_open_devtools]
|
if CONFIG[:auto_open_devtools]
|
||||||
caps["goog:chromeOptions"][:args].append("auto-open-devtools-for-tabs")
|
options.add_argument("auto-open-devtools-for-tabs")
|
||||||
end
|
end
|
||||||
# put `headless: true` and `window_size: "<x>,<y>"` in your selenium.yml
|
# put `headless: true` and `window_size: "<x>,<y>"` in your selenium.yml
|
||||||
# if you want to run against headless chrome
|
# if you want to run against headless chrome
|
||||||
if CONFIG[:headless]
|
if CONFIG[:headless]
|
||||||
caps["goog:chromeOptions"][:args].append("headless")
|
options.add_argument("headless")
|
||||||
end
|
end
|
||||||
if CONFIG[:window_size].present?
|
|
||||||
caps["goog:chromeOptions"][:args].append("window-size=#{CONFIG[:window_size]}")
|
|
||||||
end
|
|
||||||
caps["unexpectedAlertBehaviour"] = "ignore"
|
|
||||||
when :edge
|
when :edge
|
||||||
caps = Selenium::WebDriver::Remote::Capabilities.edge
|
options = Selenium::WebDriver::Options.edge
|
||||||
|
options.add_argument("disable-dev-shm-usage")
|
||||||
when :safari
|
when :safari
|
||||||
# TODO: options for safari driver
|
# TODO: options for safari driver
|
||||||
else
|
else
|
||||||
raise "unsupported browser #{browser}"
|
raise "unsupported browser #{browser}"
|
||||||
end
|
end
|
||||||
caps
|
options.unhandled_prompt_behavior = "ignore"
|
||||||
|
options
|
||||||
end
|
end
|
||||||
|
|
||||||
def selenium_url
|
def selenium_url
|
||||||
|
@ -362,24 +346,7 @@ module SeleniumDriverSetup
|
||||||
def ruby_firefox_driver
|
def ruby_firefox_driver
|
||||||
puts "Thread: provisioning local firefox driver"
|
puts "Thread: provisioning local firefox driver"
|
||||||
Selenium::WebDriver.for(:firefox,
|
Selenium::WebDriver.for(:firefox,
|
||||||
profile: firefox_profile,
|
capabilities: desired_capabilities)
|
||||||
desired_capabilities: desired_capabilities)
|
|
||||||
end
|
|
||||||
|
|
||||||
def firefox_profile
|
|
||||||
if CONFIG[:firefox_path].present?
|
|
||||||
Selenium::WebDriver::Firefox::Binary.path = (CONFIG[:firefox_path]).to_s
|
|
||||||
end
|
|
||||||
profile = Selenium::WebDriver::Firefox::Profile.new
|
|
||||||
profile.add_extension Rails.root.join("spec/selenium/test_setup/JSErrorCollector.xpi")
|
|
||||||
profile.log_file = "/dev/stdout"
|
|
||||||
# firefox randomly reloads if/when it decides to download the OpenH264 codec, so don't let it
|
|
||||||
profile["media.gmp-manager.url"] = ""
|
|
||||||
|
|
||||||
if CONFIG[:firefox_profile].present?
|
|
||||||
profile = Selenium::WebDriver::Firefox::Profile.from_name(CONFIG[:firefox_profile])
|
|
||||||
end
|
|
||||||
profile
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def_delegator :driver_capabilities, :browser_name
|
def_delegator :driver_capabilities, :browser_name
|
||||||
|
@ -517,18 +484,6 @@ module SeleniumDriverSetup
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# get some extra verbose logging from firefox for when things go wrong
|
|
||||||
Selenium::WebDriver::Firefox::Binary.class_eval do
|
|
||||||
def execute(*extra_args)
|
|
||||||
args = [self.class.path, "-no-remote"] + extra_args
|
|
||||||
SeleniumDriverSetup.browser_process = @process = ChildProcess.build(*args)
|
|
||||||
SeleniumDriverSetup.browser_log = @process.io.stdout = @process.io.stderr = Tempfile.new("firefox")
|
|
||||||
$DEBUG = true
|
|
||||||
@process.start
|
|
||||||
$DEBUG = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# make Wait play nicely with Timecop
|
# make Wait play nicely with Timecop
|
||||||
module Selenium::WebDriver::Wait::Time
|
module Selenium::WebDriver::Wait::Time
|
||||||
def self.now
|
def self.now
|
||||||
|
|
|
@ -256,4 +256,4 @@ Selenium::WebDriver::Element.prepend(SeleniumExtensions::FinderWaiting)
|
||||||
Selenium::WebDriver::Element.prepend(SeleniumExtensions::UnexpectedPageReloadProtectionElement)
|
Selenium::WebDriver::Element.prepend(SeleniumExtensions::UnexpectedPageReloadProtectionElement)
|
||||||
Selenium::WebDriver::Driver.prepend(SeleniumExtensions::PreventEarlyInteraction)
|
Selenium::WebDriver::Driver.prepend(SeleniumExtensions::PreventEarlyInteraction)
|
||||||
Selenium::WebDriver::Driver.prepend(SeleniumExtensions::FinderWaiting)
|
Selenium::WebDriver::Driver.prepend(SeleniumExtensions::FinderWaiting)
|
||||||
Selenium::WebDriver::W3CActionBuilder.prepend(SeleniumExtensions::UnexpectedPageReloadProtectionActionBuilder)
|
Selenium::WebDriver::ActionBuilder.prepend(SeleniumExtensions::UnexpectedPageReloadProtectionActionBuilder)
|
||||||
|
|
Loading…
Reference in New Issue