├── test ├── fixtures │ └── cookbooks │ │ └── selenium_test │ │ ├── README.md │ │ ├── recipes │ │ ├── hub.rb │ │ ├── package.rb │ │ ├── package_update.rb │ │ ├── screenresolution.rb │ │ └── node.rb │ │ ├── attributes │ │ └── default.rb │ │ └── metadata.rb └── integration │ └── default │ ├── rspec │ ├── Gemfile │ ├── rspec_helper.rb │ └── firefox_spec.rb │ └── serverspec │ ├── serverspec_helper.rb │ └── default_spec.rb ├── .gitignore ├── spec ├── spec_helper.rb └── unit │ ├── default_spec.rb │ ├── hub_spec.rb │ └── node_spec.rb ├── Berksfile ├── .rubocop.yml ├── Gemfile ├── libraries ├── matchers.rb └── default.rb ├── .kitchen.localhost.yml ├── templates └── default │ ├── systemd.erb │ ├── org.seleniumhq.plist.erb │ ├── hub_config.erb │ ├── node_config.erb │ ├── upstart.erb │ └── sysvinit.erb ├── Rakefile ├── recipes ├── default.rb ├── hub.rb └── node.rb ├── .kitchen.vmware.yml ├── .kitchen.appveyor.yml ├── metadata.rb ├── attributes ├── default.rb ├── hub.rb └── node.rb ├── .kitchen.yml ├── CONTRIBUTING.md ├── resources ├── hub.rb └── node.rb ├── LICENSE ├── appveyor.yml ├── .travis.yml ├── providers ├── hub.rb └── node.rb ├── chefignore ├── .kitchen.docker.yml ├── CHANGELOG.md └── README.md /test/fixtures/cookbooks/selenium_test/README.md: -------------------------------------------------------------------------------- 1 | This is a test cookbook for use by chefspec and test-kitchen 2 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/selenium_test/recipes/hub.rb: -------------------------------------------------------------------------------- 1 | include_recipe 'java_se' 2 | 3 | include_recipe 'selenium::hub' 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | 4 | Berksfile.lock 5 | Gemfile.lock 6 | .kitchen/ 7 | .kitchen.local.yml 8 | .bundle/ 9 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/berkshelf' 3 | 4 | Chef::Config[:chef_gem_compile_time] = false 5 | ChefSpec::Coverage.start! 6 | -------------------------------------------------------------------------------- /test/integration/default/rspec/Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem 'rspec' 4 | gem 'rspec-expectations' 5 | gem 'selenium-webdriver', '~> 3.5' 6 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/selenium_test/attributes/default.rb: -------------------------------------------------------------------------------- 1 | default['selenium_test']['username'] = 'vagrant' 2 | default['selenium_test']['password'] = 'vagrant' 3 | -------------------------------------------------------------------------------- /Berksfile: -------------------------------------------------------------------------------- 1 | source 'https://supermarket.chef.io' 2 | 3 | metadata 4 | 5 | group :solo do 6 | cookbook 'selenium_test', path: 'test/fixtures/cookbooks/selenium_test' 7 | end 8 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - '.kitchen/*' 4 | 5 | LineLength: 6 | Max: 120 7 | 8 | MethodLength: 9 | Max: 40 10 | 11 | AbcSize: 12 | Max: 40 13 | 14 | BlockLength: 15 | Enabled: false 16 | -------------------------------------------------------------------------------- /test/integration/default/serverspec/serverspec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'serverspec' 2 | 3 | if (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM).nil? 4 | set :backend, :exec 5 | else 6 | set :backend, :cmd 7 | set :os, family: 'windows' 8 | end 9 | -------------------------------------------------------------------------------- /test/integration/default/rspec/rspec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rspec' 2 | require 'selenium-webdriver' 3 | 4 | MACOSX = RbConfig::CONFIG['host_os'].downcase =~ /darwin/ 5 | WINDOWS = RbConfig::CONFIG['host_os'] =~ /mingw|mswin/ 6 | 7 | RSpec.configure { |c| c.formatter = 'documentation' } 8 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'berkshelf' 4 | gem 'chef', '~> 12.0' 5 | gem 'chefspec' 6 | gem 'cookstyle' 7 | gem 'foodcritic' 8 | 9 | group :integration do 10 | gem 'kitchen-dokken' 11 | gem 'kitchen-localhost' 12 | gem 'kitchen-vagrant' 13 | gem 'test-kitchen' 14 | gem 'winrm-fs' 15 | end 16 | -------------------------------------------------------------------------------- /libraries/matchers.rb: -------------------------------------------------------------------------------- 1 | if defined?(ChefSpec) 2 | def install_selenium_hub(resource_name) 3 | ChefSpec::Matchers::ResourceMatcher.new(:selenium_hub, :install, resource_name) 4 | end 5 | 6 | def install_selenium_node(resource_name) 7 | ChefSpec::Matchers::ResourceMatcher.new(:selenium_node, :install, resource_name) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /.kitchen.localhost.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: localhost 4 | 5 | provisioner: 6 | name: chef_zero 7 | require_chef_omnibus: 12 8 | 9 | platforms: 10 | - name: macosx-10 11 | 12 | suites: 13 | - name: default 14 | run_list: 15 | - selenium_test::hub 16 | - selenium_test::node 17 | attributes: 18 | selenium_test: 19 | username: travis 20 | password: travis 21 | -------------------------------------------------------------------------------- /templates/default/systemd.erb: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=<%=@name%> 3 | After=syslog.target network.target 4 | 5 | [Service] 6 | Type=simple 7 | User=<%=@user%> 8 | 9 | <% if @xdisplay %> 10 | Environment=DISPLAY=<%=@xdisplay%> 11 | <% end %> 12 | ExecStart=<%=@exec%> <%=@args%> 13 | KillMode=process 14 | Restart=on-failure 15 | RestartSec=45s 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'cookstyle' 2 | require 'foodcritic' 3 | require 'rubocop/rake_task' 4 | require 'rspec/core/rake_task' 5 | 6 | RSpec::Core::RakeTask.new do |task| 7 | task.rspec_opts = '--color -f d' 8 | end 9 | RuboCop::RakeTask.new do |task| 10 | task.options << '--display-cop-names' 11 | end 12 | FoodCritic::Rake::LintTask.new(:foodcritic) 13 | 14 | task default: %i(foodcritic rubocop spec) 15 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/selenium_test/recipes/package.rb: -------------------------------------------------------------------------------- 1 | # selenium-webdriver includes ffi which requires the following dependencies 2 | case node[:platform_family] 3 | when 'debian' 4 | %w(gcc libffi-dev make).each do |pkg| 5 | package pkg do 6 | action :nothing 7 | end.run_action(:install) 8 | end 9 | when 'rhel', 'fedora' 10 | %w(gcc libffi-devel make).each do |pkg| 11 | package pkg 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/selenium_test/recipes/package_update.rb: -------------------------------------------------------------------------------- 1 | case node[:platform_family] 2 | when 'debian' 3 | # firefox runs at compile time and firefox package is not up to date on Ubuntu 4 | execute 'sudo apt-get update' do 5 | action :nothing 6 | end.run_action(:run) 7 | when 'rhel' 8 | # firefox runs at compile time and firefox package is not up to date on CentOS 9 | execute 'yum update -y' do 10 | action :nothing 11 | end.run_action(:run) 12 | end 13 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/selenium_test/metadata.rb: -------------------------------------------------------------------------------- 1 | name 'selenium_test' 2 | maintainer 'Dennis Hoer' 3 | maintainer_email 'dennis.hoer@gmail.com' 4 | license 'MIT' 5 | description 'Tests Selenium' 6 | long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) 7 | version '0.1.0' 8 | 9 | depends 'selenium' 10 | 11 | depends 'dmg' 12 | depends 'geckodriver' 13 | depends 'java_se', '~> 8.0' 14 | depends 'mozilla_firefox' 15 | depends 'windows_screenresolution' 16 | depends 'xvfb' 17 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/selenium_test/recipes/screenresolution.rb: -------------------------------------------------------------------------------- 1 | if platform?('windows') 2 | # Call windows_screenresolution after selenium_node because windows_screenresolution 3 | # will override auto-login created by selenium_node. 4 | windows_screenresolution node['selenium_test']['username'] do 5 | password node['selenium_test']['password'] 6 | width 1440 7 | height 900 8 | rdp_password 'S6M;b.v+{DTYAQW4' # Meets windows password policy requirements for a new user 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /recipes/default.rb: -------------------------------------------------------------------------------- 1 | package 'unzip' unless platform?('windows', 'mac_os_x') 2 | 3 | dirs = %w(config server) 4 | dirs.push('bin', 'log') if platform?('windows') 5 | 6 | dirs.each do |dir| 7 | directory "#{selenium_home}/#{dir}" do 8 | recursive true 9 | action :create 10 | end 11 | end 12 | 13 | url = node['selenium']['url'] 14 | target = "#{selenium_home}/server/#{url.split('/')[-1]}" 15 | 16 | remote_file target do 17 | source url 18 | mode '0775' 19 | not_if { ::File.exist?(target) } 20 | end 21 | 22 | link selenium_jar_link do 23 | to target 24 | end 25 | -------------------------------------------------------------------------------- /.kitchen.vmware.yml: -------------------------------------------------------------------------------- 1 | # Usage: KITCHEN_YAML=.kitchen.vmware.yml bundle exec kitchen list 2 | --- 3 | driver: 4 | name: vagrant 5 | provider: vmware_fusion 6 | customize: 7 | memory: 2048 8 | 9 | provisioner: 10 | name: chef_solo 11 | require_chef_omnibus: 11.16 12 | 13 | platforms: 14 | - name: macosx-10.10 15 | driver: 16 | box: macosx-10.10 17 | driver_config: 18 | network: 19 | - ["forwarded_port", {guest: 4444, host: 4447}] 20 | 21 | suites: 22 | - name: selenium_test 23 | run_list: 24 | - selenium_test::package 25 | - selenium_test::hub 26 | - selenium_test::node 27 | -------------------------------------------------------------------------------- /.kitchen.appveyor.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: proxy 4 | host: localhost 5 | reset_command: "exit 0" 6 | port: <%= ENV["machine_port"] %> 7 | username: <%= ENV["machine_user"] %> 8 | password: <%= ENV["machine_pass"] %> 9 | 10 | provisioner: 11 | name: chef_zero 12 | require_chef_omnibus: 12 13 | 14 | platforms: 15 | - name: windows-2012R2 16 | driver_config: 17 | box: WindowsServ2012R2 18 | 19 | suites: 20 | - name: default 21 | run_list: 22 | - selenium_test::hub 23 | - selenium_test::node 24 | attributes: 25 | selenium_test: 26 | username: <%= ENV["machine_user"] %> 27 | password: <%= ENV["machine_pass"] %> 28 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/selenium_test/recipes/node.rb: -------------------------------------------------------------------------------- 1 | include_recipe 'java_se' 2 | 3 | include_recipe 'xvfb' unless platform?('windows', 'mac_os_x') 4 | 5 | capabilities = [] 6 | 7 | include_recipe 'mozilla_firefox' 8 | include_recipe 'geckodriver' 9 | 10 | capabilities << { 11 | browserName: 'firefox', 12 | maxInstances: 5, 13 | version: firefox_version, 14 | seleniumProtocol: 'WebDriver', 15 | } 16 | 17 | node.override['selenium']['node']['capabilities'] = capabilities 18 | node.override['selenium']['node']['username'] = node['selenium_test']['username'] 19 | node.override['selenium']['node']['password'] = node['selenium_test']['password'] 20 | 21 | include_recipe 'selenium::node' 22 | -------------------------------------------------------------------------------- /test/integration/default/rspec/firefox_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rspec_helper' 2 | 3 | describe 'Firefox Grid' do 4 | before(:all) do 5 | @selenium = Selenium::WebDriver.for(:remote, desired_capabilities: :firefox) 6 | end 7 | 8 | after(:all) do 9 | @selenium.quit 10 | end 11 | 12 | res = if MACOSX 13 | '1024 x 768' 14 | elsif WINDOWS 15 | '1440 x 900' 16 | else 17 | '1280 x 1024' 18 | end 19 | 20 | it "Should return display resolution of #{res}" do 21 | @selenium.get 'http://www.whatismyscreenresolution.com/' 22 | element = @selenium.find_element(:id, 'resolutionNumber') 23 | expect(element.text).to eq(res) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /metadata.rb: -------------------------------------------------------------------------------- 1 | name 'selenium' 2 | maintainer 'Dennis Hoer' 3 | maintainer_email 'dennis.hoer@gmail.com' 4 | license 'MIT' 5 | description 'Installs/Configures Selenium' 6 | long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) 7 | source_url 'https://github.com/dhoer/chef-selenium' 8 | issues_url 'https://github.com/dhoer/chef-selenium/issues' 9 | version '5.0.1' 10 | 11 | chef_version '>= 12.14' 12 | 13 | supports 'centos' 14 | supports 'debian' 15 | supports 'fedora' 16 | supports 'mac_os_x' 17 | supports 'redhat' 18 | supports 'ubuntu' 19 | supports 'windows' 20 | 21 | depends 'macosx_autologin', '>= 4.0' 22 | depends 'nssm', '>= 4.0' 23 | depends 'windows' 24 | depends 'windows_autologin', '>= 3.0' 25 | -------------------------------------------------------------------------------- /attributes/default.rb: -------------------------------------------------------------------------------- 1 | default['selenium']['url'] = 2 | 'http://selenium-release.storage.googleapis.com/3.5/selenium-server-standalone-3.5.2.jar' 3 | 4 | default['selenium']['windows']['home'] = "#{ENV['SYSTEMDRIVE']}/selenium" 5 | default['selenium']['windows']['java'] = "#{ENV['SYSTEMDRIVE']}\\java\\bin\\java.exe" 6 | 7 | # used by both macosx and linux platforms 8 | default['selenium']['unix']['home'] = '/opt/selenium' 9 | default['selenium']['unix']['java'] = '/usr/bin/java' 10 | 11 | if platform?('windows') 12 | default['selenium']['home'] = node['selenium']['windows']['home'] 13 | default['selenium']['java'] = node['selenium']['windows']['java'] 14 | else 15 | default['selenium']['home'] = node['selenium']['unix']['home'] 16 | default['selenium']['java'] = node['selenium']['unix']['java'] 17 | end 18 | -------------------------------------------------------------------------------- /templates/default/org.seleniumhq.plist.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | <%=@name%> 7 | RunAtLoad 8 | 9 | KeepAlive 10 | 11 | ProgramArguments 12 | 13 | <%=@exec%> 14 | <% @args.each do |arg| %> 15 | <%=arg.gsub('"', '')%> 16 | <% end %> 17 | 18 | ServiceDescription 19 | <%=@name%> service 20 | StandardErrorPath 21 | /var/log/selenium/<%=@name%>.log 22 | StandardOutPath 23 | /var/log/selenium/<%=@name%>.log 24 | 25 | 26 | -------------------------------------------------------------------------------- /.kitchen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: vagrant 4 | 5 | provisioner: 6 | name: chef_solo 7 | require_chef_omnibus: '12' 8 | 9 | platforms: 10 | - name: centos-7.1 11 | driver: 12 | network: 13 | - ["forwarded_port", {guest: 4444, host: 4444}] 14 | 15 | - name: ubuntu-14.04 16 | driver: 17 | network: 18 | - ["forwarded_port", {guest: 4444, host: 4445}] 19 | 20 | - name: windows-2012r2 21 | driver: 22 | box: dhoer/windows-2012r2 23 | network: 24 | - ["forwarded_port", {guest: 4444, host: 4446}] 25 | attributes: 26 | 27 | suites: 28 | - name: default 29 | run_list: 30 | - selenium_test::package_update 31 | - selenium_test::package 32 | - selenium_test::hub 33 | - selenium_test::node 34 | - selenium_test::screenresolution 35 | attributes: 36 | mozilla_firefox: 37 | 32bit_only: true 38 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Getting Involved 2 | 3 | New contributors are always welcome, when it doubt please ask questions. We strive to be an open and welcoming community. Please be nice to one another. 4 | 5 | ### Coding 6 | 7 | * Pick a task: 8 | * Offer feedback on open [pull requests](https://github.com/dhoer/chef-selenium/pulls). 9 | * Review open [issues](https://github.com/dhoer/chef-selenium/issues) for things to help on. 10 | * [Create an issue](https://github.com/dhoer/chef-selenium/issues/new) to start a discussion on additions or features. 11 | * Fork the project, add your changes and tests to cover them in a topic branch. 12 | * Commit your changes and rebase against `dhoer/chef-selenium` to ensure everything is up to date. 13 | * [Submit a pull request](https://github.com/dhoer/chef-selenium/compare/). 14 | 15 | ### Non-Coding 16 | 17 | * Offer feedback on open [issues](https://github.com/dhoer/chef-selenium/issues). 18 | * Organize or volunteer at events. 19 | -------------------------------------------------------------------------------- /attributes/hub.rb: -------------------------------------------------------------------------------- 1 | default['selenium']['hub']['servicename'] = 'selenium_hub' # used by hub recipe only 2 | default['selenium']['hub']['host'] = nil 3 | default['selenium']['hub']['port'] = 4444 4 | default['selenium']['hub']['jvm_args'] = nil 5 | default['selenium']['hub']['newSessionWaitTimeout'] = -1 6 | default['selenium']['hub']['prioritizer'] = nil 7 | default['selenium']['hub']['servlets'] = [] 8 | default['selenium']['hub']['withoutServlets'] = [] 9 | default['selenium']['hub']['capabilityMatcher'] = 'org.openqa.grid.internal.utils.DefaultCapabilityMatcher' 10 | default['selenium']['hub']['throwOnCapabilityNotPresent'] = true 11 | default['selenium']['hub']['cleanUpCycle'] = 5000 12 | default['selenium']['hub']['debug'] = true 13 | default['selenium']['hub']['timeout'] = 1800 14 | default['selenium']['hub']['browserTimeout'] = 0 15 | default['selenium']['hub']['maxSession'] = 5 16 | default['selenium']['hub']['jettyMaxThreads'] = 0 17 | default['selenium']['hub']['log'] = nil 18 | -------------------------------------------------------------------------------- /templates/default/hub_config.erb: -------------------------------------------------------------------------------- 1 | { 2 | <% if @resource.host %> 3 | "host": "<%= @resource.host %>", 4 | <% end %> 5 | "newSessionWaitTimeout": <%= @resource.newSessionWaitTimeout %>, 6 | <% if @resource.prioritizer %> 7 | "prioritizer": <%= @resource.prioritizer %>, 8 | <% end %> 9 | "servlets": <%= @resource.servlets %>, 10 | "withoutServlets": <%= @resource.withoutServlets %>, 11 | "capabilityMatcher": "<%= @resource.capabilityMatcher %>", 12 | "throwOnCapabilityNotPresent": <%= @resource.throwOnCapabilityNotPresent %>, 13 | "cleanUpCycle": <%= @resource.cleanUpCycle %>, 14 | "timeout": <%= @resource.timeout %>, 15 | "debug": <%= @resource.debug %>, 16 | "timeout": <%= @resource.timeout %>, 17 | "browserTimeout": <%= @resource.browserTimeout %>, 18 | "maxSession": <%= @resource.maxSession %>, 19 | "jettyMaxThreads": <%= @resource.jettyMaxThreads %>, 20 | <% if @resource.log %> 21 | "log": "<%= @resource.log %>", 22 | <% end %> 23 | "port": <%= @resource.port %> 24 | } 25 | -------------------------------------------------------------------------------- /recipes/hub.rb: -------------------------------------------------------------------------------- 1 | selenium_hub node['selenium']['hub']['servicename'] do 2 | host node['selenium']['hub']['host'] 3 | port node['selenium']['hub']['port'] 4 | jvm_args node['selenium']['hub']['jvm_args'] 5 | newSessionWaitTimeout node['selenium']['hub']['newSessionWaitTimeout'] 6 | prioritizer node['selenium']['hub']['prioritizer'] 7 | servlets node['selenium']['hub']['servlets'] 8 | withoutServlets node['selenium']['hub']['withoutServlets'] 9 | capabilityMatcher node['selenium']['hub']['capabilityMatcher'] 10 | throwOnCapabilityNotPresent node['selenium']['hub']['throwOnCapabilityNotPresent'] 11 | cleanUpCycle node['selenium']['hub']['cleanUpCycle'] 12 | debug node['selenium']['hub']['debug'] 13 | timeout node['selenium']['hub']['timeout'] 14 | browserTimeout node['selenium']['hub']['browserTimeout'] 15 | maxSession node['selenium']['hub']['maxSession'] 16 | jettyMaxThreads node['selenium']['hub']['jettyMaxThreads'] 17 | log node['selenium']['hub']['log'] 18 | action :install 19 | end 20 | -------------------------------------------------------------------------------- /templates/default/node_config.erb: -------------------------------------------------------------------------------- 1 | { 2 | "capabilities": <%= JSON.pretty_generate(@resource.capabilities) %>, 3 | "proxy": "<%= @resource.proxy %>", 4 | "maxSession": <%= @resource.maxSession %>, 5 | "port": <%= @resource.port %>, 6 | <% if @resource.host %> 7 | "host": "<%= @resource.host %>", 8 | <% end %> 9 | "register": <%= @resource.register %>, 10 | "registerCycle": <%= @resource.registerCycle %>, 11 | "servlets": <%= @resource.servlets %>, 12 | "withoutServlets": <%= @resource.withoutServlets %>, 13 | "timeout": <%= @resource.timeout %>, 14 | "debug": <%= @resource.debug %>, 15 | "timeout": <%= @resource.timeout %>, 16 | "browserTimeout": <%= @resource.browserTimeout %>, 17 | "jettyMaxThreads": <%= @resource.jettyMaxThreads %>, 18 | "nodeStatusCheckTimeout": <%= @resource.nodeStatusCheckTimeout %>, 19 | "nodePolling": <%= @resource.nodePolling %>, 20 | "unregisterIfStillDownAfter": <%= @resource.unregisterIfStillDownAfter %>, 21 | "downPollingLimit": <%= @resource.downPollingLimit %>, 22 | <% if @resource.log %> 23 | "log": "<%= @resource.log %>", 24 | <% end %> 25 | "hub": "<%= @resource.hub %>" 26 | } 27 | -------------------------------------------------------------------------------- /resources/hub.rb: -------------------------------------------------------------------------------- 1 | actions :install 2 | default_action :install 3 | 4 | attribute :servicename, kind_of: String, name_attribute: true 5 | attribute :host, kind_of: [String, NilClass] 6 | attribute :port, kind_of: Integer, default: 4444 7 | attribute :jvm_args, kind_of: [String, NilClass] 8 | attribute :newSessionWaitTimeout, kind_of: Integer, default: -1 9 | attribute :prioritizer, kind_of: [Class, String, Symbol, NilClass] 10 | attribute :servlets, kind_of: Array, default: [] 11 | attribute :withoutServlets, kind_of: Array, default: [] 12 | attribute :capabilityMatcher, kind_of: String, default: 'org.openqa.grid.internal.utils.DefaultCapabilityMatcher' 13 | attribute :throwOnCapabilityNotPresent, kind_of: [TrueClass, FalseClass], default: true 14 | attribute :cleanUpCycle, kind_of: Integer, default: 5000 15 | attribute :debug, kind_of: [TrueClass, FalseClass], default: false 16 | attribute :timeout, kind_of: Integer, default: 1800 17 | attribute :browserTimeout, kind_of: Integer, default: 0 18 | attribute :maxSession, kind_of: Integer, default: 5 19 | attribute :jettyMaxThreads, kind_of: Integer, default: -1 20 | attribute :log, kind_of: [String, NilClass] 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 [CONTRIBUTORS](https://github.com/dhoer/chef-selenium/graphs/contributors) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /templates/default/upstart.erb: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### BEGIN INIT INFO 3 | # Provides: <%=@name%> 4 | # Required-Start: $local_fs $remote_fs $network $syslog 5 | # Required-Stop: $local_fs $remote_fs $network $syslog 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # X-Interactive: true 9 | # Short-Description: Selenium <%=@name%> service 10 | ### END INIT INFO 11 | 12 | set -e 13 | 14 | user=<%=@user%> 15 | exec=<%=@exec%> 16 | args="<%=@args%>" 17 | pidfile="/var/run/<%=@name%>.pid" 18 | logfile="/var/log/<%=@name%>.log" 19 | prog="<%=@name%>" 20 | xdisplay="<%=@xdisplay%>" 21 | 22 | if [ "$1" = "status" ]; then 23 | if [ -f "$pidfile" ]; then 24 | echo 25 | echo "$prog is running ... PID=$(cat $pidfile)." 26 | else 27 | echo "$prog is not running." 28 | fi 29 | exit 1 30 | fi 31 | 32 | if [ -f "$pidfile" ]; then 33 | PID=$(cat $pidfile) 34 | if [ "$1" = "restart" -o "$1" = "stop" ]; then 35 | kill $PID 36 | rm $pidfile 37 | fi 38 | fi 39 | 40 | if [ "$1" = "start" -o "$1" = "restart" ]; then 41 | if [ ! -f "$pidfile" ]; then 42 | touch $pidfile 43 | chown $user $pidfile 44 | 45 | touch $logfile 46 | chown $user $logfile 47 | 48 | /bin/su - $user -c "DISPLAY=\"$xdisplay\" nohup $exec $args >> $logfile 2>&1 & echo \$! > $pidfile" 49 | fi 50 | fi 51 | -------------------------------------------------------------------------------- /attributes/node.rb: -------------------------------------------------------------------------------- 1 | default['selenium']['node']['servicename'] = 'selenium_node' # used by node recipe only 2 | default['selenium']['node']['host'] = nil 3 | default['selenium']['node']['port'] = 5555 4 | default['selenium']['node']['jvm_args'] = nil 5 | default['selenium']['node']['proxy'] = 'org.openqa.grid.selenium.proxy.DefaultRemoteProxy' 6 | default['selenium']['node']['maxSession'] = 5 7 | default['selenium']['node']['register'] = true 8 | default['selenium']['node']['registerCycle'] = 5000 9 | default['selenium']['node']['hub'] = 'http://localhost:4444' 10 | default['selenium']['node']['capabilities'] = [] 11 | default['selenium']['node']['servlets'] = [] 12 | default['selenium']['node']['withoutServlets'] = [] 13 | default['selenium']['node']['nodeStatusCheckTimeout'] = 5000 14 | default['selenium']['node']['nodePolling'] = 5000 15 | default['selenium']['node']['unregisterIfStillDownAfter'] = 60_000 16 | default['selenium']['node']['downPollingLimit'] = 2 17 | default['selenium']['node']['debug'] = true 18 | default['selenium']['node']['timeout'] = 1800 19 | default['selenium']['node']['browserTimeout'] = 0 20 | default['selenium']['node']['jettyMaxThreads'] = 0 21 | default['selenium']['node']['log'] = nil 22 | default['selenium']['node']['xdisplay'] = ':0' 23 | default['selenium']['node']['username'] = nil 24 | default['selenium']['node']['password'] = nil 25 | -------------------------------------------------------------------------------- /recipes/node.rb: -------------------------------------------------------------------------------- 1 | selenium_node node['selenium']['node']['servicename'] do 2 | host node['selenium']['node']['host'] 3 | port node['selenium']['node']['port'] 4 | jvm_args node['selenium']['node']['jvm_args'] 5 | proxy node['selenium']['node']['proxy'] 6 | maxSession node['selenium']['node']['maxSession'] 7 | register node['selenium']['node']['register'] 8 | registerCycle node['selenium']['node']['registerCycle'] 9 | hub node['selenium']['node']['hub'] 10 | capabilities node['selenium']['node']['capabilities'] 11 | servlets node['selenium']['node']['servlets'] 12 | withoutServlets node['selenium']['node']['withoutServlets'] 13 | nodeStatusCheckTimeout node['selenium']['node']['nodeStatusCheckTimeout'] 14 | nodePolling node['selenium']['node']['nodePolling'] 15 | unregisterIfStillDownAfter node['selenium']['node']['unregisterIfStillDownAfter'] 16 | downPollingLimit node['selenium']['node']['downPollingLimit'] 17 | debug node['selenium']['node']['debug'] 18 | timeout node['selenium']['node']['timeout'] 19 | browserTimeout node['selenium']['node']['browserTimeout'] 20 | jettyMaxThreads node['selenium']['node']['jettyMaxThreads'] 21 | log node['selenium']['node']['log'] 22 | username node['selenium']['node']['username'] 23 | password node['selenium']['node']['password'] 24 | xdisplay node['selenium']['node']['xdisplay'] unless platform?('windows') 25 | action :install 26 | end 27 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "build-{build}" 2 | 3 | os: Windows Server 2012 R2 4 | platform: 5 | - x64 6 | 7 | environment: 8 | machine_user: test_user 9 | machine_pass: Pass@word1 10 | machine_port: 5985 11 | KITCHEN_YAML: .kitchen.appveyor.yml 12 | SSL_CERT_FILE: c:\projects\kitchen-machine\certs.pem 13 | 14 | matrix: 15 | - ruby_version: "24" 16 | 17 | clone_folder: c:\projects\kitchen-machine 18 | clone_depth: 1 19 | branches: 20 | except: 21 | - macosx 22 | 23 | install: 24 | - ps: net user /add $env:machine_user $env:machine_pass 25 | - ps: net localgroup administrators $env:machine_user /add 26 | - ps: $env:PATH="C:\Ruby$env:ruby_version\bin;$env:PATH" 27 | - ps: gem install bundler --quiet --no-ri --no-rdoc 28 | - ps: |- 29 | [Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12 30 | - ps: Invoke-WebRequest -Uri http://curl.haxx.se/ca/cacert.pem -OutFile c:\projects\kitchen-machine\certs.pem 31 | 32 | build_script: 33 | # - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/set-screenresolution.ps1')) 34 | # - ps: Set-ScreenResolution 1440 900 35 | - bundle install || bundle install || bundle install 36 | - bundle exec rake 37 | - bundle exec kitchen conv 38 | # - shutdown /a 39 | # - ps: Restart-Computer -Force 40 | # - ps: Start-Sleep -s 10 41 | # 42 | #test_script: 43 | # - bundle exec kitchen veri 44 | -------------------------------------------------------------------------------- /test/integration/default/serverspec/default_spec.rb: -------------------------------------------------------------------------------- 1 | require 'serverspec_helper' 2 | 3 | describe 'selenium::default' do 4 | version = '3.5.2' 5 | 6 | if os[:family] == 'windows' 7 | describe file('C:/selenium/config') do 8 | it { should be_directory } 9 | end 10 | 11 | describe file('C:/selenium/server') do 12 | it { should be_directory } 13 | end 14 | 15 | describe file('C:/selenium/bin') do 16 | it { should be_directory } 17 | end 18 | 19 | describe file('C:/selenium/log') do 20 | it { should be_directory } 21 | end 22 | 23 | describe file("C:/selenium/server/selenium-server-standalone-#{version}.jar") do 24 | it { should be_file } 25 | end 26 | 27 | describe file('C:/selenium/server/selenium-server-standalone.jar') do 28 | it { should be_file } 29 | end 30 | else 31 | describe file('/opt/selenium/config') do 32 | it { should be_directory } 33 | it { should be_owned_by 'root' } 34 | end 35 | 36 | describe file('/opt/selenium/server') do 37 | it { should be_directory } 38 | it { should be_owned_by 'root' } 39 | end 40 | 41 | describe file("/opt/selenium/server/selenium-server-standalone-#{version}.jar") do 42 | it { should be_file } 43 | it { should be_executable.by_user('root') } 44 | end 45 | 46 | describe file('/opt/selenium/server/selenium-server-standalone.jar') do 47 | it { should be_symlink } 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | sudo: required 4 | 5 | # install the pre-release chef-dk. Use chef-stable-precise to install the stable release 6 | addons: 7 | apt: 8 | sources: 9 | - chef-current-precise 10 | packages: 11 | - chefdk 12 | - gcc 13 | - libffi-dev 14 | 15 | services: docker 16 | 17 | env: 18 | matrix: 19 | - INSTANCE=default-centos-6 20 | - INSTANCE=default-centos-7 21 | - INSTANCE=default-ubuntu-1204 22 | - INSTANCE=default-ubuntu-1404 23 | - INSTANCE=default-ubuntu-1604 24 | - INSTANCE=default-debian-7 25 | - INSTANCE=default-debian-8 26 | - INSTANCE=default-fedora-latest 27 | 28 | # Don't `bundle install` 29 | install: echo "skip bundle install" 30 | 31 | # Ensure we make ChefDK's Ruby the default 32 | before_script: 33 | # https://github.com/zuazo/kitchen-in-travis-native/issues/1#issuecomment-142230889 34 | - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER ) 35 | - eval "$(/opt/chefdk/bin/chef shell-init bash)" 36 | - /opt/chefdk/embedded/bin/chef gem install kitchen-dokken 37 | 38 | script: 39 | - /opt/chefdk/embedded/bin/chef --version 40 | - /opt/chefdk/embedded/bin/cookstyle --version 41 | - /opt/chefdk/embedded/bin/cookstyle 42 | - /opt/chefdk/embedded/bin/foodcritic --version 43 | - /opt/chefdk/embedded/bin/foodcritic . --exclude spec -f any 44 | - /opt/chefdk/embedded/bin/rspec 45 | - KITCHEN_LOCAL_YAML=.kitchen.docker.yml /opt/chefdk/embedded/bin/kitchen veri ${INSTANCE} 46 | -------------------------------------------------------------------------------- /providers/hub.rb: -------------------------------------------------------------------------------- 1 | use_inline_resources 2 | 3 | def whyrun_supported? 4 | true 5 | end 6 | 7 | def config 8 | config_file = "#{selenium_home}/config/#{new_resource.servicename}.json" 9 | template config_file do 10 | source 'hub_config.erb' 11 | cookbook 'selenium' 12 | variables( 13 | resource: new_resource 14 | ) 15 | notifies :restart, "service[#{new_resource.servicename}]", :delayed unless platform_family?('windows', 'mac_os_x') 16 | if platform_family?('mac_os_x') 17 | notifies :run, "execute[reload #{selenium_mac_domain(new_resource.servicename)}]", 18 | :delayed 19 | end 20 | end 21 | config_file 22 | end 23 | 24 | def args 25 | args = [] 26 | args << new_resource.jvm_args unless new_resource.jvm_args.nil? 27 | args << %W(-jar "#{selenium_jar_link}" -role hub -hubConfig "#{config}") 28 | args.flatten! 29 | end 30 | 31 | action :install do 32 | unless run_context.loaded_recipe? 'selenium::default' 33 | recipe_eval do 34 | run_context.include_recipe 'selenium::default' 35 | end 36 | end 37 | 38 | case node['platform'] 39 | when 'windows' 40 | selenium_windows_service(new_resource.servicename, selenium_java_exec, args) 41 | selenium_windows_firewall(new_resource.servicename, new_resource.port) 42 | when 'mac_os_x' 43 | plist = "/Library/LaunchDaemons/#{selenium_mac_domain(new_resource.servicename)}.plist" 44 | selenium_mac_service(new_resource, selenium_java_exec, args, plist, nil) 45 | else 46 | selenium_linux_service(new_resource.servicename, selenium_java_exec, args, new_resource.port, nil) 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /chefignore: -------------------------------------------------------------------------------- 1 | # Put files/directories that should be ignored in this file when uploading 2 | # or sharing to the community site. 3 | # Lines that start with '# ' are comments. 4 | 5 | # OS generated files # 6 | ###################### 7 | .DS_Store 8 | Icon? 9 | nohup.out 10 | ehthumbs.db 11 | Thumbs.db 12 | 13 | # SASS # 14 | ######## 15 | .sass-cache 16 | 17 | # EDITORS # 18 | ########### 19 | \#* 20 | .#* 21 | *~ 22 | *.sw[a-z] 23 | *.bak 24 | REVISION 25 | TAGS* 26 | tmtags 27 | *_flymake.* 28 | *_flymake 29 | *.tmproj 30 | .project 31 | .settings 32 | mkmf.log 33 | .idea 34 | 35 | ## COMPILED ## 36 | ############## 37 | a.out 38 | *.o 39 | *.pyc 40 | *.so 41 | *.com 42 | *.class 43 | *.dll 44 | *.exe 45 | */rdoc/ 46 | 47 | # Testing # 48 | ########### 49 | .watchr 50 | .rspec 51 | spec/* 52 | spec/fixtures/* 53 | test/* 54 | features/* 55 | Guardfile 56 | Procfile 57 | 58 | # Test-Kitchen # 59 | ################ 60 | .kitchen.yml 61 | .kitchen/* 62 | 63 | # SCM # 64 | ####### 65 | .git 66 | */.git 67 | .gitignore 68 | .gitmodules 69 | .gitconfig 70 | .gitattributes 71 | .svn 72 | */.bzr/* 73 | */.hg/* 74 | */.svn/* 75 | 76 | # Berkshelf # 77 | ############# 78 | Berksfile 79 | Berksfile.lock 80 | cookbooks/* 81 | tmp 82 | 83 | # Cookbooks # 84 | ############# 85 | CONTRIBUTING 86 | CHANGELOG* 87 | 88 | # Strainer # 89 | ############ 90 | Colanderfile 91 | Strainerfile 92 | .colander 93 | .strainer 94 | 95 | # Vagrant # 96 | ########### 97 | .vagrant 98 | Vagrantfile 99 | 100 | # Travis # 101 | ########## 102 | .travis.yml 103 | 104 | # Rubocop # 105 | ########### 106 | .rubocop.yml 107 | -------------------------------------------------------------------------------- /templates/default/sysvinit.erb: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # <%=@name%> 4 | # 5 | # chkconfig: 345 90 25 6 | # description: Selenium <%=@name%> service 7 | 8 | # Source function library. 9 | . /etc/init.d/functions 10 | 11 | user=<%=@user%> 12 | exec="<%=@exec%>" 13 | args="<%=@args%>" 14 | lockfile="/var/lock/subsys/<%=@name%>" 15 | pidfile="/var/run/<%=@name%>.pid" 16 | logfile="/var/log/<%=@name%>.log" 17 | prog="<%=@name%>" 18 | xdisplay="<%=@xdisplay%>" 19 | port="<%=@port%>" 20 | 21 | RETVAL=0 22 | 23 | start() { 24 | echo -n $"Starting $prog: " 25 | 26 | touch $pidfile 27 | chown $user $pidfile 28 | 29 | touch $logfile 30 | chown $user $logfile 31 | 32 | /bin/su - $user -c "DISPLAY=\"$xdisplay\" $exec $args >> $logfile 2>&1 & echo \$! > $pidfile" 33 | 34 | sleep 2 35 | 36 | pgrep -fl $prog 37 | RETVAL=$? 38 | [ $RETVAL -eq 0 ] && echo_success || echo_failure 39 | 40 | listen=0 41 | while [ $listen -eq 0 ]; 42 | do 43 | listen=`netstat -ltn | awk '{print $4}' | grep "${port}$" | wc -l` 44 | sleep 1 45 | done 46 | 47 | echo 48 | [ $RETVAL -eq 0 ] && touch $lockfile 49 | return $RETVAL 50 | } 51 | 52 | stop() { 53 | echo -n $"Stopping $prog: " 54 | killproc -p $pidfile $prog 55 | RETVAL=$? 56 | echo 57 | [ $RETVAL -eq 0 ] && rm -f $lockfile $pidfile 58 | return $RETVAL 59 | } 60 | 61 | restart() { 62 | stop 63 | start 64 | } 65 | 66 | 67 | case "$1" in 68 | start) 69 | start 70 | ;; 71 | stop) 72 | stop 73 | ;; 74 | status) 75 | status -p ${pidfile} ${prog} 76 | RETVAL=$? 77 | ;; 78 | restart) 79 | restart 80 | ;; 81 | *) 82 | echo $"Usage: $0 {start|stop|restart}" 83 | exit 1 84 | esac 85 | -------------------------------------------------------------------------------- /resources/node.rb: -------------------------------------------------------------------------------- 1 | actions :install 2 | default_action :install 3 | 4 | attribute :servicename, kind_of: String, name_attribute: true 5 | attribute :host, kind_of: [String, NilClass] 6 | attribute :port, kind_of: Integer, default: 5555 7 | attribute :jvm_args, kind_of: [String, NilClass] 8 | attribute :proxy, kind_of: String, default: 'org.openqa.grid.selenium.proxy.DefaultRemoteProxy' 9 | attribute :maxSession, kind_of: Integer, default: 5 10 | attribute :register, kind_of: [TrueClass, FalseClass], default: true 11 | attribute :registerCycle, kind_of: Integer, default: 5000 12 | attribute :nodeStatusCheckTimeout, kind_of: Integer, default: 5000 13 | attribute :nodePolling, kind_of: Integer, default: 5000 14 | attribute :unregisterIfStillDownAfter, kind_of: Integer, default: 60_000 15 | attribute :downPollingLimit, kind_of: Integer, default: 2 16 | attribute :debug, kind_of: [TrueClass, FalseClass], default: false 17 | attribute :servlets, kind_of: Array, default: [] 18 | attribute :withoutServlets, kind_of: Array, default: [] 19 | attribute :timeout, kind_of: Integer, default: 1800 20 | attribute :browserTimeout, kind_of: Integer, default: 0 21 | attribute :jettyMaxThreads, kind_of: Integer, default: -1 22 | attribute :log, kind_of: [String, NilClass] 23 | attribute :hub, kind_of: String, default: 'http://localhost:4444' 24 | attribute :capabilities, kind_of: [Array, Hash], default: [] 25 | 26 | # linux only - DISPLAY must match running instance of Xvfb, x11vnc or equivalent 27 | attribute :xdisplay, kind_of: String, default: ':0' 28 | 29 | # mac/windows only - set username/password to run service in gui or leave nil to run service in background 30 | attribute :username, kind_of: [String, NilClass], default: nil 31 | attribute :password, kind_of: [String, NilClass], default: nil 32 | -------------------------------------------------------------------------------- /.kitchen.docker.yml: -------------------------------------------------------------------------------- 1 | # Usage: KITCHEN_YAML=.kitchen.docker.yml bundle exec kitchen list 2 | --- 3 | driver: 4 | name: dokken 5 | chef_version: latest 6 | privileged: true # because Docker and SystemD/Upstart 7 | 8 | transport: 9 | name: dokken 10 | 11 | provisioner: 12 | name: dokken 13 | 14 | verifier: 15 | root_path: '/opt/verifier' 16 | sudo: false 17 | 18 | platforms: 19 | - name: centos-6 20 | driver: 21 | image: centos:6 22 | intermediate_instructions: 23 | - RUN yum -y install tar which initscripts 24 | 25 | - name: centos-7 26 | driver: 27 | image: centos:7 28 | intermediate_instructions: 29 | - RUN yum clean all 30 | - RUN yum -y install net-tools lsof 31 | pid_one_command: /usr/lib/systemd/systemd 32 | 33 | - name: debian-7 34 | driver: 35 | image: debian:7 36 | intermediate_instructions: 37 | - RUN /usr/bin/apt-get update && /usr/bin/apt-get upgrade -y 38 | - RUN /usr/bin/apt-get install apt-transport-https net-tools -y 39 | 40 | - name: debian-8 41 | driver: 42 | image: debian:8 43 | pid_one_command: /bin/systemd 44 | intermediate_instructions: 45 | - RUN /usr/bin/apt-get update 46 | - RUN /usr/bin/apt-get install apt-transport-https net-tools -y 47 | 48 | - name: fedora-latest 49 | driver: 50 | image: fedora:latest 51 | intermediate_instructions: 52 | - RUN yum clean all 53 | pid_one_command: /usr/lib/systemd/systemd 54 | intermediate_instructions: 55 | - RUN yum -y install tar yum 56 | 57 | - name: ubuntu-12.04 58 | driver: 59 | image: ubuntu-upstart:12.04 60 | pid_one_command: /sbin/init 61 | intermediate_instructions: 62 | - RUN /usr/bin/apt-get update 63 | - RUN /usr/bin/apt-get install apt-transport-https net-tools -y 64 | 65 | - name: ubuntu-14.04 66 | driver: 67 | image: ubuntu-upstart:14.04 68 | pid_one_command: /sbin/init 69 | intermediate_instructions: 70 | - RUN /usr/bin/apt-get update 71 | - RUN /usr/bin/apt-get install apt-transport-https net-tools -y 72 | 73 | - name: ubuntu-16.04 74 | driver: 75 | image: ubuntu:16.04 76 | pid_one_command: /bin/systemd 77 | intermediate_instructions: 78 | - RUN /usr/bin/apt-get update 79 | - RUN /usr/bin/apt-get install apt-transport-https net-tools -y 80 | 81 | suites: 82 | - name: default 83 | run_list: 84 | - selenium_test::package 85 | - selenium_test::hub 86 | - selenium_test::node 87 | attributes: 88 | selenium_test: 89 | username: travis 90 | password: travis 91 | -------------------------------------------------------------------------------- /providers/node.rb: -------------------------------------------------------------------------------- 1 | use_inline_resources 2 | 3 | def whyrun_supported? 4 | true 5 | end 6 | 7 | def config 8 | config_file = "#{selenium_home}/config/#{new_resource.servicename}.json" 9 | template config_file do 10 | source 'node_config.erb' 11 | cookbook 'selenium' 12 | variables(resource: new_resource) 13 | if platform_family?('windows') 14 | notifies :request_reboot, "reboot[Reboot to start #{new_resource.servicename}]", :delayed 15 | end 16 | notifies :restart, "service[#{new_resource.servicename}]", :delayed unless platform_family?('windows', 'mac_os_x') 17 | if platform_family?('mac_os_x') 18 | notifies :run, "execute[reload #{selenium_mac_domain(new_resource.servicename)}]", 19 | :immediately 20 | end 21 | end 22 | config_file 23 | end 24 | 25 | def args 26 | args = [] 27 | args << new_resource.jvm_args unless new_resource.jvm_args.nil? 28 | args << %W(-jar "#{selenium_jar_link}" -role node -nodeConfig "#{config}") 29 | args.flatten! 30 | end 31 | 32 | action :install do 33 | unless run_context.loaded_recipe? 'selenium::default' 34 | recipe_eval do 35 | run_context.include_recipe 'selenium::default' 36 | end 37 | end 38 | 39 | case node['platform'] 40 | when 'windows' 41 | selenium_windows_gui_service(new_resource.servicename, selenium_java_exec, args, new_resource.username) 42 | selenium_autologon(new_resource.username, new_resource.password) 43 | 44 | selenium_windows_firewall(new_resource.servicename, new_resource.port) 45 | 46 | reboot "Reboot to start #{new_resource.servicename}" do 47 | action :nothing 48 | reason 'Need to reboot when the run completes successfully.' 49 | delay_mins 1 50 | end 51 | when 'mac_os_x' 52 | plist = if new_resource.username && new_resource.password 53 | "/Library/LaunchAgents/#{selenium_mac_domain(new_resource.servicename)}.plist" 54 | else 55 | "/Library/LaunchDaemons/#{selenium_mac_domain(new_resource.servicename)}.plist" 56 | end 57 | 58 | selenium_mac_service(new_resource, selenium_java_exec, args, plist, new_resource.username) 59 | selenium_autologon(new_resource.username, new_resource.password) 60 | 61 | execute "Reboot to start #{selenium_mac_domain(new_resource.servicename)}" do 62 | command 'sudo shutdown -r +1' 63 | action :nothing 64 | end 65 | else 66 | selenium_linux_service( 67 | new_resource.servicename, selenium_java_exec, args, new_resource.port, new_resource.xdisplay 68 | ) 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /spec/unit/default_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'selenium::default' do 4 | context 'windows' do 5 | let(:chef_run) do 6 | ChefSpec::SoloRunner.new(platform: 'windows', version: '2008R2') do |node| 7 | ENV['SYSTEMDRIVE'] = 'C:' 8 | node.override['selenium']['url'] = 9 | 'https://selenium-release.storage.googleapis.com/3.0/selenium-server-standalone-3.0.1.jar' 10 | end.converge(described_recipe) 11 | end 12 | 13 | it 'does not install zip package' do 14 | expect(chef_run).to_not install_package('unzip') 15 | end 16 | 17 | it 'creates bin directory' do 18 | expect(chef_run).to create_directory('C:/selenium/bin') 19 | end 20 | 21 | it 'creates config directory' do 22 | expect(chef_run).to create_directory('C:/selenium/config') 23 | end 24 | 25 | it 'creates log directory' do 26 | expect(chef_run).to create_directory('C:/selenium/log') 27 | end 28 | 29 | it 'creates selenium server directory' do 30 | expect(chef_run).to create_directory('C:/selenium/server') 31 | end 32 | 33 | it 'downloads jar' do 34 | expect(chef_run).to create_remote_file('C:/selenium/server/selenium-server-standalone-3.0.1.jar') 35 | end 36 | 37 | it 'creates link to jar' do 38 | expect(chef_run).to create_link('C:/selenium/server/selenium-server-standalone.jar').with( 39 | to: 'C:/selenium/server/selenium-server-standalone-3.0.1.jar' 40 | ) 41 | end 42 | end 43 | 44 | context 'linux' do 45 | let(:chef_run) do 46 | ChefSpec::SoloRunner.new(platform: 'centos', version: '7.3.1611') do |node| 47 | node.override['selenium']['url'] = 48 | 'https://selenium-release.storage.googleapis.com/3.0/selenium-server-standalone-3.0.1.jar' 49 | end.converge(described_recipe) 50 | end 51 | 52 | it 'installs package' do 53 | expect(chef_run).to install_package('unzip') 54 | end 55 | 56 | it 'does not create bin directory' do 57 | expect(chef_run).to_not create_directory('/opt/selenium/bin') 58 | end 59 | 60 | it 'creates config directory' do 61 | expect(chef_run).to create_directory('/opt/selenium/config') 62 | end 63 | 64 | it 'does not create log directory' do 65 | expect(chef_run).to_not create_directory('/opt/selenium/log') 66 | end 67 | 68 | it 'creates selenium server directory' do 69 | expect(chef_run).to create_directory('/opt/selenium/server') 70 | end 71 | 72 | it 'downloads jar' do 73 | expect(chef_run).to create_remote_file('/opt/selenium/server/selenium-server-standalone-3.0.1.jar').with( 74 | source: 'https://selenium-release.storage.googleapis.com/3.0/selenium-server-standalone-3.0.1.jar' 75 | ) 76 | end 77 | 78 | it 'creates link to jar' do 79 | expect(chef_run).to create_link('/opt/selenium/server/selenium-server-standalone.jar').with( 80 | to: '/opt/selenium/server/selenium-server-standalone-3.0.1.jar' 81 | ) 82 | end 83 | end 84 | 85 | context 'mac_os_x' do 86 | let(:chef_run) do 87 | ChefSpec::SoloRunner.new(platform: 'mac_os_x', version: '10.10') do |node| 88 | node.override['selenium']['url'] = 89 | 'https://selenium-release.storage.googleapis.com/3.0/selenium-server-standalone-3.0.1.jar' 90 | end.converge(described_recipe) 91 | end 92 | 93 | it 'does not install package' do 94 | expect(chef_run).to_not install_package('unzip') 95 | end 96 | 97 | it 'does not create bin directory' do 98 | expect(chef_run).to_not create_directory('/opt/selenium/bin') 99 | end 100 | 101 | it 'creates config directory' do 102 | expect(chef_run).to create_directory('/opt/selenium/config') 103 | end 104 | 105 | it 'does not create log directory' do 106 | expect(chef_run).to_not create_directory('/opt/selenium/log') 107 | end 108 | 109 | it 'creates selenium server directory' do 110 | expect(chef_run).to create_directory('/opt/selenium/server') 111 | end 112 | 113 | it 'downloads jar' do 114 | expect(chef_run).to create_remote_file('/opt/selenium/server/selenium-server-standalone-3.0.1.jar') 115 | end 116 | 117 | it 'creates link to jar' do 118 | expect(chef_run).to create_link('/opt/selenium/server/selenium-server-standalone.jar').with( 119 | to: '/opt/selenium/server/selenium-server-standalone-3.0.1.jar' 120 | ) 121 | end 122 | end 123 | end 124 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 5.0.1 2017-08-26 4 | 5 | - Selenium release 3.5.2 6 | - Update windows to use NSSM 4.0+ - which requires triple quotes to be removed 7 | - Add missing xdisplay to node recipe 8 | 9 | ## 5.0.0 2017-03-24 10 | 11 | - Rename display attribute to xdisplay to be Chef 13 compatible 12 | 13 | ## 4.1.0 2017-02-27 14 | 15 | - Selenium release 3.1.0 16 | 17 | ## 4.0.0 2017-02-17 18 | 19 | - Support Selenium 3.0 20 | - Log defaults to STDOUT when log attribute is nil 21 | - Drop support for Chef 11 22 | 23 | ## 3.7.4 2016-10-29 24 | 25 | - Fix #31 Adding support for selenium 3 in node config file 26 | 27 | ## 3.7.3 2016-08-22 28 | 29 | - Fix #29 Error executing action run on resource 'execute[reload org.seleniumhq.selenium_hub]' 30 | 31 | ## 3.7.2 2016-08-04 32 | 33 | - Fix #27 Cannot create directory due to insufficient permissions 34 | 35 | ## 3.7.1 2016-07-21 36 | 37 | - Selenium release 2.53.1 38 | 39 | ## 3.7.0 2016-06-25 40 | 41 | - Add support for systemd 42 | 43 | ## 3.6.1 2016-04-13 44 | 45 | - No more native HtmlUnitDriver, removed integration test 46 | - Include default recipe in provide only if required 47 | - Include windows::bootstrap_handler recipe in provide only if required 48 | 49 | ## 3.6.0 2016-03-22 50 | 51 | - Selenium release 2.53.0 52 | 53 | ## 3.5.0 2016-02-18 54 | 55 | - Selenium release 2.52.0 56 | 57 | ## 3.4.0 2016-02-10 58 | 59 | - Selenium release 2.51.0 60 | 61 | ## 3.3.1 2016-01-29 62 | 63 | - Selenium release 2.50.1 64 | 65 | ## 3.3.0 2016-01-28 66 | 67 | - Selenium release 2.50.0 68 | 69 | ## 3.2.1 2015-10-11 70 | 71 | - Selenium release 2.48.2 72 | 73 | ## 3.2.0 2015-10-07 74 | 75 | - Selenium release 2.48.0 76 | - Fix #23 WARN: Cannot create resource windows_service with more than one argument 77 | 78 | ## 3.1.1 2015-09-27 79 | 80 | - Fix #22 Firefox on Ubuntu fails to start 81 | 82 | ## 3.1.0 2015-09-26 83 | 84 | - Fix FC052: Metadata uses the unimplemented "suggests" keyword 85 | 86 | ## 3.0.0 2015-09-17 87 | 88 | - Merge server recipe into default recipe 89 | - Replace server_version, release_url and override attributes with just url attribute 90 | - Replace server_name attribute and provision name attribute with just servername attribute 91 | - Move drivers to their own cookbooks 92 | - Remove PhantomJS 93 | 94 | ## 2.8.1 2015-08-24 95 | 96 | - Fix #20 Unable to set hub and node recipe attributes 97 | 98 | ## 2.8.0 2015-08-21 99 | 100 | - Add hub and node recipes 101 | 102 | ## 2.7.0 2015-08-21 103 | 104 | - Allow custom arguments to be added to node service 105 | - Allow custom download url for selenium standalone jar 106 | 107 | ## 2.6.0 2015-08-02 108 | 109 | - Support Safari 110 | 111 | ## 2.5.2 2015-08-02 112 | 113 | - Fix #18 Selenium iedriver does not extract 114 | 115 | ## 2.5.1 2015-07-31 116 | 117 | - Update Selenium and IE driver from 2.46.0 to 2.47.0 118 | - Update ChromeDriver from 2.15 to 2.16 119 | 120 | ## 2.5.0 2015-07-29 121 | 122 | - Deprecate PhantomJS 123 | 124 | ## 2.4.2 2015-06-29 125 | 126 | - Add powershell_version check 127 | 128 | ## 2.4.1 2015-06-29 129 | 130 | - Fix #16 Errror provisioning chromedriver on centos7 131 | - Fix #15 Drivers not copied to /selenium/drivers/ folders on Windows 7 132 | 133 | ## 2.4.0 2015-06-09 134 | 135 | - Allow resources to be globally configured 136 | 137 | ## 2.3.3 2015-06-09 138 | 139 | - Fix #13 org.openqa.selenium.WebDriverException: chrome not reachable on CentOS 7.0/Ubuntu 14.04 140 | 141 | ## 2.3.2 2015-06-04 142 | 143 | - Update Selenium and IE driver from 2.45.0 to 2.46.0 144 | 145 | ## 2.3.1 2015-05-26 146 | 147 | - Add supports 'mac_os_x' to metadata 148 | 149 | ## 2.3.0 2015-05-26 150 | 151 | - Add support for Mac OS X (Chef 11.14 or higher required) 152 | 153 | ## 2.2.6 2015-05-26 154 | 155 | - Fix #11 chromedriver version does not update 156 | - Update ChromeDriver from 2.14 to 2.15 157 | 158 | ## 2.2.5 2015-05-01 159 | 160 | - Fix #10 'failed to allocate memory' exception on Windows 2008 161 | 162 | ## 2.2.4 2015-04-12 163 | 164 | - Fix selenium_node provider depends on windows 165 | - Set Windows display resolution in selenium_test cookbook 166 | 167 | ## 2.2.3 2015-03-23 168 | 169 | - Wrap host and hubHost in quotes in node config 170 | - Update IE driver from 2.44.0 to 2.45.0 171 | 172 | ## 2.2.2 2015-02-26 173 | 174 | - Firefox 36 breaks WebDriver 2.44.0 175 | 176 | ## 2.2.1 2015-02-18 177 | 178 | - Update ChromeDriver from 2.12 to 2.14 179 | 180 | ## 2.2.0 2015-02-05 181 | 182 | - Make Windows service an option for HtmlUnit and PhantomJS 183 | 184 | ## 2.1.0 2015-02-02 185 | 186 | - Support HtmlUnit 187 | 188 | ## 2.0.0 2015-02-02 189 | 190 | - Replace PhantomJS attributes 191 | 192 | ## 1.0.0 2015-02-01 193 | 194 | - Initial release 195 | -------------------------------------------------------------------------------- /libraries/default.rb: -------------------------------------------------------------------------------- 1 | def selenium_java_exec 2 | java = node['selenium']['java'] 3 | validate_exec(%("#{java}" -version)) 4 | java 5 | end 6 | 7 | def validate_exec(cmd) 8 | exec = Mixlib::ShellOut.new(cmd) 9 | exec.run_command 10 | exec.error! 11 | end 12 | 13 | def selenium_home 14 | node['selenium']['home'] 15 | end 16 | 17 | def selenium_jar_link 18 | "#{selenium_home}/server/selenium-server-standalone.jar" 19 | end 20 | 21 | def selenium_windows_service(name, exec, args) 22 | nssm name do 23 | program exec 24 | args args.join(' ') 25 | parameters(AppDirectory: selenium_home) 26 | action :install 27 | end 28 | end 29 | 30 | # http://sqa.stackexchange.com/a/6267 31 | def selenium_windows_gui_service(name, exec, args, username) 32 | cmd = "#{selenium_home}/bin/#{name}.cmd" 33 | 34 | file cmd do 35 | content %("#{exec}" #{args.join(' ')}) 36 | action :create 37 | notifies :request_reboot, "reboot[Reboot to start #{name}]" 38 | end 39 | 40 | startup_path = "C:\\Users\\#{username}\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup" 41 | 42 | ruby_block 'hack to mkdir on windows' do 43 | block do 44 | FileUtils.mkdir_p startup_path 45 | end 46 | end 47 | 48 | windows_shortcut "#{startup_path}\\#{name}.lnk" do 49 | target cmd 50 | cwd selenium_home 51 | action :create 52 | end 53 | end 54 | 55 | def selenium_windows_firewall(name, port) 56 | execute "Firewall rule #{name} for port #{port}" do 57 | command "netsh advfirewall firewall add rule name=\"#{name}\" protocol=TCP dir=in profile=any"\ 58 | " localport=#{port} remoteip=any localip=any action=allow" 59 | action :run 60 | not_if "netsh advfirewall firewall show rule name=\"#{name}\" > nul" 61 | end 62 | end 63 | 64 | def selenium_autologon(username, password) 65 | case node['platform_family'] 66 | when 'windows' 67 | windows_autologin username do 68 | password password 69 | end 70 | when 'mac_os_x' 71 | macosx_autologin username do 72 | password password 73 | end 74 | end 75 | end 76 | 77 | def selenium_systype 78 | return 'systemd' if ::File.exist?('/proc/1/comm') && ::File.open('/proc/1/comm').gets.chomp == 'systemd' 79 | return 'upstart' if platform?('ubuntu') && ::File.exist?('/sbin/initctl') 80 | 'sysvinit' 81 | end 82 | 83 | def selenium_linux_service(name, exec, args, port, xdisplay) 84 | # TODO: make selenium username default and pass it in as a param 85 | username = 'selenium' 86 | 87 | user "ensure user #{username} exits for #{name}" do 88 | username username 89 | manage_home true 90 | shell '/bin/bash' 91 | home "/home/#{username}" 92 | system true 93 | end 94 | 95 | systype = selenium_systype 96 | if systype == 'systemd' 97 | path = "/etc/systemd/system/#{name}.service" 98 | formatted_args = args.join(' ') 99 | else 100 | path = "/etc/init.d/#{name}" 101 | formatted_args = args.join(' ').gsub('"', '\"') 102 | end 103 | 104 | template path do 105 | source "#{systype}.erb" 106 | cookbook 'selenium' 107 | mode '0755' 108 | variables( 109 | name: name, 110 | user: username, 111 | exec: exec, 112 | args: formatted_args, 113 | port: port, 114 | xdisplay: xdisplay 115 | ) 116 | notifies :restart, "service[#{name}]" 117 | end 118 | 119 | service name do 120 | supports restart: true, reload: true, status: true 121 | action %i(enable start) 122 | end 123 | end 124 | 125 | def log_path(log, username) 126 | return if log.nil? 127 | 128 | directory 'create log dir' do 129 | path log[0, log.rindex('/')] 130 | mode '0755' 131 | recursive true 132 | not_if { ::File.exist?(log) } 133 | end 134 | 135 | file 'create log file' do 136 | path log 137 | mode '0664' 138 | user username 139 | action :touch 140 | not_if { ::File.exist?(log) } 141 | end 142 | end 143 | 144 | def selenium_mac_service(new_resource, exec, args, plist, username) 145 | name = selenium_mac_domain(new_resource.servicename) 146 | log = new_resource.log 147 | 148 | execute "reload #{name}" do 149 | command "launchctl unload -w #{plist}; launchctl load -w #{plist}" 150 | user username 151 | action :nothing 152 | returns [0, 112] # 112 not logged into gui 153 | end 154 | 155 | log_path(log, username) 156 | 157 | template plist do 158 | source 'org.seleniumhq.plist.erb' 159 | cookbook 'selenium' 160 | mode '0755' 161 | variables( 162 | name: name, 163 | exec: exec, 164 | args: args 165 | ) 166 | notifies :run, "execute[reload #{name}]", :immediately 167 | notifies :run, "execute[Reboot to start #{name}]" if username # assume node 168 | end 169 | end 170 | 171 | def selenium_mac_domain(name) 172 | "org.seleniumhq.#{name}" 173 | end 174 | -------------------------------------------------------------------------------- /spec/unit/hub_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'selenium_test::hub' do 4 | let(:shellout) { double(run_command: nil, error!: nil, stdout: ' ') } 5 | 6 | before do 7 | allow(Mixlib::ShellOut).to receive(:new).and_return(shellout) 8 | end 9 | 10 | context 'windows' do 11 | let(:chef_run) do 12 | ChefSpec::SoloRunner.new(platform: 'windows', version: '2008R2', step_into: ['selenium_hub']) do |node| 13 | ENV['SYSTEMDRIVE'] = 'C:' 14 | node.override['selenium']['url'] = 15 | 'https://selenium-release.storage.googleapis.com/3.0/selenium-server-standalone-3.0.1.jar' 16 | node.override['java']['windows']['url'] = 'http://ignore/jdk-windows-64x.tar.gz' 17 | stub_command('netsh advfirewall firewall show rule name="selenium_hub" > nul').and_return(false) 18 | end.converge(described_recipe) 19 | end 20 | 21 | it 'installs selenium_hub server' do 22 | expect(chef_run).to install_selenium_hub('selenium_hub') 23 | end 24 | 25 | it 'creates hub config file' do 26 | expect(chef_run).to create_template('C:/selenium/config/selenium_hub.json').with( 27 | source: 'hub_config.erb', 28 | cookbook: 'selenium' 29 | ) 30 | end 31 | 32 | it 'install selenium_hub' do 33 | expect(chef_run).to install_nssm('selenium_hub').with( 34 | program: 'C:\java\bin\java.exe', 35 | args: '-jar "C:/selenium/server/selenium-server-standalone.jar"'\ 36 | ' -role hub -hubConfig "C:/selenium/config/selenium_hub.json"', 37 | parameters: { 38 | AppDirectory: 'C:/selenium', 39 | } 40 | ) 41 | end 42 | 43 | it 'creates firewall rule' do 44 | expect(chef_run).to run_execute('Firewall rule selenium_hub for port 4444') 45 | end 46 | 47 | it 'reboots windows server' do 48 | expect(chef_run).to_not request_reboot('Reboot to start selenium_hub') 49 | end 50 | end 51 | 52 | context 'linux' do 53 | let(:chef_run) do 54 | ChefSpec::SoloRunner.new(platform: 'centos', version: '7.3.1611', step_into: ['selenium_hub']) do |node| 55 | node.override['selenium']['url'] = 56 | 'https://selenium-release.storage.googleapis.com/3.0/selenium-server-standalone-3.0.1.jar' 57 | allow_any_instance_of(Chef::Provider).to receive(:selenium_systype).and_return('systemd') 58 | end.converge(described_recipe) 59 | end 60 | 61 | it 'installs selenium_hub server' do 62 | expect(chef_run).to install_selenium_hub('selenium_hub') 63 | end 64 | 65 | it 'creates selenium user' do 66 | expect(chef_run).to create_user('ensure user selenium exits for selenium_hub').with(username: 'selenium') 67 | end 68 | 69 | it 'creates hub config file' do 70 | expect(chef_run).to create_template('/opt/selenium/config/selenium_hub.json').with( 71 | source: 'hub_config.erb', 72 | cookbook: 'selenium' 73 | ) 74 | end 75 | 76 | it 'install selenium_hub' do 77 | expect(chef_run).to create_template('/etc/systemd/system/selenium_hub.service').with( 78 | source: 'systemd.erb', 79 | cookbook: 'selenium', 80 | mode: '0755', 81 | variables: { 82 | name: 'selenium_hub', 83 | user: 'selenium', 84 | exec: '/usr/bin/java', 85 | args: '-jar "/opt/selenium/server/selenium-server-standalone.jar" -role hub ' \ 86 | '-hubConfig "/opt/selenium/config/selenium_hub.json"', 87 | port: 4444, 88 | xdisplay: nil, 89 | } 90 | ) 91 | end 92 | 93 | it 'start selenium_hub' do 94 | expect(chef_run).to start_service('selenium_hub') 95 | end 96 | end 97 | 98 | context 'mac_os_x' do 99 | let(:chef_run) do 100 | ChefSpec::SoloRunner.new(platform: 'mac_os_x', version: '10.10', step_into: ['selenium_hub']) do |node| 101 | node.override['selenium']['url'] = 102 | 'https://selenium-release.storage.googleapis.com/3.0/selenium-server-standalone-3.0.1.jar' 103 | node.override['selenium']['hub']['log'] = '/var/log/selenium/org.seleniumhq.selenium_hub.log' 104 | allow_any_instance_of(Chef::Recipe).to receive(:java_version_on_macosx?).and_return(false) 105 | end.converge(described_recipe) 106 | end 107 | 108 | it 'installs selenium_hub server' do 109 | expect(chef_run).to install_selenium_hub('selenium_hub') 110 | end 111 | 112 | it 'creates hub config file' do 113 | expect(chef_run).to create_template('/opt/selenium/config/selenium_hub.json').with( 114 | source: 'hub_config.erb', 115 | cookbook: 'selenium' 116 | ) 117 | end 118 | 119 | it 'creates log directory' do 120 | expect(chef_run).to create_directory('/var/log/selenium').with(user: nil) 121 | end 122 | 123 | it 'adds permissions to log file' do 124 | expect(chef_run).to touch_file('/var/log/selenium/org.seleniumhq.selenium_hub.log').with(user: nil, mode: '0664') 125 | end 126 | 127 | it 'install selenium_hub' do 128 | expect(chef_run).to create_template('/Library/LaunchDaemons/org.seleniumhq.selenium_hub.plist').with( 129 | source: 'org.seleniumhq.plist.erb', 130 | cookbook: 'selenium', 131 | mode: '0755', 132 | variables: { 133 | name: 'org.seleniumhq.selenium_hub', 134 | exec: '/usr/bin/java', 135 | args: ['-jar', '"/opt/selenium/server/selenium-server-standalone.jar"', '-role', 'hub', 136 | '-hubConfig', '"/opt/selenium/config/selenium_hub.json"'], 137 | } 138 | ) 139 | end 140 | 141 | it 'executes launchd reload' do 142 | expect(chef_run).to_not run_execute('reload org.seleniumhq.selenium_hub') 143 | end 144 | end 145 | end 146 | -------------------------------------------------------------------------------- /spec/unit/node_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'selenium_test::node' do 4 | let(:shellout) { double(run_command: nil, error!: nil, stdout: 'systemd') } 5 | 6 | before do 7 | stub_command('netsh advfirewall firewall show rule name="RDP" > nul').and_return(true) 8 | allow(Mixlib::ShellOut).to receive(:new).and_return(shellout) 9 | end 10 | 11 | context 'windows' do 12 | let(:chef_run) do 13 | ChefSpec::SoloRunner.new( 14 | file_cache_path: 'C:/chef/cache', 15 | platform: 'windows', 16 | version: '2008R2', 17 | step_into: ['selenium_node'] 18 | ) do |node| 19 | ENV['SYSTEMDRIVE'] = 'C:' 20 | node.override['selenium']['url'] = 21 | 'https://selenium-release.storage.googleapis.com/3.0/selenium-server-standalone-3.0.1.jar' 22 | node.override['java']['windows']['url'] = 'http://ignore/jdk-windows-64x.tar.gz' 23 | node.override['selenium_test']['username'] = 'vagrant' 24 | node.override['selenium_test']['password'] = 'vagrant' 25 | allow_any_instance_of(Chef::Recipe).to receive(:firefox_version).and_return('33.0.0') 26 | allow_any_instance_of(Chef::Recipe).to receive(:chrome_version).and_return('39.0.0.0') 27 | allow_any_instance_of(Chef::Recipe).to receive(:ie_version).and_return('11.0.0.0') 28 | allow_any_instance_of(Chef::DSL::RegistryHelper).to receive(:registry_key_exists?).and_return(true) 29 | stub_command('netsh advfirewall firewall show rule name="selenium_node" > nul') 30 | end.converge(described_recipe) 31 | end 32 | 33 | it 'installs selenium_node server' do 34 | expect(chef_run).to install_selenium_node('selenium_node') 35 | end 36 | 37 | it 'creates node config file' do 38 | expect(chef_run).to create_template('C:/selenium/config/selenium_node.json').with( 39 | source: 'node_config.erb', 40 | cookbook: 'selenium' 41 | ) 42 | end 43 | 44 | it 'sets up autologin' do 45 | expect(chef_run).to enable_windows_autologin('vagrant') 46 | end 47 | 48 | it 'creates startup dir' do 49 | expect(chef_run).to run_ruby_block('hack to mkdir on windows') 50 | end 51 | 52 | it 'creates shortcut to selenium cmd file' do 53 | expect(chef_run).to create_windows_shortcut( 54 | 'C:\Users\vagrant\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\selenium_node.lnk' 55 | ) 56 | end 57 | 58 | it 'creates selenium foreground command' do 59 | expect(chef_run).to create_file('C:/selenium/bin/selenium_node.cmd').with( 60 | content: '"C:\java\bin\java.exe" -jar "C:/selenium/server/selenium-server-standalone.jar" '\ 61 | '-role node -nodeConfig "C:/selenium/config/selenium_node.json"' 62 | ) 63 | end 64 | 65 | it 'creates firewall rule' do 66 | expect(chef_run).to run_execute('Firewall rule selenium_node for port 5555') 67 | end 68 | 69 | it 'reboots windows server' do 70 | expect(chef_run).to_not request_reboot('Reboot to start selenium_node') 71 | end 72 | end 73 | 74 | context 'linux' do 75 | let(:chef_run) do 76 | ChefSpec::SoloRunner.new( 77 | file_cache_path: '/var/chef/cache', platform: 'centos', version: '7.3.1611', step_into: ['selenium_node'] 78 | ) do |node| 79 | node.override['selenium']['url'] = 80 | 'https://selenium-release.storage.googleapis.com/3.0/selenium-server-standalone-3.0.1.jar' 81 | allow_any_instance_of(Chef::Recipe).to receive(:firefox_version).and_return('33.0.0') 82 | allow_any_instance_of(Chef::Recipe).to receive(:chrome_version).and_return('39.0.0.0') 83 | allow_any_instance_of(Chef::Provider).to receive(:selenium_systype).and_return('systemd') 84 | end.converge(described_recipe) 85 | end 86 | 87 | it 'installs selenium_node server' do 88 | expect(chef_run).to install_selenium_node('selenium_node') 89 | end 90 | 91 | it 'creates selenium user' do 92 | expect(chef_run).to create_user('ensure user selenium exits for selenium_node').with(username: 'selenium') 93 | end 94 | 95 | it 'creates node config file' do 96 | expect(chef_run).to create_template('/opt/selenium/config/selenium_node.json') 97 | end 98 | 99 | it 'install selenium_node' do 100 | expect(chef_run).to create_template('/etc/systemd/system/selenium_node.service').with( 101 | source: 'systemd.erb', 102 | cookbook: 'selenium', 103 | mode: '0755', 104 | variables: { 105 | name: 'selenium_node', 106 | user: 'selenium', 107 | exec: '/usr/bin/java', 108 | args: '-jar "/opt/selenium/server/selenium-server-standalone.jar" -role node ' \ 109 | '-nodeConfig "/opt/selenium/config/selenium_node.json"', 110 | port: 5555, 111 | xdisplay: ':0', 112 | } 113 | ) 114 | end 115 | 116 | it 'start selenium_node' do 117 | expect(chef_run).to start_service('selenium_node') 118 | end 119 | end 120 | 121 | context 'selenium 3' do 122 | let(:chef_run) do 123 | ChefSpec::SoloRunner.new( 124 | file_cache_path: '/var/chef/cache', platform: 'centos', version: '7.3.1611', step_into: ['selenium_node'] 125 | ) do |node| 126 | node.override['selenium']['url'] = 127 | 'https://selenium-release.storage.googleapis.com/3.0/selenium-server-standalone-3.0.1.jar' 128 | allow_any_instance_of(Chef::Recipe).to receive(:firefox_version).and_return('33.0.0') 129 | allow_any_instance_of(Chef::Recipe).to receive(:chrome_version).and_return('39.0.0.0') 130 | allow_any_instance_of(Chef::Provider).to receive(:selenium_systype).and_return('systemd') 131 | end.converge(described_recipe) 132 | end 133 | 134 | it 'uses selenium version 3 syntax for config file' do 135 | expect(chef_run).to render_file('/opt/selenium/config/selenium_node.json') 136 | end 137 | end 138 | end 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Selenium Cookbook 2 | 3 | [![Cookbook Version](http://img.shields.io/cookbook/v/selenium.svg?style=flat-square)][cookbook] 4 | [![linux](http://img.shields.io/travis/dhoer/chef-selenium/master.svg?label=linux&style=flat-square)][linux] 5 | [![osx](http://img.shields.io/travis/dhoer/chef-selenium/macosx.svg?label=macosx&style=flat-square)][osx] 6 | [![win](https://img.shields.io/appveyor/ci/dhoer/chef-selenium/master.svg?label=windows&style=flat-square)][win] 7 | 8 | [cookbook]: https://supermarket.chef.io/cookbooks/selenium 9 | [linux]: https://travis-ci.org/dhoer/chef-selenium/branches 10 | [osx]: https://travis-ci.org/dhoer/chef-selenium/branches 11 | [win]: https://ci.appveyor.com/project/dhoer/chef-selenium 12 | 13 | This cookbook installs and configures Selenium 3+ (http://www.seleniumhq.org/). 14 | 15 | This cookbook comes with the following recipes: 16 | 17 | - **[default](https://github.com/dhoer/chef-selenium#default)** - Downloads and installs Selenium Standalone jar. 18 | - **[hub](https://github.com/dhoer/chef-selenium#hub)** - Installs and configures a Selenium Hub as a service. 19 | - **[node](https://github.com/dhoer/chef-selenium#node)** - Installs and configures a Selenium Node as service 20 | on Linux and a GUI service on Mac OS X and Windows. 21 | 22 | Resources [selenium_hub](https://github.com/dhoer/chef-selenium#selenium_hub) and 23 | [selenium_node](https://github.com/dhoer/chef-selenium#selenium_node) are also available. 24 | 25 | ## Usage 26 | 27 | See [selenium_grid](https://github.com/dhoer/chef-selenium_grid#selenium-grid-cookbook) cookbook that wraps selenium, 28 | browsers, drivers, and screenresolution cookbooks into one comprehensive cookbook. 29 | 30 | ## Requirements 31 | 32 | - Java (not installed by this cookbook) 33 | - Chef 12.6+ 34 | 35 | ### Platforms 36 | 37 | - CentOS, Fedora, RedHat 38 | - Mac OS X 39 | - Debian, Ubuntu 40 | - Windows 41 | 42 | ### Cookbooks 43 | 44 | - nssm - Required by Windows services only (e.g. Hub and HtmlUnit running in background) 45 | - macosx_autologin - Required by Mac OS X GUI services 46 | - windows 47 | - windows_autologin - Required by Windows GUI service 48 | 49 | ## Recipes 50 | 51 | ## default 52 | 53 | Downloads and installs Selenium Standalone jar. 54 | 55 | ### Attributes 56 | 57 | - `node['selenium']['url']` - The download URL of Selenium Standalone jar. 58 | - `node['selenium']['windows']['home']` - Home directory. Default `#{ENV['SYSTEMDRIVE']}/selenium`. 59 | - `node['selenium']['windows']['java']` - Path to Java executable. Default 60 | `#{ENV['SYSTEMDRIVE']}\\java\\bin\\java.exe`. 61 | - `node['selenium']['unix']['home']` - Home directory. Default `/opt/selenium`. 62 | - `node['selenium']['unix']['java']` - Path to Java executable. Default `/usr/bin/java`. 63 | 64 | ## hub 65 | 66 | Installs and configures a Selenium Hub as a service. 67 | 68 | ### Attributes 69 | 70 | See [selenium_hub](https://github.com/dhoer/chef-selenium#attributes-3) 71 | resource attributes for description. 72 | 73 | ## node 74 | 75 | Installs and configures a Selenium Node as service on Linux and a GUI 76 | service on Mac OS X and Windows. 77 | 78 | - Firefox browser must be installed outside of this cookbook. 79 | - Linux nodes without a physical monitor require a headless display 80 | (e.g., [xvfb](https://supermarket.chef.io/cookbooks/xvfb), 81 | [x11vnc](https://supermarket.chef.io/cookbooks/x11vnc), 82 | etc...) and must be installed and configured outside this cookbook. 83 | - Mac OS X/Windows nodes must run as a GUI service and that requires a 84 | username and password for automatic login. Note that Windows password 85 | is stored unencrypted under windows registry 86 | `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon` and 87 | Mac OS X password is stored encrypted under `/etc/kcpassword` but it 88 | can be easily decrypted. 89 | 90 | ### Attributes 91 | 92 | See [selenium_hub](https://github.com/dhoer/chef-selenium#attributes-4) 93 | resource attributes for description. 94 | 95 | ### Example 96 | 97 | #### Install Selenium Node with Firefox and HtmlUnit capabilities 98 | 99 | ```ruby 100 | node.override['selenium']['node']['username'] = 'vagrant' if platform?('windows', 'mac_os_x') 101 | node.override['selenium']['node']['password'] = 'vagrant' if platform?('windows', 'mac_os_x') 102 | 103 | node.override['selenium']['node']['capabilities'] = [ 104 | { 105 | browserName: 'firefox', 106 | maxInstances: 5, 107 | seleniumProtocol: 'WebDriver' 108 | }, 109 | { 110 | browserName: 'htmlunit', 111 | maxInstances: 1, 112 | platform: 'ANY', 113 | seleniumProtocol: 'WebDriver' 114 | } 115 | ] 116 | 117 | include_recipe 'selenium::node' 118 | ``` 119 | 120 | ## Resources 121 | 122 | ## selenium_hub 123 | 124 | Installs and configures a Selenium Hub as a service. 125 | 126 | ### Attributes 127 | 128 | - `servicename` - Name attribute. The name of the service. 129 | - `host` - IP or hostname. Usually determined automatically. Most 130 | commonly useful in exotic network configurations (e.g. network with 131 | VPN). Default `nil`. 132 | - `port` - The port number the server will use. Default: `4444`. 133 | - `jvm_args` - JVM options, e.g., -Xms2G -Xmx2G. Default: `nil`. 134 | - `newSessionWaitTimeout` - The time (in ms) after which a new test 135 | waiting for a node to become available will time out. When that happens, 136 | the test will throw an exception before attempting to start a browser. 137 | An unspecified, zero, or negative value means wait indefinitely. 138 | Default: `-1`. 139 | - `prioritizer` - A class implementing the Prioritizer interface. 140 | Specify a custom Prioritizer if you want to sort the order in which new 141 | session requests are processed when there is a queue. 142 | Default to null ( no priority = FIFO ). 143 | - `servlets` - List of extra servlets the grid (hub or node) will make 144 | available. The servlet must exist in the path, e.g., 145 | /grid/admin/Servlet. Default: `[]`. 146 | - `withoutServlets` - List of default (hub or node) servlets to disable. 147 | Advanced use cases only. Not all default servlets can be disabled. 148 | Default: `[]`. 149 | - `capabilityMatcher` - A class implementing the CapabilityMatcher 150 | interface. Specifies the logic the hub will follow to define whether a 151 | request can be assigned to a node. For example, if you want to have the 152 | matching process use regular expressions instead of exact match when 153 | specifying browser version. ALL nodes of a grid ecosystem would then 154 | use the same capabilityMatcher, as defined here. 155 | Default: `org.openqa.grid.internal.utils.DefaultCapabilityMatcher` 156 | - `throwOnCapabilityNotPresent` - If true, the hub will reject all test 157 | requests if no compatible proxy is currently registered. If set to 158 | false, the request will queue until a node supporting the capability is 159 | registered with the grid. Default: `true`. 160 | - `cleanUpCycle` - Specifies how often the hub will poll (in ms) 161 | running proxies for timed-out (i.e. hung) threads. Must also specify 162 | "timeout" option. Default: `5000`. 163 | - `debug` - Enables LogLevel.FINE. Default: `false`. 164 | - `timeout` - Specifies the timeout before the server automatically 165 | kills a session that hasn't had any activity in the last X seconds. 166 | The test slot will then be released for another test to use. This is 167 | typically used to take care of client crashes. For grid hub/node roles, 168 | cleanUpCycle must also be set. Default: `1800`. 169 | - `browserTimeout` - Number of seconds a browser session is allowed to 170 | hang while a WebDriver command is running (example: driver.get(url)). 171 | If the timeout is reached while a WebDriver command is still processing, 172 | the session will quit. Minimum value is `60`. An unspecified, zero, 173 | or negative value means wait indefinitely. Default: `0`. 174 | - `maxSession` - Max number of tests that can run at the same time on 175 | the node, irrespective of the browser used. Default: `5`. 176 | - `jettyMaxThreads` - Max number of threads for Jetty. An unspecified, 177 | zero, or negative value means the Jetty default value (200) will be 178 | used. Default: `-1`. 179 | - `log` - The filename to use for logging. If omitted, will log to 180 | STDOUT. Default: `nil`. 181 | 182 | ## selenium_node 183 | 184 | Installs and configures a Selenium Node as a service. 185 | 186 | ### Attributes 187 | 188 | - `servicename` - Name attribute. The name of the service. 189 | - `host` - IP or hostname. Usually determined automatically. Most 190 | commonly useful in exotic network configurations (e.g. network with 191 | VPN). Default `nil`. 192 | - `port` - The port number the server will use. Default: `5555`. 193 | - `hub` - The url that will be used to post the registration request. 194 | Default: `http://localhost:4444`. 195 | - `jvm_args` - JVM options, e.g., -Xms2G -Xmx2G. Default: `nil`. 196 | - `proxy` - The class used to represent the node proxy. 197 | Default: `org.openqa.grid.selenium.proxy.DefaultRemoteProxy`. 198 | - `maxSession` - Max number of tests that can run at the same time on 199 | the node, irrespective of the browser used. Default: `5`. 200 | - `register` - Node will attempt to re-register itself automatically 201 | with its known grid hub if the hub becomes unavailable. Default: `true`. 202 | - `registerCycle` - Specifies (in ms) how often the node will try to 203 | register itself again. Allows administrator to restart the hub without 204 | restarting (or risk orphaning) registered nodes. Must be specified with 205 | the "register" option. Default: `5000`. 206 | - `nodeStatusCheckTimeout` - When to time out a node status check. 207 | Default: `5000`. 208 | - `nodePolling` - Specifies (in ms) how often the hub will 209 | poll to see if the node is still responding. Default: `5000`. 210 | - `unregisterIfStillDownAfter` - If the node remains down for more 211 | than specified (in ms), it will stop attempting to re-register from the 212 | hub. Default: `60000`. 213 | - `downPollingLimit` - Node is marked as "down" if the node hasn't 214 | responded after the number of checks specified. Default: `2`. 215 | - `debug` - [TrueClass, FalseClass], default: false 216 | - `servlets` - List of extra servlets the grid (hub or node) will make 217 | available. The servlet must exist in the path, e.g., 218 | /grid/admin/Servlet. Default: `[]`. 219 | - `withoutServlets` - List of default (hub or node) servlets to disable. 220 | Advanced use cases only. Not all default servlets can be disabled. 221 | Default: `[]`. 222 | - `debug` - Enables LogLevel.FINE. Default: `false`. 223 | - `timeout` - Specifies the timeout before the server automatically 224 | kills a session that hasn't had any activity in the last X seconds. 225 | The test slot will then be released for another test to use. This is 226 | typically used to take care of client crashes. For grid hub/node roles, 227 | cleanUpCycle must also be set. Default: `1800`. 228 | - `browserTimeout` - Number of seconds a browser session is allowed to 229 | hang while a WebDriver command is running (example: driver.get(url)). 230 | If the timeout is reached while a WebDriver command is still processing, 231 | the session will quit. Minimum value is `60`. An unspecified, zero, 232 | or negative value means wait indefinitely. Default: `0`. 233 | - `jettyMaxThreads` - Max number of threads for Jetty. An unspecified, 234 | zero, or negative value means the Jetty default value (200) will be 235 | used. Default: `-1`. 236 | - `log` - The filename to use for logging. If omitted, will log to 237 | STDOUT. Default: `nil`. 238 | - `capabilities` - Based on 239 | [capabilities](https://code.google.com/p/selenium/wiki/DesiredCapabilities). Default `[]`. 240 | - Mac OS X/Windows only - Set both username and password to run as a GUI service: 241 | - `username` - Default `nil`. 242 | - `password` - Default `nil`. Note that Windows password is stored unencrypted under windows registry 243 | `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon` and Mac OS X password is stored encrypted under 244 | `/etc/kcpassword` but it can be easily decrypted. 245 | - `domain` - Optional for Windows only. Default `nil`. 246 | 247 | ### Example 248 | 249 | #### Install Selenium Node with Firefox and HtmlUnit capabilities 250 | 251 | ```ruby 252 | selenium_node 'selenium_node' do 253 | username 'vagrant' if platform?('windows', 'mac_os_x') 254 | password 'vagrant' if platform?('windows', 'mac_os_x') 255 | capabilities [ 256 | { 257 | browserName: 'firefox', 258 | maxInstances: 5, 259 | seleniumProtocol: 'WebDriver' 260 | }, 261 | { 262 | browserName: 'htmlunit', 263 | maxInstances: 1, 264 | platform: 'ANY', 265 | seleniumProtocol: 'WebDriver' 266 | } 267 | ] 268 | action :install 269 | end 270 | ``` 271 | 272 | ## ChefSpec Matchers 273 | 274 | This cookbook includes custom 275 | [ChefSpec](https://github.com/sethvargo/chefspec) matchers you can use 276 | to test your own cookbooks. 277 | 278 | Example Matcher Usage 279 | 280 | ```ruby 281 | expect(chef_run).to install_selenium_hub('resource_name').with( 282 | port: '4444' 283 | ) 284 | ``` 285 | 286 | Selenium Cookbook Matchers 287 | 288 | - install_selenium_hub(resource_name) 289 | - install_selenium_node(resource_name) 290 | 291 | ## Getting Help 292 | 293 | - Ask specific questions on 294 | [Stack Overflow](http://stackoverflow.com/questions/tagged/selenium). 295 | - Report bugs and discuss potential features in 296 | [Github issues](https://github.com/dhoer/chef-selenium/issues). 297 | 298 | ## Contributing 299 | 300 | Please refer to [CONTRIBUTING](https://github.com/dhoer/chef-selenium/blob/master/CONTRIBUTING.md). 301 | 302 | ## License 303 | 304 | MIT - see the accompanying 305 | [LICENSE](https://github.com/dhoer/chef-selenium/blob/master/LICENSE.md) 306 | file for details. 307 | --------------------------------------------------------------------------------