leap_salesforce_ui 0.1.3 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8dac68f1cd35a89cc7051367715789f511f2bbb3bdf4cd992b62d6b4d775d8db
4
- data.tar.gz: 9b50e04ed020695d9a94ffa43f84182aa336645cc2ff4e6fb1244a5a7384b2fd
3
+ metadata.gz: 10d36f660f1fd79ec05c6dcf5a0571b8b9394154ee59bece411722672045d9cc
4
+ data.tar.gz: e4bb12986774537a04ec55759e3005da3e54b1825acd62ec4e0251e8fdbabd4c
5
5
  SHA512:
6
- metadata.gz: a31aa4579890235a69dee8192f17f7bfc74867ba23f0d0b9f10e69f41ab0c64973d1a280e7646f72f77c7be3f836746089cb28456e91342d2f4d27e7a4efc3eb
7
- data.tar.gz: 885dcb0b1df4df2b4efd76bc98bc2bf7370ef0a137d83d141d2f3bd757007b0474c027245fc88f679c6da666cde2f0323179a0be50937fd4a22708bb4eaa0396
6
+ metadata.gz: 810cd99d1b6d9ff927cba94c9c680267c5005801210b5e9287229a897aa305befdb506c05cec90f55fb8dc2ae1949b47a5f33cb2855c0d5b417e5c20b2686f1f
7
+ data.tar.gz: cab59fba240bdad1a26b86166339ed1caa4e46cc6bacca7c7e2502b387c6343e3d8ba710c523c4cd25c6243fb085fd33788760ccb1e1c826d2c2cdb61fddd603
data/.leap_salesforce.yml CHANGED
@@ -4,3 +4,4 @@ sfdx: false
4
4
  soql_objects:
5
5
  - Contact
6
6
  - Account
7
+ - Broker: Broker__c
data/ChangeLog CHANGED
@@ -1,3 +1,19 @@
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
+
1
17
  Version 0.1.3
2
18
  * Enhancements
3
19
  * Explicitly specify higher version of Watir as minimum which is compatible with Ruby 3
data/README.md CHANGED
@@ -40,9 +40,41 @@ LeapSalesforce.ui_user = 'test.user@salesforce.com'
40
40
  See [leap salesforce test users section](https://gitlab.com/leap-dojo/leap_salesforce#test-users)
41
41
  for details on ways to set test users.
42
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
+
43
72
  ### Populate form using label in metadata
44
73
 
45
- Following is a walk through of one of the tests
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
46
78
 
47
79
  ```ruby
48
80
  RSpec.describe "Creating record" do
@@ -93,6 +125,14 @@ Follow other examples in `spec` to build tests using the auto-generated classes.
93
125
  unless run from an IP address that your Salesforce Org trusts. Many shared CI environments have runners that would
94
126
  be hard to whitelist as the address is dynamic.
95
127
 
128
+ ### Options
129
+
130
+ To run tests in headless mode, set headless mode to true.
131
+ ```ruby
132
+ LeapSalesforce.headless = true
133
+ ```
134
+
135
+
96
136
  ## Development
97
137
 
98
138
  After checking out the repo, run `bin/setup` to install dependencies.
@@ -1,4 +1,47 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "leap_salesforce_ui"
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)
@@ -4,6 +4,7 @@ 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"
8
9
  require_relative "leap_salesforce_ui/page_introspection"
9
10
  require_relative "leap_salesforce_ui/form_filler"
@@ -32,6 +33,8 @@ end
32
33
  module LeapSalesforce
33
34
  # @return [String] User for UI
34
35
  @ui_user = nil
36
+ # @return [Boolean] Whether to run in headless mode
37
+ @headless = false
35
38
  class << self
36
39
  def browser(test_name = nil)
37
40
  @browser ||= new_browser(test_name)
@@ -42,8 +45,12 @@ module LeapSalesforce
42
45
  @browser = nil
43
46
  end
44
47
 
48
+ # @return [String] User used for UI tests
45
49
  attr_reader :ui_user
46
50
 
51
+ # @return [Boolean] Whether to run in headless mode. Default false
52
+ attr_accessor :headless
53
+
47
54
  # @param [String, Symbol, Regexp, LeapSalesforce::User] user User or email address of user
48
55
  def ui_user=(user)
49
56
  @ui_user = if user.is_a? String
@@ -61,7 +68,9 @@ module LeapSalesforce
61
68
  # TODO: Get working on Ruby 3
62
69
  Watir::Browser.new :chrome, { **zalenium_args("LeapSalesforce", test_name || "Test") }
63
70
  else
64
- Watir::Browser.new
71
+ args = {}
72
+ args[:headless] = true if headless
73
+ Watir::Browser.new :chrome, args
65
74
  end
66
75
  end
67
76
  end
@@ -8,13 +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
19
  LoginPage.login
17
- 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}"
18
22
  browser.goto page_url
19
23
  self
20
24
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LeapSalesforce
4
+ # Error related to submitting a form
5
+ class SubmitError < Error; end
6
+
7
+ # Error related to setting a field
8
+ class SetFieldError < Error; end
9
+ end
@@ -2,7 +2,8 @@
2
2
 
3
3
  module LeapSalesforce
4
4
  module FormFiller
5
- def new_panel
5
+ # @return [Watir::Elements::Div] Div where form entered to create/edit entity
6
+ def form
6
7
  browser.div(class: "inlinePanel")
7
8
  end
8
9
 
@@ -27,7 +28,8 @@ module LeapSalesforce
27
28
  end
28
29
 
29
30
  # Based on data type of field passed, fill in each field
30
- # @param [Hash] data
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.
31
33
  def populate_with(data)
32
34
  sleep 1 # Being too fast can cause issues with first value. TODO: Wait for a condition
33
35
  data.each do |desc, value|
@@ -36,28 +38,48 @@ module LeapSalesforce
36
38
 
37
39
  type = field[:type] || field["type"]
38
40
  case type
39
- when "string"
40
- set_text_field(label_name, value)
41
+ when "string" then set_text_field(label_name, value)
41
42
  when "picklist" then set_picklist(label_name, value)
42
- when "textarea"
43
- new_panel.textarea(xpath: "//label[contains(.,'#{label_name}')]//following-sibling::textarea").set value
44
- when "reference"
45
- set_reference_field(label_name, value)
43
+ when "textarea" then set_text_area(label_name, value)
44
+ when "reference" then set_reference_field(label_name, value)
46
45
  else
47
- raise NotImplementedError, "#{type} not yet defined in #{self.class}"
46
+ raise NotImplementedError, "#{type} not yet defined in #{self}"
48
47
  end
49
48
  end
50
49
  self
51
50
  end
52
51
 
53
- def set_text_field(label, value)
54
- text_field = new_panel.text_field(xpath: "//label[contains(.,'#{label}')]//following-sibling::input")
55
- text_field.focus
56
- text_field.set! value
57
- return self if text_field.value == value
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
+ text_area = form.textarea(xpath: "//lightning-textarea[./label[contains(.,'#{label}')]]//following-sibling::div/textarea")
59
+ text_area.set value
60
+ end
61
+ end
58
62
 
59
- sleep 2 # Wait a bit and then set field
60
- text_field.set! value
63
+ # Set input text field
64
+ # @param [String] label Label of text field
65
+ # @param [String] value Value to set text field to
66
+ def set_text_field(label, value)
67
+ value = value.to_s
68
+ LeapSalesforce.logger.info "Setting text field, label '#{label}' with '#{value}'"
69
+ field_transaction label, value do
70
+ label_element = form.label(xpath: "//label[contains(.,'#{label}')]")
71
+ text_field = label_element.text_field(xpath: ".//following-sibling::input|.//following-sibling::div/input")
72
+ text_field.focus
73
+ text_field.set! value
74
+ return self if text_field.value == value
75
+
76
+ sleep 2 # Wait a bit and then set field
77
+ text_field.set! value
78
+ unless text_field.value == value
79
+ raise SetFieldError, "Unable to set text field '#{label}' with value " \
80
+ "'#{value}'. Value set is '#{text_field.value}'"
81
+ end
82
+ end
61
83
  self
62
84
  end
63
85
 
@@ -65,21 +87,32 @@ module LeapSalesforce
65
87
  # @param [String] label Label of picklist
66
88
  # @param [String] value Value to set picklist to
67
89
  def set_picklist(label, value)
68
- dropdown = new_panel.link(xpath: "//*[./*[text()='#{label}']]//following-sibling::div//a")
69
- dropdown.focus
70
- sleep 0.5
71
- dropdown.click
72
- browser.link(xpath: ".//div[contains(@class, 'select-options')]//a[contains(.,'#{value}')]").click
90
+ LeapSalesforce.logger.info "Setting picklist, label '#{label}' with '#{value}'"
91
+ field_transaction label, value do
92
+ dropdown = form.text_field(xpath: "//*[./*[text()='#{label}']]//following-sibling::div//input")
93
+ dropdown_id = dropdown.attribute "id"
94
+ LeapSalesforce.logger.debug "Using dropdown id: #{dropdown_id}"
95
+ dropdown.focus
96
+ sleep 0.5
97
+ dropdown.click
98
+ has_value = "@data-value='#{value}'"
99
+ in_dropdown = "starts-with(@data-item-id, '#{dropdown_id}')"
100
+ browser.element(xpath: ".//lightning-base-combobox-item[#{has_value} and #{in_dropdown}]").click
101
+ end
73
102
  self
74
103
  end
75
104
 
105
+ # Set reference field where another entity is referred to by this entity
76
106
  def set_reference_field(label, value)
77
107
  ref_label = label.gsub("ID", "Name")
78
108
  search_val = value[0..12]
79
- search_field = browser.text_field(xpath: "//label[contains(.,'#{ref_label}')]//following-sibling::div[1]//input")
80
- search_field.set search_val
81
- browser.div(xpath: "//div[contains(@data-aura-class,'forceSearchInputLookupDesktopActionItem') and contains(., 'Search')]").click
82
- browser.link(xpath: "//div[contains(@class,'searchScroller')]//a[contains(.,'#{search_val}')]").click
109
+ LeapSalesforce.logger.info "Setting reference field, label '#{ref_label}', searching with '#{search_val}'"
110
+ field_transaction label, value do
111
+ search_field = browser.text_field(xpath: "//label[contains(.,'#{ref_label}')]//following-sibling::div[1]//input")
112
+ search_field.set search_val
113
+ browser.element(xpath: ".//lightning-base-combobox-item[starts-with(.,'SearchShow')]").click
114
+ browser.link(xpath: "//div[contains(@class,'searchScroller')]//a[contains(.,'#{search_val}')]").click
115
+ end
83
116
  self
84
117
  end
85
118
 
@@ -88,20 +121,37 @@ module LeapSalesforce
88
121
  save
89
122
  end
90
123
 
124
+ # @return [Watir::Elements::Ul] List of errors
125
+ def error_list
126
+ browser.ul(xpath: "//ul[contains(@class, 'errorsList') and not(normalize-space()='')]")
127
+ end
128
+
91
129
  # Save the current form, creating the new object
92
130
  def save
93
- browser.button(title: "Save").click
94
- new_panel.wait_until do |panel|
95
- browser_list = browser.ul(class: "errorsList")
96
- if browser_list.exists?
97
- sleep 3
98
- errors = browser_list.lis
99
- sleep 1 if errors.collect(&:text) != [""] # Wait for error to come
100
- raise "Errors creating: #{errors.collect(&:text)}" if errors.count.positive?
131
+ LeapSalesforce.logger.info "Saving form for #{self}"
132
+ form.button(xpath: "//button[@title='Save']|//button[text()='Save']").click
133
+ form.wait_until do |panel|
134
+ sleep 1.5
135
+ if error_list.exists?
136
+ errors = error_list.lis
137
+ error_text = errors.collect(&:text)
138
+ LeapSalesforce.logger.error "Error submitting #{self} #{error_list.text}"
139
+ raise LeapSalesforce::SubmitError, "Errors creating on #{self}: #{error_text}" if errors.count.positive?
101
140
  end
102
141
 
103
142
  !panel.present?
104
143
  end
105
144
  end
145
+
146
+ private
147
+
148
+ # Process of setting a field within this block. Exceptions will be caught
149
+ # and thrown with context specific information
150
+ def field_transaction(label, value)
151
+ yield
152
+ rescue Watir::Exception::Error => e
153
+ raise LeapSalesforce::SetFieldError, "Unable to set label '#{label}' to '#{value}'" \
154
+ " on #{self}. Due to #{e.inspect}"
155
+ end
106
156
  end
107
157
  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 @@
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
@@ -41,6 +41,7 @@ Go to #{DISABLE_2STEP_URL} to learn how to disable it"
41
41
  end
42
42
 
43
43
  def login
44
+ LeapSalesforce.logger.info "Logging in as user '#{LeapSalesforce.ui_user}'"
44
45
  browser.goto "#{LeapSalesforce.general_url}/?un=#{LeapSalesforce.ui_user}&pw=#{LeapSalesforce.password}"
45
46
  continue_button = browser.button(id: "thePage:inputForm:continue")
46
47
  if continue_button.exists?
@@ -9,7 +9,7 @@ class UpdatePage
9
9
  class << self
10
10
  def visit(id)
11
11
  LoginPage.login
12
- browser.goto "#{SoqlHandler.instance_url}/lightning/r/#{@soql_object}/#{id}/edit"
12
+ browser.goto "#{SoqlHandler.instance_url}/lightning/r/#{@soql_object.soql_object_name}/#{id}/edit"
13
13
  self
14
14
  end
15
15
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LeapSalesforceUi
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.7"
5
5
  end
@@ -7,7 +7,7 @@ class ViewPage
7
7
  class << self
8
8
  def visit(id)
9
9
  LoginPage.login
10
- browser.goto "#{SoqlHandler.instance_url}lightning/r/#{@soql_object}/#{id}/view"
10
+ browser.goto "#{SoqlHandler.instance_url}lightning/r/#{@soql_object.soql_object_name}/#{id}/view"
11
11
  self
12
12
  end
13
13
  end
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.3
4
+ version: 0.1.7
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-02-09 00:00:00.000000000 Z
12
+ date: 2021-08-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: leap_salesforce
@@ -87,9 +87,15 @@ 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
90
91
  - lib/leap_salesforce_ui/form_filler.rb
92
+ - lib/leap_salesforce_ui/generator/appenders.rb
91
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
92
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
93
99
  - lib/leap_salesforce_ui/generator/templates/update_page.rb.erb
94
100
  - lib/leap_salesforce_ui/generator/templates/view_page.rb.erb
95
101
  - lib/leap_salesforce_ui/login_page.rb