leap_salesforce_ui 0.1.1 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.gitlab-ci.yml +4 -0
- data/.leap_salesforce.yml +1 -0
- data/ChangeLog +25 -0
- data/Dockerfile +2 -1
- data/Gemfile +1 -0
- data/README.md +102 -7
- data/Rakefile +3 -1
- data/bin/console +2 -5
- data/bin/setup +1 -2
- data/exe/leap_salesforce_ui +45 -1
- data/leap_salesforce_ui.gemspec +3 -3
- data/lib/leap_salesforce_ui.rb +25 -8
- data/lib/leap_salesforce_ui/base_page.rb +5 -2
- data/lib/leap_salesforce_ui/create_page.rb +2 -90
- data/lib/leap_salesforce_ui/error.rb +9 -0
- data/lib/leap_salesforce_ui/form_filler.rb +152 -0
- data/lib/leap_salesforce_ui/generator/appenders.rb +28 -0
- data/lib/leap_salesforce_ui/generator/page_objects.rb +30 -0
- data/lib/leap_salesforce_ui/generator/templates/Gemfile.erb +1 -0
- data/lib/leap_salesforce_ui/generator/templates/Rakefile.erb +1 -0
- data/lib/leap_salesforce_ui/generator/templates/create_page.rb.erb +5 -0
- data/lib/leap_salesforce_ui/generator/templates/spec_helper.rb.erb +21 -0
- data/lib/leap_salesforce_ui/generator/templates/ui_spec.rb.erb +22 -0
- data/lib/leap_salesforce_ui/generator/templates/update_page.rb.erb +5 -0
- data/lib/leap_salesforce_ui/generator/templates/view_page.rb.erb +5 -0
- data/lib/leap_salesforce_ui/login_page.rb +15 -6
- data/lib/leap_salesforce_ui/page_introspection.rb +11 -0
- data/lib/leap_salesforce_ui/rake.rb +4 -0
- data/lib/leap_salesforce_ui/rake/pom.rake +9 -0
- data/lib/leap_salesforce_ui/update_page.rb +16 -0
- data/lib/leap_salesforce_ui/version.rb +1 -1
- data/lib/leap_salesforce_ui/view_page.rb +6 -3
- data/run_tests_when_ready.sh +1 -1
- metadata +21 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d7e63743b865e1a4fd706abeb9772fa5726a6304c336fe40dd6f32a455119b9
|
4
|
+
data.tar.gz: da6bfe38c4f2479a0ddb06e748ff262ac80d45fe9872839d115c0ccdb1eda758
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c0e05222bdb5c9c86f4367fc50a5e0bbb381d21329e7588cc4760011d636b4526aebe2e84db0ccf6ae60441186f673b6df70a73a1c370b3cbb54191d19f84c8
|
7
|
+
data.tar.gz: 78456a3a7afcd73a3e7b779843f17485f45b6c11b406038fa1ec43bdf2b8aee1b9cb6bef4fa66796f44c32f571591e6db1ce7b8b8f636d2064c3383dc85ddb6d
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
CHANGED
@@ -11,6 +11,7 @@ test:
|
|
11
11
|
script:
|
12
12
|
- apk add docker-compose
|
13
13
|
- docker-compose version
|
14
|
+
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
14
15
|
- docker pull --quiet elgalu/selenium:3.141.59-p37 # Explicitly pull selenium image with working version
|
15
16
|
- docker-compose up --abort-on-container-exit --exit-code-from test
|
16
17
|
artifacts:
|
@@ -22,3 +23,6 @@ test:
|
|
22
23
|
when: always
|
23
24
|
variables:
|
24
25
|
BROWSER: chrome
|
26
|
+
|
27
|
+
include:
|
28
|
+
- template: Code-Quality.gitlab-ci.yml
|
data/.leap_salesforce.yml
CHANGED
data/ChangeLog
CHANGED
@@ -1,3 +1,28 @@
|
|
1
|
+
Version 0.1.6
|
2
|
+
* Bug fix
|
3
|
+
* Not appending to Gemfile meant Rakefile could not load leap_salesfore_ui rake tasks
|
4
|
+
|
5
|
+
Version 0.1.5
|
6
|
+
* Enhancements
|
7
|
+
* Added logging of actions
|
8
|
+
* Allow headless mode with LeapSalesforce.headless = true
|
9
|
+
* Throw custom errors when problem occurs setting fields
|
10
|
+
* Bug fix
|
11
|
+
* Didn't handle where class name and backend name differ
|
12
|
+
|
13
|
+
Version 0.1.4
|
14
|
+
* Enhancements
|
15
|
+
* Add executable to make it fast to setup testing with exe `leap_salesforce_ui init`
|
16
|
+
|
17
|
+
Version 0.1.3
|
18
|
+
* Enhancements
|
19
|
+
* Explicitly specify higher version of Watir as minimum which is compatible with Ruby 3
|
20
|
+
|
21
|
+
Version 0.1.2
|
22
|
+
* Enhancements
|
23
|
+
* Added Rake task to auto-generate page classes from soql_objects
|
24
|
+
* Added update base class to update entities in a similar way to creating them
|
25
|
+
|
1
26
|
Version 0.1.1
|
2
27
|
* Enhancements
|
3
28
|
* Add filling in of dropdown, reference field and text area to create
|
data/Dockerfile
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -23,21 +23,116 @@ Or install it yourself as:
|
|
23
23
|
|
24
24
|
## Usage
|
25
25
|
|
26
|
-
|
26
|
+
This gem is an extension to [leap salesforce](https://gitlab.com/leap-dojo/leap_salesforce)
|
27
|
+
Setup `leap_salesforce` with `leap_salesforce init` and use the retrospective rake tasks to create soql objects.
|
27
28
|
|
28
|
-
|
29
|
+
Add the line `require 'leap_salesforce_ui/rake'` to your `Rakefile`. Then run the Rake task `rake leaps:create_poms`
|
30
|
+
which will generate page objects based upon the `soql_objects`
|
29
31
|
|
30
|
-
|
32
|
+
Set the current UI user to be used with the `LeapSalesforce.ui_user` attribute.
|
33
|
+
|
34
|
+
E.g
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
LeapSalesforce.ui_user = 'test.user@salesforce.com'
|
38
|
+
```
|
39
|
+
|
40
|
+
See [leap salesforce test users section](https://gitlab.com/leap-dojo/leap_salesforce#test-users)
|
41
|
+
for details on ways to set test users.
|
42
|
+
|
43
|
+
### Example spec_helper
|
44
|
+
|
45
|
+
Following is example code that can be used to setup and teardown each test.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
require 'leap_salesforce_ui'
|
49
|
+
RSpec.configure do |config|
|
50
|
+
LeapSalesforce.ui_user = LeapSalesforce::Users.list.first
|
51
|
+
config.before(:each) do |example|
|
52
|
+
# Start browser with test name as metadata
|
53
|
+
LeapSalesforce.browser example.full_description
|
54
|
+
end
|
55
|
+
|
56
|
+
config.after(:each) do |example|
|
57
|
+
# Take screenshot on failure
|
58
|
+
screenshot = "logs/#{tidy_description(example.full_description.to_s)}_failure.png"
|
59
|
+
LeapSalesforce.browser.screenshot.save screenshot if example.exception
|
60
|
+
# Use a fresh browser for each test
|
61
|
+
LeapSalesforce.close_browser
|
62
|
+
end
|
63
|
+
|
64
|
+
# Tidy RSpec example description so it's suitable for a screenshot name
|
65
|
+
# @return [String] Description of test suitable for file name
|
66
|
+
def tidy_description(rspec_description)
|
67
|
+
rspec_description.delete("#<RSpec::Core::Example").delete('>".:{}').delete("=").delete(" ")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
### Populate form using label in metadata
|
73
|
+
|
74
|
+
Following is a walk through of one of the tests. If you've copied the above code into `spec/spec_helper.rb`
|
75
|
+
you should be able to copy this code into a new test file and execute it.
|
76
|
+
|
77
|
+
> It may or may not work depending on how the fields Contact has in your org
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
RSpec.describe "Creating record" do
|
81
|
+
let(:first_name) { Faker::Name.first_name }
|
82
|
+
let(:last_name) { Faker::Name.last_name }
|
83
|
+
let(:salutation) { Contact::Salutation.sample }
|
84
|
+
let(:other_street) { Faker::Address.full_address }
|
85
|
+
it "populate form and save" do
|
86
|
+
account = FactoryBot.create(:account)
|
87
|
+
CreateContactPage.visit.submit_with({ "First Name" => first_name,
|
88
|
+
last_name: last_name,
|
89
|
+
salutation: salutation,
|
90
|
+
other_street: other_street,
|
91
|
+
account_id: account.account_name })
|
92
|
+
contact_id = ViewContactPage.id
|
93
|
+
@contact = Contact.find(id: contact_id)
|
94
|
+
expect(@contact.first_name).to eq first_name
|
95
|
+
expect(@contact.last_name).to eq last_name
|
96
|
+
expect(@contact.salutation).to eq salutation
|
97
|
+
expect(@contact.other_street).to eq other_street
|
98
|
+
expect(@contact.account_id).to eq account.id
|
99
|
+
end
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
One doesn’t have to identify how to recognise first_name, last_name, etc.
|
104
|
+
From the metadata it works out what kind of field (textfield, textarea, reference) and then
|
105
|
+
finds the corresponding label and uses that to fill in the field.
|
106
|
+
|
107
|
+
This test then uses the API to setup a dependent account (through FactoryBot) and then to find the contact
|
108
|
+
created to verify that what was entered on the UI was saved as expected.
|
109
|
+
|
110
|
+
### Manually specify label
|
111
|
+
|
112
|
+
Sometimes the label on the UI does not match the metadata label. In that case, one case manually
|
113
|
+
specify the type of object, the label and what to set it to. E.g.,
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
UpdateAccountPage.visit(@account.id).set_picklist("Type", Account::AccountType.prospect).save
|
117
|
+
```
|
118
|
+
|
119
|
+
This will set a picklist with the label `Type` to the value defined by the picklist `AccountType`, and
|
120
|
+
the value `prospect`.
|
121
|
+
|
122
|
+
Follow other examples in `spec` to build tests using the auto-generated classes.
|
123
|
+
|
124
|
+
> These tests require that you [disable 2 step authentication for users](https://gitlab.com/leap-dojo/leap_salesforce_ui/-/wikis/Disable-2-step-authentication-and-allow-IP-range)
|
31
125
|
unless run from an IP address that your Salesforce Org trusts. Many shared CI environments have runners that would
|
32
126
|
be hard to whitelist as the address is dynamic.
|
33
127
|
|
34
|
-
> In the future a script will be used to scaffold base classes based on `.leap_salesforce.yml`
|
35
|
-
|
36
128
|
## Development
|
37
129
|
|
38
|
-
After checking out the repo, run `bin/setup` to install dependencies.
|
130
|
+
After checking out the repo, run `bin/setup` to install dependencies.
|
131
|
+
Then, run `rake spec` to run the tests.
|
132
|
+
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
39
133
|
|
40
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
134
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
135
|
+
To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
41
136
|
|
42
137
|
## Contributing
|
43
138
|
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -8,8 +8,5 @@ require "leap_salesforce_ui"
|
|
8
8
|
# with your gem easier. You can also use a different console, if you like.
|
9
9
|
|
10
10
|
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
require "irb"
|
15
|
-
IRB.start(__FILE__)
|
11
|
+
require "pry"
|
12
|
+
Pry.start
|
data/bin/setup
CHANGED
data/exe/leap_salesforce_ui
CHANGED
@@ -1,3 +1,47 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
|
4
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
5
|
+
|
6
|
+
require "leap_salesforce/error"
|
7
|
+
require "thor"
|
8
|
+
|
9
|
+
def init_setup?
|
10
|
+
File.exist?(".leap_salesforce.yml")
|
11
|
+
end
|
12
|
+
|
13
|
+
unless init_setup?
|
14
|
+
puts "Setting up leap_salesforce API first"
|
15
|
+
Process.fork { exec("leap_salesforce init") }
|
16
|
+
Process.wait
|
17
|
+
|
18
|
+
raise LeapSalesforce::SetupError, "Unable to setup leap_salesforce" unless init_setup?
|
19
|
+
end
|
20
|
+
|
21
|
+
require "leap_salesforce_ui/version"
|
22
|
+
require "leap_salesforce_ui/generator/appenders"
|
23
|
+
require "colorize"
|
24
|
+
|
25
|
+
module LeapSalesforce
|
26
|
+
# Executable for setting up Leap Salesforce UI
|
27
|
+
class UiExe < Thor
|
28
|
+
include LeapSalesforce::Generators::Appenders
|
29
|
+
|
30
|
+
desc "init", "Create leap salesforce ui configuration"
|
31
|
+
def init
|
32
|
+
puts "Initialising initial files to get started with leap_salesforce_ui"
|
33
|
+
append "Rakefile", "Rakefile.erb"
|
34
|
+
append "Gemfile", "Gemfile.erb"
|
35
|
+
puts 'Running Rake task "leaps:create_poms"'
|
36
|
+
puts `rake leaps:create_poms`
|
37
|
+
append File.join("spec", "spec_helper.rb"), "spec_helper.rb.erb"
|
38
|
+
append File.join("spec", "ui_spec.rb"), "ui_spec.rb.erb"
|
39
|
+
puts "Note test 'ui_spec' is specific to 'Contact' and 'Account' object using fields that
|
40
|
+
may not exist but purely as a demonstration."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
puts "Using #{LeapSalesforceUi::VERSION} of LeapSalesforceUi"
|
46
|
+
|
47
|
+
LeapSalesforce::UiExe.start(ARGV)
|
data/leap_salesforce_ui.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "lib/leap_salesforce_ui/version"
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "leap_salesforce_ui"
|
@@ -29,8 +29,8 @@ It reads the Metadata from Salesforce and creates the foundation for UI tests.'
|
|
29
29
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
30
30
|
spec.require_paths = ["lib"]
|
31
31
|
|
32
|
-
spec.add_dependency "leap_salesforce", ">= 1.0.
|
33
|
-
spec.add_dependency "watir"
|
32
|
+
spec.add_dependency "leap_salesforce", ">= 1.0.3"
|
33
|
+
spec.add_dependency "watir", ">= 6.17.0"
|
34
34
|
spec.add_dependency "webdrivers"
|
35
35
|
|
36
36
|
# For more information and examples about making a new gem, checkout our
|
data/lib/leap_salesforce_ui.rb
CHANGED
@@ -4,18 +4,26 @@ require "watir"
|
|
4
4
|
require "webdrivers"
|
5
5
|
require "leap_salesforce"
|
6
6
|
require_relative "leap_salesforce_ui/version"
|
7
|
+
require_relative "leap_salesforce_ui/error"
|
7
8
|
require_relative "leap_salesforce_ui/base_page"
|
9
|
+
require_relative "leap_salesforce_ui/page_introspection"
|
10
|
+
require_relative "leap_salesforce_ui/form_filler"
|
8
11
|
require_relative "leap_salesforce_ui/create_page"
|
9
12
|
require_relative "leap_salesforce_ui/view_page"
|
13
|
+
require_relative "leap_salesforce_ui/update_page"
|
14
|
+
page_obj_folder = File.join(LeapSalesforce.lib_folder, "page_objects")
|
15
|
+
require_all page_obj_folder if Dir.exist? page_obj_folder
|
10
16
|
|
17
|
+
# @return [Hash]
|
11
18
|
def zalenium_args(feature_name, scenario)
|
12
|
-
args = { timeout: 120, url: ENV[
|
19
|
+
args = { timeout: 120, url: ENV["WEBDRIVER_URL"], name: "Scenario: #{scenario} #{Time.now}",
|
13
20
|
'zal:build': "Feature: #{feature_name}" }
|
14
|
-
|
21
|
+
case ENV["BROWSER"]
|
22
|
+
when "chrome"
|
15
23
|
args.merge!('goog:chromeOptions': {
|
16
|
-
|
17
|
-
|
18
|
-
|
24
|
+
args: ENV["WEBDRIVER_CHROMEOPTIONS"]&.split(" ") || %w[]
|
25
|
+
})
|
26
|
+
when "firefox"
|
19
27
|
args.merge!(timeouts: 120)
|
20
28
|
end
|
21
29
|
args
|
@@ -25,6 +33,8 @@ end
|
|
25
33
|
module LeapSalesforce
|
26
34
|
# @return [String] User for UI
|
27
35
|
@ui_user = nil
|
36
|
+
# @return [Boolean] Whether to run in headless mode
|
37
|
+
@headless = false
|
28
38
|
class << self
|
29
39
|
def browser(test_name = nil)
|
30
40
|
@browser ||= new_browser(test_name)
|
@@ -35,8 +45,12 @@ module LeapSalesforce
|
|
35
45
|
@browser = nil
|
36
46
|
end
|
37
47
|
|
48
|
+
# @return [String] User used for UI tests
|
38
49
|
attr_reader :ui_user
|
39
50
|
|
51
|
+
# @return [Boolean] Whether to run in headless mode. Default false
|
52
|
+
attr_accessor :headless
|
53
|
+
|
40
54
|
# @param [String, Symbol, Regexp, LeapSalesforce::User] user User or email address of user
|
41
55
|
def ui_user=(user)
|
42
56
|
@ui_user = if user.is_a? String
|
@@ -50,10 +64,13 @@ module LeapSalesforce
|
|
50
64
|
private
|
51
65
|
|
52
66
|
def new_browser(test_name = nil)
|
53
|
-
if ENV[
|
54
|
-
|
67
|
+
if ENV["WEBDRIVER_URL"]
|
68
|
+
# TODO: Get working on Ruby 3
|
69
|
+
Watir::Browser.new :chrome, { **zalenium_args("LeapSalesforce", test_name || "Test") }
|
55
70
|
else
|
56
|
-
|
71
|
+
args = {}
|
72
|
+
args[:headless] = true if headless
|
73
|
+
Watir::Browser.new :chrome, args
|
57
74
|
end
|
58
75
|
end
|
59
76
|
end
|
@@ -8,14 +8,17 @@ module BasePage
|
|
8
8
|
LeapSalesforce.browser
|
9
9
|
end
|
10
10
|
|
11
|
+
# Set entity this page object refers to
|
12
|
+
# @param [Class] soql_object Backend name of SoqlObject this page object refers to
|
11
13
|
def soql_object(soql_object)
|
12
14
|
@soql_object = soql_object
|
13
15
|
end
|
14
16
|
|
17
|
+
# Visit the current page, logging in if required
|
15
18
|
def visit
|
16
|
-
LoginPage.visit
|
17
19
|
LoginPage.login
|
18
|
-
page_url = "#{SoqlHandler.instance_url}/lightning/o/#{@soql_object}/new"
|
20
|
+
page_url = "#{SoqlHandler.instance_url}/lightning/o/#{@soql_object.soql_object_name}/new"
|
21
|
+
LeapSalesforce.logger.info "Visiting #{self}"
|
19
22
|
browser.goto page_url
|
20
23
|
self
|
21
24
|
end
|
@@ -1,95 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
def new_panel
|
5
|
-
browser.div(class: "inlinePanel")
|
6
|
-
end
|
7
|
-
|
8
|
-
# Use metadata to get label for field
|
9
|
-
# @param [String] field
|
10
|
-
def get_label_for(field)
|
11
|
-
raise "SoqlObject not defined" unless @soql_object
|
12
|
-
|
13
|
-
return field if @soql_object.label_names.include? field
|
14
|
-
|
15
|
-
raise "Cannot find field #{field} on #{@soql_object}" unless @soql_object.accessors[field.to_sym]
|
16
|
-
|
17
|
-
@soql_object.accessors[field.to_sym][:label]
|
18
|
-
end
|
19
|
-
|
20
|
-
# @return [Hash] Description of field
|
21
|
-
def field_for(desc)
|
22
|
-
ruby_desc = @soql_object.accessors[desc.to_sym]
|
23
|
-
return ruby_desc if ruby_desc
|
24
|
-
|
25
|
-
@soql_object.properties_for(desc)
|
26
|
-
end
|
27
|
-
|
28
|
-
# Based on data type of field passed, fill in each field
|
29
|
-
# @param [Hash] data
|
30
|
-
def populate_with(data)
|
31
|
-
data.each do |desc, value|
|
32
|
-
field = field_for(desc)
|
33
|
-
label_name = field[:label] || field['label']
|
34
|
-
|
35
|
-
type = field[:type] || field['type']
|
36
|
-
case type
|
37
|
-
when 'string'
|
38
|
-
new_panel.text_field(xpath: "//label[contains(.,'#{label_name}')]//following-sibling::input").set value
|
39
|
-
when 'picklist' then set_picklist(label_name, value)
|
40
|
-
when 'textarea'
|
41
|
-
new_panel.textarea(xpath: "//label[contains(.,'#{label_name}')]//following-sibling::textarea").set value
|
42
|
-
when 'reference'
|
43
|
-
set_reference_field(label_name, value)
|
44
|
-
else
|
45
|
-
raise NotImplementedError, "#{type} not yet defined in #{self.class}"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
# Set value of picklist
|
51
|
-
# @param [String] label Label of picklist
|
52
|
-
# @param [String] value Value to set picklist to
|
53
|
-
def set_picklist(label, value)
|
54
|
-
dropdown = new_panel.link(xpath: "//*[./*[text()='#{label}']]//following-sibling::div//a")
|
55
|
-
dropdown.focus
|
56
|
-
sleep 0.5
|
57
|
-
dropdown.click
|
58
|
-
browser.link(xpath: ".//div[contains(@class, 'select-options')]//a[contains(.,'#{value}')]").click
|
59
|
-
end
|
60
|
-
|
61
|
-
def set_reference_field(label, value)
|
62
|
-
ref_label = label.gsub('ID', 'Name')
|
63
|
-
search_val = value[0..12]
|
64
|
-
search_field = browser.text_field(xpath: "//label[contains(.,'#{ref_label}')]//following-sibling::div[1]//input")
|
65
|
-
search_field.set search_val
|
66
|
-
sleep 1.5
|
67
|
-
search_field.send_keys :enter
|
68
|
-
browser.link(xpath: "//div[contains(@class,'searchScroller')]//a[contains(.,'#{search_val}')]").click
|
69
|
-
end
|
70
|
-
|
71
|
-
def submit_with(data)
|
72
|
-
populate_with data
|
73
|
-
save
|
74
|
-
end
|
75
|
-
|
76
|
-
# Save the current form, creating the new object
|
77
|
-
def save
|
78
|
-
browser.button(title: "Save").click
|
79
|
-
new_panel.wait_until do |panel|
|
80
|
-
browser_list = browser.ul(class: "errorsList")
|
81
|
-
if browser_list.exists?
|
82
|
-
errors = browser_list.lis
|
83
|
-
raise "Errors creating: #{errors.collect(&:text)}" if errors.count.positive?
|
84
|
-
end
|
85
|
-
|
86
|
-
!panel.present?
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
# Base class for creating entities
|
3
|
+
# Base class for creating entities in Salesforce
|
92
4
|
class CreatePage
|
93
5
|
extend BasePage
|
94
|
-
extend
|
6
|
+
extend LeapSalesforce::FormFiller
|
95
7
|
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LeapSalesforce
|
4
|
+
module FormFiller
|
5
|
+
# @return [Watir::Elements::Div] Div where form entered to create/edit entity
|
6
|
+
def form
|
7
|
+
browser.div(class: "inlinePanel")
|
8
|
+
end
|
9
|
+
|
10
|
+
# Use metadata to get label for field
|
11
|
+
# @param [String] field
|
12
|
+
def get_label_for(field)
|
13
|
+
raise "SoqlObject not defined" unless @soql_object
|
14
|
+
|
15
|
+
return field if @soql_object.label_names.include? field
|
16
|
+
|
17
|
+
raise "Cannot find field #{field} on #{@soql_object}" unless @soql_object.accessors[field.to_sym]
|
18
|
+
|
19
|
+
@soql_object.accessors[field.to_sym][:label]
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Hash] Description of field
|
23
|
+
def field_for(desc)
|
24
|
+
ruby_desc = @soql_object.accessors[desc.to_sym]
|
25
|
+
return ruby_desc if ruby_desc
|
26
|
+
|
27
|
+
@soql_object.properties_for(desc)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Based on data type of field passed, fill in each field
|
31
|
+
# The type of field is attempted to be filled in based on the metadata
|
32
|
+
# @param [Hash] data Hash with label => value to set syntax.
|
33
|
+
def populate_with(data)
|
34
|
+
sleep 1 # Being too fast can cause issues with first value. TODO: Wait for a condition
|
35
|
+
data.each do |desc, value|
|
36
|
+
field = field_for(desc)
|
37
|
+
label_name = field[:label] || field["label"]
|
38
|
+
|
39
|
+
type = field[:type] || field["type"]
|
40
|
+
case type
|
41
|
+
when "string" then set_text_field(label_name, value)
|
42
|
+
when "picklist" then set_picklist(label_name, value)
|
43
|
+
when "textarea" then set_text_area(label_name, value)
|
44
|
+
when "reference" then set_reference_field(label_name, value)
|
45
|
+
else
|
46
|
+
raise NotImplementedError, "#{type} not yet defined in #{self}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
# Set input text field
|
53
|
+
# @param [String] label Label of textfield
|
54
|
+
# @param [String] value Value to set textfield to
|
55
|
+
def set_text_area(label, value)
|
56
|
+
LeapSalesforce.logger.info "Setting text area, label '#{label}' with '#{value}'"
|
57
|
+
field_transaction label, value do
|
58
|
+
form.textarea(xpath: "//label[contains(.,'#{label}')]//following-sibling::textarea").set value
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Set input text field
|
63
|
+
# @param [String] label Label of text field
|
64
|
+
# @param [String] value Value to set text field to
|
65
|
+
def set_text_field(label, value)
|
66
|
+
value = value.to_s
|
67
|
+
LeapSalesforce.logger.info "Setting text field, label '#{label}' with '#{value}'"
|
68
|
+
field_transaction label, value do
|
69
|
+
label_element = form.label(xpath: "//label[contains(.,'#{label}')]")
|
70
|
+
text_field = label_element.text_field(xpath: ".//following-sibling::input|.//following-sibling::div/input")
|
71
|
+
text_field.focus
|
72
|
+
text_field.set! value
|
73
|
+
return self if text_field.value == value
|
74
|
+
|
75
|
+
sleep 2 # Wait a bit and then set field
|
76
|
+
text_field.set! value
|
77
|
+
unless text_field.value == value
|
78
|
+
raise SetFieldError, "Unable to set text field '#{label}' with value " \
|
79
|
+
"'#{value}'. Value set is '#{text_field.value}'"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
# Set value of picklist
|
86
|
+
# @param [String] label Label of picklist
|
87
|
+
# @param [String] value Value to set picklist to
|
88
|
+
def set_picklist(label, value)
|
89
|
+
LeapSalesforce.logger.info "Setting picklist, label '#{label}' with '#{value}'"
|
90
|
+
field_transaction label, value do
|
91
|
+
dropdown = form.link(xpath: "//*[./*[text()='#{label}']]//following-sibling::div//a")
|
92
|
+
dropdown.focus
|
93
|
+
sleep 0.5
|
94
|
+
dropdown.click
|
95
|
+
browser.link(xpath: ".//div[contains(@class, 'select-options')]//a[contains(.,'#{value}')]").click
|
96
|
+
end
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
# Set reference field where another entity is referred to by this entity
|
101
|
+
def set_reference_field(label, value)
|
102
|
+
ref_label = label.gsub("ID", "Name")
|
103
|
+
search_val = value[0..12]
|
104
|
+
LeapSalesforce.logger.info "Setting reference field, label '#{ref_label}', searching with '#{search_val}'"
|
105
|
+
field_transaction label, value do
|
106
|
+
search_field = browser.text_field(xpath: "//label[contains(.,'#{ref_label}')]//following-sibling::div[1]//input")
|
107
|
+
search_field.set search_val
|
108
|
+
browser.div(xpath: "//div[contains(@data-aura-class,'forceSearchInputLookupDesktopActionItem') and contains(., 'Search')]").click
|
109
|
+
browser.link(xpath: "//div[contains(@class,'searchScroller')]//a[contains(.,'#{search_val}')]").click
|
110
|
+
end
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
def submit_with(data)
|
115
|
+
populate_with data
|
116
|
+
save
|
117
|
+
end
|
118
|
+
|
119
|
+
# @return [Watir::Elements::Ul] List of errors
|
120
|
+
def error_list
|
121
|
+
browser.ul(xpath: "//ul[contains(@class, 'errorsList') and not(normalize-space()='')]")
|
122
|
+
end
|
123
|
+
|
124
|
+
# Save the current form, creating the new object
|
125
|
+
def save
|
126
|
+
LeapSalesforce.logger.info "Saving form for #{self}"
|
127
|
+
form.button(xpath: "//button[@title='Save']|//button[text()='Save']").click
|
128
|
+
form.wait_until do |panel|
|
129
|
+
sleep 1.5
|
130
|
+
if error_list.exists?
|
131
|
+
errors = error_list.lis
|
132
|
+
error_text = errors.collect(&:text)
|
133
|
+
LeapSalesforce.logger.error "Error submitting #{self} #{error_list.text}"
|
134
|
+
raise LeapSalesforce::SubmitError, "Errors creating on #{self}: #{error_text}" if errors.count.positive?
|
135
|
+
end
|
136
|
+
|
137
|
+
!panel.present?
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
# Process of setting a field within this block. Exceptions will be caught
|
144
|
+
# and thrown with context specific information
|
145
|
+
def field_transaction(label, value)
|
146
|
+
yield
|
147
|
+
rescue Watir::Exception::Error => e
|
148
|
+
raise LeapSalesforce::SetFieldError, "Unable to set label '#{label}' to '#{value}'" \
|
149
|
+
" on #{self}. Due to #{e.inspect}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "leap_salesforce/generator/generator"
|
4
|
+
|
5
|
+
module LeapSalesforce
|
6
|
+
module Generators
|
7
|
+
# Used for appending content onto existing files (or adding if not already present)
|
8
|
+
module Appenders
|
9
|
+
include LeapSalesforce::Generator
|
10
|
+
|
11
|
+
# Create content in a file, adding to an existing file if present
|
12
|
+
def append(filename, template_path)
|
13
|
+
verb = "Appending to"
|
14
|
+
unless File.exist? filename
|
15
|
+
FileUtils.touch filename
|
16
|
+
verb = "Creating"
|
17
|
+
end
|
18
|
+
content = read_template template_path, binding, folder: __dir__
|
19
|
+
if File.read(filename).include?(content)
|
20
|
+
puts "File '#{filename}' already has expected content, skipping...".colorize :red
|
21
|
+
else
|
22
|
+
puts "\u2713 #{verb} #{filename}".colorize :green
|
23
|
+
open(filename, "a") { |f| f.puts content }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "leap_salesforce/generator/generator"
|
4
|
+
|
5
|
+
module LeapSalesforce
|
6
|
+
# Generators for creating code
|
7
|
+
module Generator
|
8
|
+
# Creates SoqlObjects and related modules
|
9
|
+
class PageObjects
|
10
|
+
include Generator
|
11
|
+
|
12
|
+
POM_FOLDER = File.join(LeapSalesforce.lib_folder, "page_objects").freeze
|
13
|
+
|
14
|
+
def create
|
15
|
+
LeapSalesforce.objects_to_verify.each { |entity| create_poms_for entity }
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param [LeapSalesforce::SoqlData] entity An object representing an object in Salesforce
|
19
|
+
def create_poms_for(entity)
|
20
|
+
@entity_name = entity
|
21
|
+
@soql_object = LeapSalesforce.soql_objects.find { |so| so.class_name == entity.to_s }
|
22
|
+
%w[create view update].each do |page_action|
|
23
|
+
content = read_template "#{page_action}_page.rb.erb", binding, folder: __dir__
|
24
|
+
file = File.join(POM_FOLDER, "#{@entity_name.to_s.snakecase}/#{page_action}_page.rb")
|
25
|
+
generate_file file, content, overwrite: false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
gem 'leap_salesforce_ui'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'leap_salesforce_ui/rake'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'leap_salesforce_ui'
|
2
|
+
RSpec.configure do |config|
|
3
|
+
config.before(:each) do |example|
|
4
|
+
# Default to first test user defined for each test
|
5
|
+
LeapSalesforce.ui_user = LeapSalesforce::Users.list.first
|
6
|
+
# Start browser with test name as metadata
|
7
|
+
LeapSalesforce.browser example.full_description
|
8
|
+
end
|
9
|
+
|
10
|
+
config.after(:each) do |example|
|
11
|
+
screenshot = "logs/#{tidy_description(example.full_description.to_s)}_failure.png"
|
12
|
+
LeapSalesforce.browser.screenshot.save screenshot if example.exception
|
13
|
+
LeapSalesforce.close_browser # Fresh browser for each test
|
14
|
+
end
|
15
|
+
|
16
|
+
# Tidy RSpec example description so it's suitable for a screenshot name
|
17
|
+
# @return [String] Description of test suitable for file name
|
18
|
+
def tidy_description(rspec_description)
|
19
|
+
rspec_description.delete("#<RSpec::Core::Example").delete('>".:{}').delete("=").delete(" ")
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
RSpec.describe "Creating record" do
|
3
|
+
let(:first_name) { Faker::Name.first_name }
|
4
|
+
let(:last_name) { Faker::Name.last_name }
|
5
|
+
let(:salutation) { Contact::Salutation.sample }
|
6
|
+
let(:other_street) { Faker::Address.full_address }
|
7
|
+
it "populate form and save" do
|
8
|
+
account = FactoryBot.create(:account)
|
9
|
+
CreateContactPage.visit.submit_with({ "First Name" => first_name,
|
10
|
+
last_name: last_name,
|
11
|
+
salutation: salutation,
|
12
|
+
other_street: other_street,
|
13
|
+
account_id: account.account_name })
|
14
|
+
contact_id = ViewContactPage.id
|
15
|
+
@contact = Contact.find(id: contact_id)
|
16
|
+
expect(@contact.first_name).to eq first_name
|
17
|
+
expect(@contact.last_name).to eq last_name
|
18
|
+
expect(@contact.salutation).to eq salutation
|
19
|
+
expect(@contact.other_street).to eq other_street
|
20
|
+
expect(@contact.account_id).to eq account.id
|
21
|
+
end
|
22
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
class LoginPage
|
4
4
|
class << self
|
5
|
-
DISABLE_2STEP_URL =
|
5
|
+
DISABLE_2STEP_URL = "https://gitlab.com/leap-dojo/leap_salesforce_ui/-/wikis/Disable-2-step-authentication-and-allow-IP-range"
|
6
6
|
|
7
7
|
# @return [Watir::Browser]
|
8
8
|
def browser
|
@@ -21,18 +21,17 @@ class LoginPage
|
|
21
21
|
error_message_element.present?
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
# Could be used if a user wants to login through UI for login specific tests
|
25
|
+
# which should not be necessary
|
26
|
+
def login_manually
|
25
27
|
browser.goto LeapSalesforce.general_url
|
26
|
-
end
|
27
|
-
|
28
|
-
def login
|
29
28
|
browser.text_field(id: "username").set(LeapSalesforce.ui_user)
|
30
29
|
browser.text_field(id: "password").set(LeapSalesforce.password)
|
31
30
|
browser.button(id: "Login").click
|
32
31
|
Watir::Wait.until(timeout: 60, message: "Did not login within the expected time") do
|
33
32
|
raise "Cannot Login. #{error_message}" if error_message?
|
34
33
|
|
35
|
-
if browser.url.include?
|
34
|
+
if browser.url.include? "_ui/identity/verification/"
|
36
35
|
raise LeapSalesforce::SetupError, "2 step verification page appears.
|
37
36
|
Go to #{DISABLE_2STEP_URL} to learn how to disable it"
|
38
37
|
end
|
@@ -40,5 +39,15 @@ Go to #{DISABLE_2STEP_URL} to learn how to disable it"
|
|
40
39
|
!browser.url.include? LeapSalesforce.general_url
|
41
40
|
end
|
42
41
|
end
|
42
|
+
|
43
|
+
def login
|
44
|
+
LeapSalesforce.logger.info "Logging in as user '#{LeapSalesforce.ui_user}'"
|
45
|
+
browser.goto "#{LeapSalesforce.general_url}/?un=#{LeapSalesforce.ui_user}&pw=#{LeapSalesforce.password}"
|
46
|
+
continue_button = browser.button(id: "thePage:inputForm:continue")
|
47
|
+
if continue_button.exists?
|
48
|
+
browser.checkbox(id: "thePage:inputForm:remember").set
|
49
|
+
continue_button.click
|
50
|
+
end
|
51
|
+
end
|
43
52
|
end
|
44
53
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Base class for updating an entity in Salesforce
|
4
|
+
class UpdatePage
|
5
|
+
extend BasePage
|
6
|
+
extend LeapSalesforce::PageIntrospection
|
7
|
+
extend LeapSalesforce::FormFiller
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def visit(id)
|
11
|
+
LoginPage.login
|
12
|
+
browser.goto "#{SoqlHandler.instance_url}/lightning/r/#{@soql_object.soql_object_name}/#{id}/edit"
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,11 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Base class for viewing an entity in Salesforce
|
3
4
|
class ViewPage
|
4
5
|
extend BasePage
|
6
|
+
extend LeapSalesforce::PageIntrospection
|
5
7
|
class << self
|
6
|
-
|
7
|
-
|
8
|
-
|
8
|
+
def visit(id)
|
9
|
+
LoginPage.login
|
10
|
+
browser.goto "#{SoqlHandler.instance_url}lightning/r/#{@soql_object.soql_object_name}/#{id}/view"
|
11
|
+
self
|
9
12
|
end
|
10
13
|
end
|
11
14
|
end
|
data/run_tests_when_ready.sh
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env bash
|
2
2
|
# exit if a command returns a non-zero exit code and also print the commands and their args as they are executed
|
3
3
|
set -e -x
|
4
|
+
bin/setup # Setup POMs fresh each time testing generator
|
4
5
|
# Wait for Zalenium to be up and running
|
5
6
|
timeout 30 bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' zalenium:4444/wd/hub/status)" != "200" ]]; do sleep 5; done' || false
|
6
|
-
bundle install
|
7
7
|
bundle exec rake spec
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: leap_salesforce_ui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- IQA
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-
|
12
|
+
date: 2021-02-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: leap_salesforce
|
@@ -17,28 +17,28 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 1.0.
|
20
|
+
version: 1.0.3
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: 1.0.
|
27
|
+
version: 1.0.3
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: watir
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - ">="
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version:
|
34
|
+
version: 6.17.0
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version:
|
41
|
+
version: 6.17.0
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: webdrivers
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -87,7 +87,22 @@ files:
|
|
87
87
|
- lib/leap_salesforce_ui.rb
|
88
88
|
- lib/leap_salesforce_ui/base_page.rb
|
89
89
|
- lib/leap_salesforce_ui/create_page.rb
|
90
|
+
- lib/leap_salesforce_ui/error.rb
|
91
|
+
- lib/leap_salesforce_ui/form_filler.rb
|
92
|
+
- lib/leap_salesforce_ui/generator/appenders.rb
|
93
|
+
- lib/leap_salesforce_ui/generator/page_objects.rb
|
94
|
+
- lib/leap_salesforce_ui/generator/templates/Gemfile.erb
|
95
|
+
- lib/leap_salesforce_ui/generator/templates/Rakefile.erb
|
96
|
+
- lib/leap_salesforce_ui/generator/templates/create_page.rb.erb
|
97
|
+
- lib/leap_salesforce_ui/generator/templates/spec_helper.rb.erb
|
98
|
+
- lib/leap_salesforce_ui/generator/templates/ui_spec.rb.erb
|
99
|
+
- lib/leap_salesforce_ui/generator/templates/update_page.rb.erb
|
100
|
+
- lib/leap_salesforce_ui/generator/templates/view_page.rb.erb
|
90
101
|
- lib/leap_salesforce_ui/login_page.rb
|
102
|
+
- lib/leap_salesforce_ui/page_introspection.rb
|
103
|
+
- lib/leap_salesforce_ui/rake.rb
|
104
|
+
- lib/leap_salesforce_ui/rake/pom.rake
|
105
|
+
- lib/leap_salesforce_ui/update_page.rb
|
91
106
|
- lib/leap_salesforce_ui/version.rb
|
92
107
|
- lib/leap_salesforce_ui/view_page.rb
|
93
108
|
- run_tests_when_ready.sh
|