Merge branch 'develop' of git.znuny.com:zammad/zammad into develop
This commit is contained in:
commit
221c8a0538
7 changed files with 279 additions and 80 deletions
|
@ -235,7 +235,15 @@ class EmailReply extends App.Controller
|
||||||
|
|
||||||
articleTypes
|
articleTypes
|
||||||
|
|
||||||
@setArticleType: (type, ticket, ui, signaturePosition) ->
|
@setArticleTypePre: (type, ticket, ui, signaturePosition) ->
|
||||||
|
|
||||||
|
# remove old signature
|
||||||
|
if type isnt 'email'
|
||||||
|
ui.$('[data-name=body] [data-signature=true]').remove()
|
||||||
|
return
|
||||||
|
|
||||||
|
@setArticleTypePost: (type, ticket, ui, signaturePosition) ->
|
||||||
|
return if type isnt 'email'
|
||||||
|
|
||||||
# detect current signature (use current group_id, if not set, use ticket.group_id)
|
# detect current signature (use current group_id, if not set, use ticket.group_id)
|
||||||
ticketCurrent = App.Ticket.fullLocal(ticket.id)
|
ticketCurrent = App.Ticket.fullLocal(ticket.id)
|
||||||
|
@ -249,7 +257,7 @@ class EmailReply extends App.Controller
|
||||||
signature = App.Signature.find(group.signature_id)
|
signature = App.Signature.find(group.signature_id)
|
||||||
|
|
||||||
# add/replace signature
|
# add/replace signature
|
||||||
if signature && signature.body && type is 'email'
|
if signature && signature.body
|
||||||
|
|
||||||
# if signature has changed, remove it
|
# if signature has changed, remove it
|
||||||
signature_id = ui.$('[data-signature=true]').data('signature-id')
|
signature_id = ui.$('[data-signature=true]').data('signature-id')
|
||||||
|
@ -271,15 +279,6 @@ class EmailReply extends App.Controller
|
||||||
body.append(signature)
|
body.append(signature)
|
||||||
ui.$('[data-name=body]').replaceWith(body)
|
ui.$('[data-name=body]').replaceWith(body)
|
||||||
|
|
||||||
# remove old signature
|
|
||||||
else
|
|
||||||
ui.$('[data-name=body] [data-signature=true]').remove()
|
|
||||||
|
|
||||||
if type isnt 'email'
|
|
||||||
ui.$('[name=to]').val('')
|
|
||||||
ui.$('[name=cc]').val('')
|
|
||||||
ui.$('[name=subject]').val('')
|
|
||||||
|
|
||||||
@validation: (type, params, ui) ->
|
@validation: (type, params, ui) ->
|
||||||
return true if type isnt 'email'
|
return true if type isnt 'email'
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ class TelegramReply
|
||||||
}
|
}
|
||||||
articleTypes
|
articleTypes
|
||||||
|
|
||||||
@setArticleType: (type, ticket, ui) ->
|
@setArticleTypePost: (type, ticket, ui) ->
|
||||||
return if type isnt 'telegram personal-message'
|
return if type isnt 'telegram personal-message'
|
||||||
rawHTML = ui.$('[data-name=body]').html()
|
rawHTML = ui.$('[data-name=body]').html()
|
||||||
cleanHTML = App.Utils.htmlRemoveRichtext(rawHTML)
|
cleanHTML = App.Utils.htmlRemoveRichtext(rawHTML)
|
||||||
|
|
|
@ -116,7 +116,7 @@ class TwitterReply
|
||||||
else
|
else
|
||||||
articleNew.to = article.from
|
articleNew.to = article.from
|
||||||
|
|
||||||
if !articleNew.to
|
if !articleNew.to && customer && customer.accounts
|
||||||
articleNew.to = customer.accounts['twitter'].username || customer.accounts['twitter'].uid
|
articleNew.to = customer.accounts['twitter'].username || customer.accounts['twitter'].uid
|
||||||
|
|
||||||
App.Event.trigger('ui::ticket::setArticleType', {
|
App.Event.trigger('ui::ticket::setArticleType', {
|
||||||
|
@ -169,9 +169,23 @@ class TwitterReply
|
||||||
textLength = ui.maxTextLength - App.Utils.textLengthWithUrl(params.body)
|
textLength = ui.maxTextLength - App.Utils.textLengthWithUrl(params.body)
|
||||||
return false if textLength < 0
|
return false if textLength < 0
|
||||||
|
|
||||||
|
# check if recipient exists
|
||||||
|
if _.isEmpty(params.to)
|
||||||
|
new App.ControllerModal(
|
||||||
|
head: 'Text missing'
|
||||||
|
buttonCancel: 'Cancel'
|
||||||
|
buttonCancelClass: 'btn--danger'
|
||||||
|
buttonSubmit: false
|
||||||
|
message: 'Need recipient in "To".'
|
||||||
|
shown: true
|
||||||
|
small: true
|
||||||
|
container: ui.el.closest('.content')
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
||||||
@setArticleType: (type, ticket, ui) ->
|
@setArticleTypePost: (type, ticket, ui) ->
|
||||||
return if type isnt 'twitter status' && type isnt 'twitter direct-message'
|
return if type isnt 'twitter status' && type isnt 'twitter direct-message'
|
||||||
rawHTML = ui.$('[data-name=body]').html()
|
rawHTML = ui.$('[data-name=body]').html()
|
||||||
cleanHTML = App.Utils.htmlRemoveRichtext(rawHTML)
|
cleanHTML = App.Utils.htmlRemoveRichtext(rawHTML)
|
||||||
|
|
|
@ -51,6 +51,8 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
@bind('ui::ticket::setArticleType', (data) =>
|
@bind('ui::ticket::setArticleType', (data) =>
|
||||||
return if data.ticket.id.toString() isnt @ticket_id.toString()
|
return if data.ticket.id.toString() isnt @ticket_id.toString()
|
||||||
|
|
||||||
|
@setArticleTypePre(data.type.name, data.signaturePosition)
|
||||||
|
|
||||||
@openTextarea(null, true)
|
@openTextarea(null, true)
|
||||||
for key, value of data.article
|
for key, value of data.article
|
||||||
if key is 'body'
|
if key is 'body'
|
||||||
|
@ -58,8 +60,7 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
else
|
else
|
||||||
@$("[name=\"#{key}\"]").val(value).trigger('change')
|
@$("[name=\"#{key}\"]").val(value).trigger('change')
|
||||||
|
|
||||||
# preselect article type
|
@setArticleTypePost(data.type.name, data.signaturePosition)
|
||||||
@setArticleType(data.type.name, data.signaturePosition)
|
|
||||||
|
|
||||||
# set focus into field
|
# set focus into field
|
||||||
if data.focus
|
if data.focus
|
||||||
|
@ -120,11 +121,8 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
App.Delay.set(a, 500, undefined, 'tags')
|
App.Delay.set(a, 500, undefined, 'tags')
|
||||||
|
|
||||||
setPossibleArticleTypes: =>
|
setPossibleArticleTypes: =>
|
||||||
actionConfig = App.Config.get('TicketZoomArticleAction')
|
|
||||||
keys = _.keys(actionConfig).sort()
|
|
||||||
@articleTypes = []
|
@articleTypes = []
|
||||||
for key in keys
|
for config in @actions()
|
||||||
config = actionConfig[key]
|
|
||||||
if config && config.articleTypes
|
if config && config.articleTypes
|
||||||
@articleTypes = config.articleTypes(@articleTypes, @ticket, @)
|
@articleTypes = config.articleTypes(@articleTypes, @ticket, @)
|
||||||
|
|
||||||
|
@ -166,7 +164,8 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
isCustomer: @permissionCheck('ticket.customer')
|
isCustomer: @permissionCheck('ticket.customer')
|
||||||
internalSelector: @internalSelector
|
internalSelector: @internalSelector
|
||||||
)
|
)
|
||||||
@setArticleType(@type)
|
@setArticleTypePre(@type)
|
||||||
|
@setArticleTypePost(@type)
|
||||||
|
|
||||||
new App.WidgetAvatar(
|
new App.WidgetAvatar(
|
||||||
el: @$('.js-avatar')
|
el: @$('.js-avatar')
|
||||||
|
@ -278,10 +277,7 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
params.internal = false
|
params.internal = false
|
||||||
|
|
||||||
# backend based validation
|
# backend based validation
|
||||||
actionConfig = App.Config.get('TicketZoomArticleAction')
|
for config in @actions()
|
||||||
keys = _.keys(actionConfig).sort()
|
|
||||||
for key in keys
|
|
||||||
config = actionConfig[key]
|
|
||||||
if config && config.params
|
if config && config.params
|
||||||
params = config.params(params.type, params, @)
|
params = config.params(params.type, params, @)
|
||||||
|
|
||||||
|
@ -323,10 +319,7 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# backend based validation
|
# backend based validation
|
||||||
actionConfig = App.Config.get('TicketZoomArticleAction')
|
for config in @actions()
|
||||||
keys = _.keys(actionConfig).sort()
|
|
||||||
for key in keys
|
|
||||||
config = actionConfig[key]
|
|
||||||
if config && config.validation
|
if config && config.validation
|
||||||
return false if !config.validation(params.type, params, @)
|
return false if !config.validation(params.type, params, @)
|
||||||
|
|
||||||
|
@ -350,10 +343,11 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
selectArticleType: (event) =>
|
selectArticleType: (event) =>
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
articleTypeToSet = $(event.target).closest('.pop-selectable').data('value')
|
articleTypeToSet = $(event.target).closest('.pop-selectable').data('value')
|
||||||
@setArticleType(articleTypeToSet)
|
@setArticleTypePre(articleTypeToSet)
|
||||||
@hideSelectableArticleType()
|
@hideSelectableArticleType()
|
||||||
|
@setArticleTypePost(articleTypeToSet)
|
||||||
|
|
||||||
$(window).off 'click.ticket-zoom-select-type'
|
$(window).off('click.ticket-zoom-select-type')
|
||||||
@tokanice()
|
@tokanice()
|
||||||
|
|
||||||
hideSelectableArticleType: =>
|
hideSelectableArticleType: =>
|
||||||
|
@ -374,8 +368,14 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
|
|
||||||
@$('[name=internal]').val('')
|
@$('[name=internal]').val('')
|
||||||
|
|
||||||
setArticleType: (type, signaturePosition = 'bottom') =>
|
setArticleTypePre: (type, signaturePosition = 'bottom') =>
|
||||||
wasScrolledToBottom = @isScrolledToBottom()
|
wasScrolledToBottom = @isScrolledToBottom()
|
||||||
|
|
||||||
|
# reset old params
|
||||||
|
if type isnt @type
|
||||||
|
for key in ['to', 'cc', 'bcc', 'subject', 'in_reply_to']
|
||||||
|
@$("[name=#{key}]").val('').trigger('change')
|
||||||
|
|
||||||
@type = type
|
@type = type
|
||||||
@$('[name=type]').val(type).trigger('change')
|
@$('[name=type]').val(type).trigger('change')
|
||||||
@articleNewEdit.attr('data-type', type)
|
@articleNewEdit.attr('data-type', type)
|
||||||
|
@ -395,13 +395,6 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
else
|
else
|
||||||
@setArticleInternal(false)
|
@setArticleInternal(false)
|
||||||
|
|
||||||
actionConfig = App.Config.get('TicketZoomArticleAction')
|
|
||||||
keys = _.keys(actionConfig).sort()
|
|
||||||
for key in keys
|
|
||||||
localConfig = actionConfig[key]
|
|
||||||
if localConfig && localConfig.setArticleType
|
|
||||||
localConfig.setArticleType(@type, @ticket, @, signaturePosition)
|
|
||||||
|
|
||||||
# show/hide attributes/features
|
# show/hide attributes/features
|
||||||
@maxTextLength = undefined
|
@maxTextLength = undefined
|
||||||
@warningTextLength = undefined
|
@warningTextLength = undefined
|
||||||
|
@ -438,6 +431,11 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
|
|
||||||
@scrollToBottom() if wasScrolledToBottom
|
@scrollToBottom() if wasScrolledToBottom
|
||||||
|
|
||||||
|
setArticleTypePost: (type, signaturePosition = 'bottom') =>
|
||||||
|
for localConfig in @actions()
|
||||||
|
if localConfig && localConfig.setArticleTypePost
|
||||||
|
localConfig.setArticleTypePost(@type, @ticket, @, signaturePosition)
|
||||||
|
|
||||||
isScrolledToBottom: ->
|
isScrolledToBottom: ->
|
||||||
return @el.scrollParent().scrollTop() + @el.scrollParent().height() is @el.scrollParent().prop('scrollHeight')
|
return @el.scrollParent().scrollTop() + @el.scrollParent().height() is @el.scrollParent().prop('scrollHeight')
|
||||||
|
|
||||||
|
@ -615,3 +613,13 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
if element.find('.attachment').length == 0
|
if element.find('.attachment').length == 0
|
||||||
element.empty()
|
element.empty()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
actions: ->
|
||||||
|
actionConfig = App.Config.get('TicketZoomArticleAction')
|
||||||
|
keys = _.keys(actionConfig).sort()
|
||||||
|
actions = []
|
||||||
|
for key in keys
|
||||||
|
localConfig = actionConfig[key]
|
||||||
|
if localConfig
|
||||||
|
actions.push localConfig
|
||||||
|
actions
|
||||||
|
|
|
@ -23,6 +23,7 @@ class Observer::Ticket::Article::CommunicateTwitter < ActiveRecord::Observer
|
||||||
type = Ticket::Article::Type.lookup(id: record.type_id)
|
type = Ticket::Article::Type.lookup(id: record.type_id)
|
||||||
return if type['name'] !~ /\Atwitter/i
|
return if type['name'] !~ /\Atwitter/i
|
||||||
|
|
||||||
|
raise Exceptions::UnprocessableEntity, 'twitter to: parameter is missing' if record.to.blank? && type['name'] == 'twitter direct-message'
|
||||||
Delayed::Job.enqueue(Observer::Ticket::Article::CommunicateTwitter::BackgroundJob.new(record.id))
|
Delayed::Job.enqueue(Observer::Ticket::Article::CommunicateTwitter::BackgroundJob.new(record.id))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,36 +3,7 @@ require 'browser_test_helper'
|
||||||
|
|
||||||
class TwitterBrowserTest < TestCase
|
class TwitterBrowserTest < TestCase
|
||||||
def test_add_config
|
def test_add_config
|
||||||
|
twitter_config
|
||||||
# app config
|
|
||||||
if !ENV['TWITTER_BT_CONSUMER_KEY']
|
|
||||||
raise "ERROR: Need TWITTER_BT_CONSUMER_KEY - hint TWITTER_BT_CONSUMER_KEY='1234'"
|
|
||||||
end
|
|
||||||
consumer_key = ENV['TWITTER_BT_CONSUMER_KEY']
|
|
||||||
if !ENV['TWITTER_BT_CONSUMER_SECRET']
|
|
||||||
raise "ERROR: Need TWITTER_BT_CONSUMER_SECRET - hint TWITTER_BT_CONSUMER_SECRET='1234'"
|
|
||||||
end
|
|
||||||
consumer_secret = ENV['TWITTER_BT_CONSUMER_SECRET']
|
|
||||||
|
|
||||||
if !ENV['TWITTER_BT_USER_LOGIN']
|
|
||||||
raise "ERROR: Need TWITTER_BT_USER_LOGIN - hint TWITTER_BT_USER_LOGIN='1234'"
|
|
||||||
end
|
|
||||||
twitter_user_login = ENV['TWITTER_BT_USER_LOGIN']
|
|
||||||
|
|
||||||
if !ENV['TWITTER_BT_USER_PW']
|
|
||||||
raise "ERROR: Need TWITTER_BT_USER_PW - hint TWITTER_BT_USER_PW='1234'"
|
|
||||||
end
|
|
||||||
twitter_user_pw = ENV['TWITTER_BT_USER_PW']
|
|
||||||
|
|
||||||
if !ENV['TWITTER_BT_CUSTOMER_TOKEN']
|
|
||||||
raise "ERROR: Need TWITTER_BT_CUSTOMER_TOKEN - hint TWITTER_BT_CUSTOMER_TOKEN='1234'"
|
|
||||||
end
|
|
||||||
twitter_customer_token = ENV['TWITTER_BT_CUSTOMER_TOKEN']
|
|
||||||
|
|
||||||
if !ENV['TWITTER_BT_CUSTOMER_TOKEN_SECRET']
|
|
||||||
raise "ERROR: Need TWITTER_BT_CUSTOMER_TOKEN_SECRET - hint TWITTER_BT_CUSTOMER_TOKEN_SECRET='1234'"
|
|
||||||
end
|
|
||||||
twitter_customer_token_secret = ENV['TWITTER_BT_CUSTOMER_TOKEN_SECRET']
|
|
||||||
|
|
||||||
hash = "#sweet#{hash_gen}"
|
hash = "#sweet#{hash_gen}"
|
||||||
|
|
||||||
|
@ -51,7 +22,7 @@ class TwitterBrowserTest < TestCase
|
||||||
sleep 2
|
sleep 2
|
||||||
set(
|
set(
|
||||||
css: '.content.active .modal [name=consumer_key]',
|
css: '.content.active .modal [name=consumer_key]',
|
||||||
value: consumer_key,
|
value: twitter_config[:consumer_key],
|
||||||
)
|
)
|
||||||
set(
|
set(
|
||||||
css: '.content.active .modal [name=consumer_secret]',
|
css: '.content.active .modal [name=consumer_secret]',
|
||||||
|
@ -66,7 +37,7 @@ class TwitterBrowserTest < TestCase
|
||||||
|
|
||||||
set(
|
set(
|
||||||
css: '.content.active .modal [name=consumer_secret]',
|
css: '.content.active .modal [name=consumer_secret]',
|
||||||
value: consumer_secret,
|
value: twitter_config[:consumer_secret],
|
||||||
)
|
)
|
||||||
click(css: '.content.active .modal .js-submit')
|
click(css: '.content.active .modal .js-submit')
|
||||||
|
|
||||||
|
@ -95,7 +66,7 @@ class TwitterBrowserTest < TestCase
|
||||||
|
|
||||||
set(
|
set(
|
||||||
css: '.content.active .modal [name=consumer_secret]',
|
css: '.content.active .modal [name=consumer_secret]',
|
||||||
value: consumer_secret,
|
value: twitter_config[:consumer_secret],
|
||||||
)
|
)
|
||||||
click(css: '.content.active .modal .js-submit')
|
click(css: '.content.active .modal .js-submit')
|
||||||
|
|
||||||
|
@ -115,12 +86,12 @@ class TwitterBrowserTest < TestCase
|
||||||
|
|
||||||
set(
|
set(
|
||||||
css: '#username_or_email',
|
css: '#username_or_email',
|
||||||
value: twitter_user_login,
|
value: twitter_config[:twitter_user_login],
|
||||||
no_click: true, # <label> other element would receive the click
|
no_click: true, # <label> other element would receive the click
|
||||||
)
|
)
|
||||||
set(
|
set(
|
||||||
css: '#password',
|
css: '#password',
|
||||||
value: twitter_user_pw,
|
value: twitter_config[:twitter_user_pw],
|
||||||
no_click: true, # <label> other element would receive the click
|
no_click: true, # <label> other element would receive the click
|
||||||
)
|
)
|
||||||
click(css: '#allow')
|
click(css: '#allow')
|
||||||
|
@ -139,6 +110,7 @@ class TwitterBrowserTest < TestCase
|
||||||
click(css: '.content.active .modal .js-searchTermAdd')
|
click(css: '.content.active .modal .js-searchTermAdd')
|
||||||
set(css: '.content.active .modal [name="search::term"]', value: hash)
|
set(css: '.content.active .modal [name="search::term"]', value: hash)
|
||||||
select(css: '.content.active .modal [name="search::group_id"]', value: 'Users')
|
select(css: '.content.active .modal [name="search::group_id"]', value: 'Users')
|
||||||
|
select(css: '.content.active .modal [name="direct_messages::group_id"]', value: 'Users')
|
||||||
click(css: '.content.active .modal .js-submit')
|
click(css: '.content.active .modal .js-submit')
|
||||||
modal_disappear
|
modal_disappear
|
||||||
|
|
||||||
|
@ -148,7 +120,7 @@ class TwitterBrowserTest < TestCase
|
||||||
)
|
)
|
||||||
watch_for(
|
watch_for(
|
||||||
css: '.content.active',
|
css: '.content.active',
|
||||||
value: "@#{twitter_user_login}",
|
value: "@#{twitter_config[:twitter_user_login]}",
|
||||||
)
|
)
|
||||||
exists(
|
exists(
|
||||||
css: '.content.active .main .action:nth-child(1)'
|
css: '.content.active .main .action:nth-child(1)'
|
||||||
|
@ -177,7 +149,7 @@ class TwitterBrowserTest < TestCase
|
||||||
)
|
)
|
||||||
watch_for(
|
watch_for(
|
||||||
css: '.content.active',
|
css: '.content.active',
|
||||||
value: "@#{twitter_user_login}",
|
value: "@#{twitter_config[:twitter_user_login]}",
|
||||||
)
|
)
|
||||||
exists(
|
exists(
|
||||||
css: '.content.active .main .action:nth-child(1)'
|
css: '.content.active .main .action:nth-child(1)'
|
||||||
|
@ -191,10 +163,10 @@ class TwitterBrowserTest < TestCase
|
||||||
|
|
||||||
# start tweet from customer
|
# start tweet from customer
|
||||||
client = Twitter::REST::Client.new do |config|
|
client = Twitter::REST::Client.new do |config|
|
||||||
config.consumer_key = consumer_key
|
config.consumer_key = twitter_config[:consumer_key]
|
||||||
config.consumer_secret = consumer_secret
|
config.consumer_secret = twitter_config[:consumer_secret]
|
||||||
config.access_token = twitter_customer_token
|
config.access_token = twitter_config[:twitter_customer_token]
|
||||||
config.access_token_secret = twitter_customer_token_secret
|
config.access_token_secret = twitter_config[:twitter_customer_token_secret]
|
||||||
end
|
end
|
||||||
|
|
||||||
text = "Today #{rand_word}... #{hash} #{hash_gen}"
|
text = "Today #{rand_word}... #{hash} #{hash_gen}"
|
||||||
|
@ -272,6 +244,100 @@ class TwitterBrowserTest < TestCase
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reply_direct_message
|
||||||
|
twitter_config
|
||||||
|
|
||||||
|
@browser = browser_instance
|
||||||
|
login(
|
||||||
|
username: 'master@example.com',
|
||||||
|
password: 'test',
|
||||||
|
url: browser_url,
|
||||||
|
auto_wizard: true,
|
||||||
|
)
|
||||||
|
tasks_close_all()
|
||||||
|
|
||||||
|
client = Twitter::REST::Client.new do |config|
|
||||||
|
config.consumer_key = twitter_config[:consumer_key]
|
||||||
|
config.consumer_secret = twitter_config[:consumer_secret]
|
||||||
|
config.access_token = twitter_config[:twitter_customer_token]
|
||||||
|
config.access_token_secret = twitter_config[:twitter_customer_token_secret]
|
||||||
|
end
|
||||||
|
|
||||||
|
text = "Today #{rand_word}... #{hash} #{hash_gen}"
|
||||||
|
tweet = client.create_direct_message(
|
||||||
|
"@#{twitter_config[:twitter_user_login]}",
|
||||||
|
text,
|
||||||
|
)
|
||||||
|
|
||||||
|
# watch till tweet is in app
|
||||||
|
click(text: 'Overviews')
|
||||||
|
|
||||||
|
# enable full overviews
|
||||||
|
execute(
|
||||||
|
js: '$(".content.active .sidebar").css("display", "block")',
|
||||||
|
)
|
||||||
|
|
||||||
|
click(text: 'Unassigned & Open')
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active',
|
||||||
|
value: hash,
|
||||||
|
timeout: 36,
|
||||||
|
)
|
||||||
|
|
||||||
|
ticket_open_by_title(
|
||||||
|
title: hash,
|
||||||
|
)
|
||||||
|
|
||||||
|
# reply via app
|
||||||
|
click(css: '.content.active [data-type="twitterStatusReply"]')
|
||||||
|
|
||||||
|
ticket_update(
|
||||||
|
data: {
|
||||||
|
body: '@dzucker6 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890',
|
||||||
|
},
|
||||||
|
do_not_submit: true,
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
css: '.content.active .js-submit',
|
||||||
|
)
|
||||||
|
sleep 10
|
||||||
|
click(
|
||||||
|
css: '.content.active .js-reset',
|
||||||
|
)
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
match_not(
|
||||||
|
css: '.content.active',
|
||||||
|
value: '1234567890',
|
||||||
|
)
|
||||||
|
|
||||||
|
click(css: '.content.active [data-type="twitterStatusReply"]')
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
re_hash = "#{hash}re#{rand(99_999)}"
|
||||||
|
|
||||||
|
ticket_update(
|
||||||
|
data: {
|
||||||
|
body: "@dzucker6 #{rand_word} reply #{re_hash} #{rand(999_999)}",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
sleep 20
|
||||||
|
|
||||||
|
match(
|
||||||
|
css: '.content.active .ticket-article',
|
||||||
|
value: re_hash,
|
||||||
|
)
|
||||||
|
|
||||||
|
# watch till tweet reached customer
|
||||||
|
sleep 10
|
||||||
|
text = nil
|
||||||
|
client.search(re_hash, result_type: 'mixed').collect do |local_tweet|
|
||||||
|
text = local_tweet.text
|
||||||
|
end
|
||||||
|
assert(text)
|
||||||
|
end
|
||||||
|
|
||||||
def hash_gen
|
def hash_gen
|
||||||
(0...10).map { ('a'..'z').to_a[rand(26)] }.join + rand(999).to_s
|
(0...10).map { ('a'..'z').to_a[rand(26)] }.join + rand(999).to_s
|
||||||
end
|
end
|
||||||
|
@ -298,4 +364,44 @@ class TwitterBrowserTest < TestCase
|
||||||
words[rand(words.length)]
|
words[rand(words.length)]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def twitter_config
|
||||||
|
# app config
|
||||||
|
if !ENV['TWITTER_BT_CONSUMER_KEY']
|
||||||
|
raise "ERROR: Need TWITTER_BT_CONSUMER_KEY - hint TWITTER_BT_CONSUMER_KEY='1234'"
|
||||||
|
end
|
||||||
|
consumer_key = ENV['TWITTER_BT_CONSUMER_KEY']
|
||||||
|
if !ENV['TWITTER_BT_CONSUMER_SECRET']
|
||||||
|
raise "ERROR: Need TWITTER_BT_CONSUMER_SECRET - hint TWITTER_BT_CONSUMER_SECRET='1234'"
|
||||||
|
end
|
||||||
|
consumer_secret = ENV['TWITTER_BT_CONSUMER_SECRET']
|
||||||
|
|
||||||
|
if !ENV['TWITTER_BT_USER_LOGIN']
|
||||||
|
raise "ERROR: Need TWITTER_BT_USER_LOGIN - hint TWITTER_BT_USER_LOGIN='1234'"
|
||||||
|
end
|
||||||
|
twitter_user_login = ENV['TWITTER_BT_USER_LOGIN']
|
||||||
|
|
||||||
|
if !ENV['TWITTER_BT_USER_PW']
|
||||||
|
raise "ERROR: Need TWITTER_BT_USER_PW - hint TWITTER_BT_USER_PW='1234'"
|
||||||
|
end
|
||||||
|
twitter_user_pw = ENV['TWITTER_BT_USER_PW']
|
||||||
|
|
||||||
|
if !ENV['TWITTER_BT_CUSTOMER_TOKEN']
|
||||||
|
raise "ERROR: Need TWITTER_BT_CUSTOMER_TOKEN - hint TWITTER_BT_CUSTOMER_TOKEN='1234'"
|
||||||
|
end
|
||||||
|
twitter_customer_token = ENV['TWITTER_BT_CUSTOMER_TOKEN']
|
||||||
|
|
||||||
|
if !ENV['TWITTER_BT_CUSTOMER_TOKEN_SECRET']
|
||||||
|
raise "ERROR: Need TWITTER_BT_CUSTOMER_TOKEN_SECRET - hint TWITTER_BT_CUSTOMER_TOKEN_SECRET='1234'"
|
||||||
|
end
|
||||||
|
twitter_customer_token_secret = ENV['TWITTER_BT_CUSTOMER_TOKEN_SECRET']
|
||||||
|
|
||||||
|
hash = {
|
||||||
|
consumer_key: consumer_key,
|
||||||
|
consumer_secret: consumer_secret,
|
||||||
|
twitter_user_login: twitter_user_login,
|
||||||
|
twitter_user_pw: twitter_user_pw,
|
||||||
|
twitter_customer_token: twitter_customer_token,
|
||||||
|
twitter_customer_token_secret: twitter_customer_token_secret
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -427,6 +427,75 @@ class TwitterTest < ActiveSupport::TestCase
|
||||||
assert_equal('ok', channel.status_in)
|
assert_equal('ok', channel.status_in)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'c new by direct message outbound without required parameters' do
|
||||||
|
|
||||||
|
# cleanup direct messages of system
|
||||||
|
client = Twitter::REST::Client.new do |config|
|
||||||
|
config.consumer_key = consumer_key
|
||||||
|
config.consumer_secret = consumer_secret
|
||||||
|
config.access_token = system_token
|
||||||
|
config.access_token_secret = system_token_secret
|
||||||
|
end
|
||||||
|
dms = client.direct_messages(count: 100)
|
||||||
|
dms.each do |dm|
|
||||||
|
client.destroy_direct_message(dm.id)
|
||||||
|
end
|
||||||
|
client = Twitter::REST::Client.new(
|
||||||
|
consumer_key: consumer_key,
|
||||||
|
consumer_secret: consumer_secret,
|
||||||
|
access_token: customer_token,
|
||||||
|
access_token_secret: customer_token_secret
|
||||||
|
)
|
||||||
|
dms = client.direct_messages(count: 100)
|
||||||
|
dms.each do |dm|
|
||||||
|
client.destroy_direct_message(dm.id)
|
||||||
|
end
|
||||||
|
hash = "#citheo44 #{hash_gen}"
|
||||||
|
text = "How about #{rand_word} the details? #{hash} - #{'Long' * 50}"
|
||||||
|
dm = client.create_direct_message(
|
||||||
|
system_login_without_at,
|
||||||
|
text,
|
||||||
|
)
|
||||||
|
assert(dm, "dm with ##{hash} created")
|
||||||
|
|
||||||
|
# fetch check system account
|
||||||
|
sleep 15
|
||||||
|
article = nil
|
||||||
|
1.times do
|
||||||
|
Channel.fetch
|
||||||
|
|
||||||
|
# check if ticket and article has been created
|
||||||
|
article = Ticket::Article.find_by(message_id: dm.id)
|
||||||
|
break if article
|
||||||
|
sleep 10
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(article, "inbound article '#{text}' created")
|
||||||
|
assert_equal(customer_login, article.from, 'ticket article from')
|
||||||
|
assert_equal(text, article.body, 'ticket article body')
|
||||||
|
ticket = article.ticket
|
||||||
|
assert(ticket, 'ticket of inbound article exists')
|
||||||
|
assert(ticket.articles, 'ticket.articles exists')
|
||||||
|
assert_equal(1, ticket.articles.count, 'ticket article inbound count')
|
||||||
|
assert_equal(ticket.state.name, 'closed')
|
||||||
|
|
||||||
|
# reply via ticket
|
||||||
|
reply = assert_raises(Exceptions::UnprocessableEntity) do
|
||||||
|
Ticket::Article.create!(
|
||||||
|
ticket_id: ticket.id,
|
||||||
|
in_reply_to: '123456789',
|
||||||
|
body: "Will call you later #{rand_word}!",
|
||||||
|
type: Ticket::Article::Type.find_by(name: 'twitter direct-message'),
|
||||||
|
sender: Ticket::Article::Sender.find_by(name: 'Agent'),
|
||||||
|
internal: false,
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal('twitter to: parameter is missing', reply.message)
|
||||||
|
end
|
||||||
|
|
||||||
test 'd track_retweets enabled' do
|
test 'd track_retweets enabled' do
|
||||||
|
|
||||||
# enable track_retweets
|
# enable track_retweets
|
||||||
|
@ -538,6 +607,7 @@ class TwitterTest < ActiveSupport::TestCase
|
||||||
ActiveRecord::Base.connection.query_cache.clear
|
ActiveRecord::Base.connection.query_cache.clear
|
||||||
sleep 10
|
sleep 10
|
||||||
end
|
end
|
||||||
|
|
||||||
assert(article, "article from customer with text '#{text}' message_id '#{tweet.id}' created")
|
assert(article, "article from customer with text '#{text}' message_id '#{tweet.id}' created")
|
||||||
assert_equal(customer_login, article.from, 'ticket article from')
|
assert_equal(customer_login, article.from, 'ticket article from')
|
||||||
assert_nil(article.to, 'ticket article to')
|
assert_nil(article.to, 'ticket article to')
|
||||||
|
@ -768,6 +838,7 @@ class TwitterTest < ActiveSupport::TestCase
|
||||||
)
|
)
|
||||||
article = nil
|
article = nil
|
||||||
5.times do
|
5.times do
|
||||||
|
Channel.fetch
|
||||||
Scheduler.worker(true)
|
Scheduler.worker(true)
|
||||||
article = Ticket::Article.find_by(message_id: tweet.id)
|
article = Ticket::Article.find_by(message_id: tweet.id)
|
||||||
break if article
|
break if article
|
||||||
|
|
Loading…
Reference in a new issue