diff --git a/Gemfile b/Gemfile
index c5aa43639..7fd34a003 100644
--- a/Gemfile
+++ b/Gemfile
@@ -100,6 +100,9 @@ gem 'browser'
gem 'icalendar'
gem 'icalendar-recurrence'
+# feature - phone number formatting
+gem 'telephone_number'
+
# integrations
gem 'clearbit'
gem 'net-ldap'
diff --git a/Gemfile.lock b/Gemfile.lock
index 79d661a52..a25f5978c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -413,6 +413,7 @@ GEM
sqlite3 (1.3.13)
telegramAPI (1.4.2)
rest-client (~> 2.0, >= 2.0.2)
+ telephone_number (1.3.0)
term-ansicolor (1.6.0)
tins (~> 1.0)
test-unit (3.2.6)
@@ -542,6 +543,7 @@ DEPENDENCIES
sprockets
sqlite3
telegramAPI
+ telephone_number
test-unit
therubyracer
twitter
@@ -557,4 +559,4 @@ RUBY VERSION
ruby 2.4.4p296
BUNDLED WITH
- 1.16.1
+ 1.16.2
diff --git a/app/assets/javascripts/app/views/cti/caller_log.jst.eco b/app/assets/javascripts/app/views/cti/caller_log.jst.eco
index b5faa1a7f..d00c71e4a 100644
--- a/app/assets/javascripts/app/views/cti/caller_log.jst.eco
+++ b/app/assets/javascripts/app/views/cti/caller_log.jst.eco
@@ -40,9 +40,9 @@
<% end %>
<% if shown: %>
- <%= item.from %>
+ <%= item.from_pretty %>
<% else: %>
- <%= item.from %>
+ <%= item.from_pretty %>
<% end %>
@@ -66,9 +66,9 @@
<% end %>
<% if shown: %>
- <%= item.to %>
+ <%= item.to_pretty %>
<% else: %>
- <%= item.to %>
+ <%= item.to_pretty %>
<% end %>
|
diff --git a/app/models/cti/log.rb b/app/models/cti/log.rb
index af35c5cc7..edb99b8d4 100644
--- a/app/models/cti/log.rb
+++ b/app/models/cti/log.rb
@@ -246,19 +246,12 @@ returns
list = Cti::Log.order('created_at DESC, id DESC').limit(60)
# add assets
- assets = {}
- list.each do |item|
- next if !item.preferences
- %w[from to].each do |direction|
- next if !item.preferences[direction]
- item.preferences[direction].each do |caller_id|
- next if !caller_id['user_id']
- user = User.lookup(id: caller_id['user_id'])
- next if !user
- assets = user.assets(assets)
- end
- end
- end
+ assets = list.map(&:preferences)
+ .map { |p| p.slice(:from, :to) }
+ .map(&:values).flatten
+ .map { |caller_id| caller_id[:user_id] }.compact
+ .map { |user_id| User.lookup(id: user_id) }.compact
+ .each.with_object({}) { |user, a| user.assets(a) }
{
list: list,
@@ -392,5 +385,25 @@ optional you can put the max oldest chat entries as argument
true
end
+ # adds virtual attributes when rendering #to_json
+ # see http://api.rubyonrails.org/classes/ActiveModel/Serialization.html
+ def attributes
+ virtual_attributes = {
+ 'from_pretty' => from_pretty,
+ 'to_pretty' => to_pretty,
+ }
+
+ super.merge(virtual_attributes)
+ end
+
+ def from_pretty
+ parsed = TelephoneNumber.parse(from&.sub(/^\+?/, '+'))
+ parsed.send(parsed.valid? ? :international_number : :original_number)
+ end
+
+ def to_pretty
+ parsed = TelephoneNumber.parse(to&.sub(/^\+?/, '+'))
+ parsed.send(parsed.valid? ? :international_number : :original_number)
+ end
end
end
diff --git a/script/build/test_slice_tests.sh b/script/build/test_slice_tests.sh
index 4a30d2fdf..f705f0c0f 100755
--- a/script/build/test_slice_tests.sh
+++ b/script/build/test_slice_tests.sh
@@ -65,8 +65,8 @@ if [ "$LEVEL" == '1' ]; then
# test/browser/maintenance_session_message_test.rb
# test/browser/manage_test.rb
# test/browser/monitoring_test.rb
- rm test/browser/integration_sipgate_notify_not_clearing_on_leftside_test.rb
- rm test/browser/integration_cti_notify_not_clearing_on_leftside_test.rb
+ rm test/browser/integration_sipgate_test.rb
+ rm test/browser/integration_cti_test.rb
rm test/browser/preferences_language_test.rb
rm test/browser/preferences_permission_check_test.rb
rm test/browser/preferences_token_access_test.rb
@@ -139,8 +139,8 @@ elif [ "$LEVEL" == '2' ]; then
rm test/browser/maintenance_session_message_test.rb
rm test/browser/manage_test.rb
rm test/browser/monitoring_test.rb
- rm test/browser/integration_sipgate_notify_not_clearing_on_leftside_test.rb
- rm test/browser/integration_cti_notify_not_clearing_on_leftside_test.rb
+ rm test/browser/integration_sipgate_test.rb
+ rm test/browser/integration_cti_test.rb
rm test/browser/preferences_language_test.rb
rm test/browser/preferences_permission_check_test.rb
rm test/browser/preferences_token_access_test.rb
@@ -213,8 +213,8 @@ elif [ "$LEVEL" == '3' ]; then
rm test/browser/maintenance_session_message_test.rb
rm test/browser/manage_test.rb
rm test/browser/monitoring_test.rb
- rm test/browser/integration_sipgate_notify_not_clearing_on_leftside_test.rb
- rm test/browser/integration_cti_notify_not_clearing_on_leftside_test.rb
+ rm test/browser/integration_sipgate_test.rb
+ rm test/browser/integration_cti_test.rb
rm test/browser/preferences_language_test.rb
rm test/browser/preferences_permission_check_test.rb
rm test/browser/preferences_token_access_test.rb
@@ -287,8 +287,8 @@ elif [ "$LEVEL" == '4' ]; then
rm test/browser/maintenance_session_message_test.rb
rm test/browser/manage_test.rb
rm test/browser/monitoring_test.rb
- rm test/browser/integration_sipgate_notify_not_clearing_on_leftside_test.rb
- rm test/browser/integration_cti_notify_not_clearing_on_leftside_test.rb
+ rm test/browser/integration_sipgate_test.rb
+ rm test/browser/integration_cti_test.rb
rm test/browser/preferences_language_test.rb
rm test/browser/preferences_permission_check_test.rb
rm test/browser/preferences_token_access_test.rb
@@ -360,8 +360,8 @@ elif [ "$LEVEL" == '5' ]; then
rm test/browser/maintenance_session_message_test.rb
rm test/browser/manage_test.rb
rm test/browser/monitoring_test.rb
- rm test/browser/integration_sipgate_notify_not_clearing_on_leftside_test.rb
- rm test/browser/integration_cti_notify_not_clearing_on_leftside_test.rb
+ rm test/browser/integration_sipgate_test.rb
+ rm test/browser/integration_cti_test.rb
rm test/browser/preferences_language_test.rb
rm test/browser/preferences_permission_check_test.rb
rm test/browser/preferences_token_access_test.rb
@@ -436,8 +436,8 @@ elif [ "$LEVEL" == '6' ]; then
rm test/browser/maintenance_session_message_test.rb
rm test/browser/manage_test.rb
rm test/browser/monitoring_test.rb
- # rm test/browser/integration_sipgate_notify_not_clearing_on_leftside_test.rb
- # rm test/browser/integration_cti_notify_not_clearing_on_leftside_test.rb
+ # rm test/browser/integration_sipgate_test.rb
+ # rm test/browser/integration_cti_test.rb
# test/browser/preferences_language_test.rb
# test/browser/preferences_permission_check_test.rb
# test/browser/preferences_token_access_test.rb
diff --git a/spec/factories/cti/log.rb b/spec/factories/cti/log.rb
new file mode 100644
index 000000000..c2f3b0cdd
--- /dev/null
+++ b/spec/factories/cti/log.rb
@@ -0,0 +1,9 @@
+FactoryBot.define do
+ factory :cti_log, class: 'cti/log' do
+ direction { %w[in out].sample }
+ state { %w[newCall answer hangup].sample }
+ from '4930609854180'
+ to '4930609811111'
+ call_id { (Cti::Log.pluck(:call_id).max || '0').next } # has SQL UNIQUE constraint
+ end
+end
diff --git a/spec/models/cti/log.rb b/spec/models/cti/log.rb
new file mode 100644
index 000000000..519b1b978
--- /dev/null
+++ b/spec/models/cti/log.rb
@@ -0,0 +1,46 @@
+require 'rails_helper'
+
+RSpec.describe Cti::Log do
+ subject { create(:cti_log, **factory_attributes) }
+ let(:factory_attributes) { {} }
+
+ context 'with complete, E164 international numbers' do
+ let(:factory_attributes) { { from: '4930609854180', to: '4930609811111' } }
+
+ describe '#from_pretty' do
+ it 'gives the number in prettified format' do
+ expect(subject.from_pretty).to eq('+49 30 609854180')
+ end
+ end
+
+ describe '#to_pretty' do
+ it 'gives the number in prettified format' do
+ expect(subject.to_pretty).to eq('+49 30 609811111')
+ end
+ end
+ end
+
+ context 'with private network numbers' do
+ let(:factory_attributes) { { from: '007', to: '008' } }
+
+ describe '#from_pretty' do
+ it 'gives the number unaltered' do
+ expect(subject.from_pretty).to eq('007')
+ end
+ end
+
+ describe '#to_pretty' do
+ it 'gives the number unaltered' do
+ expect(subject.to_pretty).to eq('008')
+ end
+ end
+ end
+
+ describe '#to_json' do
+ let(:virtual_attributes) { %w[from_pretty to_pretty] }
+
+ it 'includes virtual attributes' do
+ expect(subject.as_json).to include(*virtual_attributes)
+ end
+ end
+end
diff --git a/test/browser/integration_cti_notify_not_clearing_on_leftside_test.rb b/test/browser/integration_cti_notify_not_clearing_on_leftside_test.rb
deleted file mode 100644
index ce222186e..000000000
--- a/test/browser/integration_cti_notify_not_clearing_on_leftside_test.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-require 'browser_test_helper'
-
-# Regression test for #2017
-
-class IntegrationCtiNotifyNotClearingOnLeftsideTest < TestCase
- setup do
- if !ENV['CTI_TOKEN']
- raise "ERROR: Need CTI_TOKEN - hint CTI_TOKEN='some_token'"
- end
-
- end
-
- def test_notify_badge
- id = rand(99_999_999)
-
- @browser = browser_instance
- login(
- username: 'master@example.com',
- password: 'test',
- url: browser_url,
- )
-
- click(css: 'a[href="#manage"]')
- click(css: 'a[href="#system/integration"]')
- click(css: 'a[href="#system/integration/cti"]')
-
- switch(
- css: '.content.active .js-switch',
- type: 'on'
- )
-
- watch_for(
- css: 'a[href="#cti"]'
- )
-
- click(css: 'a[href="#cti"]')
-
- # simulate cti callbacks
-
- url = URI.join(browser_url, "api/v1/cti/#{ENV['CTI_TOKEN']}")
- params = { direction: 'in', from: '491715000002', to: '4930600000000', callId: "4991155921769858278-#{id}", cause: 'busy' }
- Net::HTTP.post_form(url, params.merge(event: 'newCall'))
- Net::HTTP.post_form(url, params.merge(event: 'hangup'))
-
- watch_for(
- css: '.js-phoneMenuItem .counter',
- value: '1'
- )
-
- click(css: '.content.active .table-checkbox label')
-
- watch_for_disappear(
- css: '.js-phoneMenuItem .counter'
- )
-
- click(css: 'a[href="#manage"]')
- click(css: 'a[href="#system/integration"]')
- click(css: 'a[href="#system/integration/cti"]')
-
- switch(
- css: '.content.active .js-switch',
- type: 'off'
- )
- end
-end
diff --git a/test/browser/integration_cti_test.rb b/test/browser/integration_cti_test.rb
new file mode 100644
index 000000000..71edf2a00
--- /dev/null
+++ b/test/browser/integration_cti_test.rb
@@ -0,0 +1,147 @@
+require 'browser_test_helper'
+
+class IntegrationCtiTest < TestCase
+ setup do
+ if !ENV['CTI_TOKEN']
+ raise "ERROR: Need CTI_TOKEN - hint CTI_TOKEN='some_token'"
+ end
+
+ end
+
+ # Regression test for #2017
+ def test_nav_menu_notification_badge_clears
+ id = rand(99_999_999)
+
+ @browser = browser_instance
+ login(
+ username: 'master@example.com',
+ password: 'test',
+ url: browser_url,
+ )
+
+ click(css: 'a[href="#manage"]')
+ click(css: 'a[href="#system/integration"]')
+ click(css: 'a[href="#system/integration/cti"]')
+
+ switch(
+ css: '.content.active .js-switch',
+ type: 'on'
+ )
+
+ watch_for(css: 'a[href="#cti"]')
+
+ click(css: 'a[href="#cti"]')
+
+ call_counter = @browser.find_elements(css: '.js-phoneMenuItem .counter')
+ .first&.text.to_i
+
+ # simulate cti callbacks
+ url = URI.join(browser_url, "api/v1/cti/#{ENV['CTI_TOKEN']}")
+ params = {
+ direction: 'in',
+ from: '491715000002',
+ to: '4930600000000',
+ callId: "4991155921769858278-#{id}",
+ cause: 'busy'
+ }
+ Net::HTTP.post_form(url, params.merge(event: 'newCall'))
+ Net::HTTP.post_form(url, params.merge(event: 'hangup'))
+
+ watch_for(
+ css: '.js-phoneMenuItem .counter',
+ value: (call_counter + 1).to_s
+ )
+
+ click(css: '.content.active .table-checkbox label', all: true)
+
+ watch_for_disappear(
+ css: '.js-phoneMenuItem .counter'
+ )
+
+ click(css: 'a[href="#manage"]')
+ click(css: 'a[href="#system/integration"]')
+ click(css: 'a[href="#system/integration/cti"]')
+
+ switch(
+ css: '.content.active .js-switch',
+ type: 'off'
+ )
+ end
+
+ # Regression test for #2018
+ def test_e164_numbers_displayed_in_prettified_format
+ id = rand(99_999_999)
+
+ @browser = browser_instance
+ login(
+ username: 'master@example.com',
+ password: 'test',
+ url: browser_url,
+ )
+
+ click(css: 'a[href="#manage"]')
+ click(css: 'a[href="#system/integration"]')
+ click(css: 'a[href="#system/integration/cti"]')
+
+ switch(
+ css: '.content.active .js-switch',
+ type: 'on'
+ )
+
+ watch_for(
+ css: 'a[href="#cti"]'
+ )
+
+ click(css: 'a[href="#cti"]')
+
+ # simulate cti callbacks...
+ url = URI.join(browser_url, "api/v1/cti/#{ENV['CTI_TOKEN']}")
+
+ # ...for private network number
+ params = {
+ direction: 'in',
+ from: '007',
+ to: '008',
+ callId: "4991155921769858278-#{id}",
+ cause: 'busy'
+ }
+ Net::HTTP.post_form(url, params.merge(event: 'newCall'))
+ Net::HTTP.post_form(url, params.merge(event: 'hangup'))
+
+ # ...for e164 number
+ params = {
+ direction: 'in',
+ from: '4930609854180',
+ to: '4930609811111',
+ callId: "4991155921769858278-#{id.next}",
+ cause: 'busy'
+ }
+ Net::HTTP.post_form(url, params.merge(event: 'newCall'))
+ Net::HTTP.post_form(url, params.merge(event: 'hangup'))
+
+ # view caller log
+ click(css: 'a[href="#cti"]')
+
+ # assertion: private network numbers appear verbatim
+ match(
+ css: '.js-callerLog',
+ value: '007',
+ )
+
+ match(
+ css: '.js-callerLog',
+ value: '008',
+ )
+
+ # assertion: E164 numbers appear prettified
+ match(
+ css: '.js-callerLog',
+ value: '+49 30 609854180',
+ )
+
+ match(
+ css: '.js-callerLog',
+ value: '+49 30 609811111',
+ )
+ end
+end
diff --git a/test/browser/integration_sipgate_notify_not_clearing_on_leftside_test.rb b/test/browser/integration_sipgate_test.rb
similarity index 91%
rename from test/browser/integration_sipgate_notify_not_clearing_on_leftside_test.rb
rename to test/browser/integration_sipgate_test.rb
index 7f8ee8cfc..e30794e2d 100644
--- a/test/browser/integration_sipgate_notify_not_clearing_on_leftside_test.rb
+++ b/test/browser/integration_sipgate_test.rb
@@ -1,9 +1,8 @@
require 'browser_test_helper'
-# Regression test for #2017
-
-class IntegrationSipgateNotifyNotClearingOnLeftsideTest < TestCase
- def test_notify_badge
+class IntegrationSipgateTest < TestCase
+ # Regression test for #2017
+ def test_nav_menu_notification_badge_clears
id = rand(99_999_999)
@browser = browser_instance
@@ -29,7 +28,6 @@ class IntegrationSipgateNotifyNotClearingOnLeftsideTest < TestCase
click(css: 'a[href="#cti"]')
# simulate sipgate callbacks
-
url = URI.join(browser_url, 'api/v1/sipgate/in')
params = { direction: 'in', from: '491715000002', to: '4930600000000', callId: "4991155921769858278-#{id}", cause: 'busy' }
Net::HTTP.post_form(url, params.merge(event: 'newCall'))
diff --git a/test/browser_test_helper.rb b/test/browser_test_helper.rb
index 3bf22a0bc..b55b53f91 100644
--- a/test/browser_test_helper.rb
+++ b/test/browser_test_helper.rb
@@ -409,43 +409,40 @@ class TestCase < Test::Unit::TestCase
log('click', params)
instance = params[:browser] || @browser
- if params[:css]
-
- begin
- element = instance.find_elements(css: params[:css])[0]
- return if !element && params[:only_if_exists] == true
- #if element
- # instance.action.move_to(element).release.perform
- #end
- element.click
- rescue => e
- sleep 0.5
-
- # just try again
- log('click', { rescure: true })
- element = instance.find_elements(css: params[:css])[0]
- return if !element && params[:only_if_exists] == true
- #if element
- # instance.action.move_to(element).release.perform
- #end
- raise "No such element '#{params[:css]}'" if !element
- element.click
- end
-
+ if params.include?(:css)
+ param_key = :css
+ find_element_key = :css
else
+ param_key = :text
+ find_element_key = :partial_link_text
sleep 0.5
- begin
- instance.find_elements(partial_link_text: params[:text])[0].click
- rescue => e
- sleep 0.5
-
- # just try again
- log('click', { rescure: true })
- element = instance.find_elements(partial_link_text: params[:text])[0]
- raise "No such element '#{params[:text]}'" if !element
- element.click
- end
end
+
+ begin
+ elements = instance.find_elements(find_element_key => params[param_key])
+ .tap { |e| e.slice!(1..-1) unless params[:all] }
+
+ if elements.empty?
+ return if params[:only_if_exists] == true
+ raise "No such element '#{params[param_key]}'"
+ end
+
+ # a clumsy substitute for elements.each(&:click)
+ # (we need to refresh element references after each element.click
+ # because if clicks alter page content,
+ # subsequent element.clicks will raise a StaleElementReferenceError)
+ elements.length.times do |i|
+ instance.find_elements(find_element_key => params[param_key])[i].try(:click)
+ end
+ rescue => e
+ raise e if (fail_count ||= 0).positive?
+
+ fail_count += 1
+ log('click', { rescure: true })
+ sleep 0.5
+ retry
+ end
+
sleep 0.2 if !params[:fast]
sleep params[:wait] if params[:wait]
end
|