Added browser tests for customer chat.
This commit is contained in:
parent
0cb6e34870
commit
7831f1c1f8
18 changed files with 1418 additions and 566 deletions
|
@ -24,7 +24,7 @@ class App.ChannelChat extends App.Controller
|
||||||
'.js-code': 'code'
|
'.js-code': 'code'
|
||||||
'.js-palette': 'palette'
|
'.js-palette': 'palette'
|
||||||
'.js-color': 'colorField'
|
'.js-color': 'colorField'
|
||||||
'.js-chatSetting': 'chatSetting'
|
'.js-chatSetting input': 'chatSetting'
|
||||||
|
|
||||||
apiOptions: [
|
apiOptions: [
|
||||||
{
|
{
|
||||||
|
@ -279,6 +279,8 @@ class App.ChannelChat extends App.Controller
|
||||||
setting.state_current = { value: value }
|
setting.state_current = { value: value }
|
||||||
setting.save()
|
setting.save()
|
||||||
@Config.set('chat', value)
|
@Config.set('chat', value)
|
||||||
|
delay = -> App.Event.trigger('ui:rerender')
|
||||||
|
@delay(delay, 200)
|
||||||
|
|
||||||
updateParams: =>
|
updateParams: =>
|
||||||
quote = (value) ->
|
quote = (value) ->
|
||||||
|
|
|
@ -7,7 +7,7 @@ class App.ChannelForm extends App.Controller
|
||||||
|
|
||||||
elements:
|
elements:
|
||||||
'.js-paramsBlock': 'paramsBlock'
|
'.js-paramsBlock': 'paramsBlock'
|
||||||
'.js-formSetting': 'formSetting'
|
'.js-formSetting input': 'formSetting'
|
||||||
|
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
|
|
@ -11,14 +11,6 @@ class App.CustomerChat extends App.Controller
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
|
||||||
# access check
|
|
||||||
if !@isRole('Chat')
|
|
||||||
@renderScreenUnauthorized(objectName: 'Chat')
|
|
||||||
return
|
|
||||||
if !@Config.get('chat')
|
|
||||||
@renderScreenError(detail: 'Feature disabled!')
|
|
||||||
return
|
|
||||||
|
|
||||||
@chatWindows = {}
|
@chatWindows = {}
|
||||||
@maxChatWindows = 4
|
@maxChatWindows = 4
|
||||||
preferences = @Session.get('preferences')
|
preferences = @Session.get('preferences')
|
||||||
|
@ -49,7 +41,6 @@ class App.CustomerChat extends App.Controller
|
||||||
|
|
||||||
# add new chat window
|
# add new chat window
|
||||||
@bind('chat_session_start', (data) =>
|
@bind('chat_session_start', (data) =>
|
||||||
console.log('chat_session_start', data)
|
|
||||||
if data.session
|
if data.session
|
||||||
@addChat(data.session)
|
@addChat(data.session)
|
||||||
)
|
)
|
||||||
|
@ -78,6 +69,13 @@ class App.CustomerChat extends App.Controller
|
||||||
)
|
)
|
||||||
|
|
||||||
render: ->
|
render: ->
|
||||||
|
if !@isRole('Chat')
|
||||||
|
@renderScreenUnauthorized(objectName: 'Chat')
|
||||||
|
return
|
||||||
|
if !@Config.get('chat')
|
||||||
|
@renderScreenError(detail: 'Feature disabled!')
|
||||||
|
return
|
||||||
|
|
||||||
@html App.view('customer_chat/index')()
|
@html App.view('customer_chat/index')()
|
||||||
|
|
||||||
show: (params) =>
|
show: (params) =>
|
||||||
|
|
|
@ -82,7 +82,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
|
||||||
)
|
)
|
||||||
|
|
||||||
# bind on switch changes and execute it on controller
|
# bind on switch changes and execute it on controller
|
||||||
@$('.js-menu .js-switch').bind('change', (e) ->
|
@$('.js-menu .js-switch input').bind('change', (e) ->
|
||||||
val = $(e.target).prop('checked')
|
val = $(e.target).prop('checked')
|
||||||
key = $(e.target).closest('.menu-item').data('key')
|
key = $(e.target).closest('.menu-item').data('key')
|
||||||
return if !key
|
return if !key
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
<h2><%- @T('Enable') %>/<%- @T('Disable') %></h2>
|
<h2><%- @T('Enable') %>/<%- @T('Disable') %></h2>
|
||||||
<form>
|
<form>
|
||||||
<div class="zammad-switch">
|
<div class="zammad-switch js-chatSetting">
|
||||||
<input name="chat" type="checkbox" id="setting-chat" class="js-chatSetting" <% if @chatSetting: %>checked<% end %>>
|
<input name="chat" type="checkbox" id="setting-chat" <% if @chatSetting: %>checked<% end %>>
|
||||||
<label for="setting-chat"></label>
|
<label for="setting-chat"></label>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
<h2><%- @T('Enable') %>/<%- @T('Disable') %></h2>
|
<h2><%- @T('Enable') %>/<%- @T('Disable') %></h2>
|
||||||
<form>
|
<form>
|
||||||
<div class="zammad-switch">
|
<div class="zammad-switch js-formSetting">
|
||||||
<input name="form_ticket_create" type="checkbox" id="setting-form" class="js-formSetting" <% if @formSetting: %>checked<% end %>>
|
<input name="form_ticket_create" type="checkbox" id="setting-form" <% if @formSetting: %>checked<% end %>>
|
||||||
<label for="setting-form"></label>
|
<label for="setting-form"></label>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -30,8 +30,8 @@
|
||||||
<span class="counter badge badge--big"><%= item.counter %></span>
|
<span class="counter badge badge--big"><%= item.counter %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if item.switch isnt undefined: %>
|
<% if item.switch isnt undefined: %>
|
||||||
<span class="zammad-switch zammad-switch--dark zammad-switch--small zammad-switch--green">
|
<span class="zammad-switch zammad-switch--dark zammad-switch--small zammad-switch--green js-switch">
|
||||||
<input type="checkbox" id="<%- item.class %>-switch" class="js-switch" <% if item.switch: %>checked<% end %>>
|
<input type="checkbox" id="<%- item.class %>-switch" <% if item.switch: %>checked<% end %>>
|
||||||
<label for="<%- item.class %>-switch"></label>
|
<label for="<%- item.class %>-switch"></label>
|
||||||
</span>
|
</span>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -18,6 +18,18 @@ class Chat::Session < ApplicationModel
|
||||||
preferences[:participants]
|
preferences[:participants]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def recipients_active?
|
||||||
|
return true if !preferences
|
||||||
|
return true if !preferences[:participants]
|
||||||
|
count = 0
|
||||||
|
preferences[:participants].each {|client_id|
|
||||||
|
next if !Sessions.session_exists?(client_id)
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
return true if count >= 2
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
def send_to_recipients(message, ignore_client_id = nil)
|
def send_to_recipients(message, ignore_client_id = nil)
|
||||||
preferences[:participants].each {|local_client_id|
|
preferences[:participants].each {|local_client_id|
|
||||||
next if local_client_id == ignore_client_id
|
next if local_client_id == ignore_client_id
|
||||||
|
|
36
app/models/observer/chat/leave/background_job.rb
Normal file
36
app/models/observer/chat/leave/background_job.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
class Observer::Chat::Leave::BackgroundJob
|
||||||
|
def initialize(chat_session_id, client_id, session)
|
||||||
|
@chat_session_id = chat_session_id
|
||||||
|
@client_id = client_id
|
||||||
|
@session = session
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform
|
||||||
|
|
||||||
|
# check if customer has permanently left the conversation
|
||||||
|
chat_session = Chat::Session.find_by(id: @chat_session_id)
|
||||||
|
return if !chat_session
|
||||||
|
return if chat_session.recipients_active?
|
||||||
|
chat_session.state = 'closed'
|
||||||
|
chat_session.save
|
||||||
|
|
||||||
|
realname = 'Anonymous'
|
||||||
|
if @session && @session['id']
|
||||||
|
realname = User.lookup(id: @session['id']).fullname
|
||||||
|
end
|
||||||
|
|
||||||
|
# notifiy participients
|
||||||
|
message = {
|
||||||
|
event: 'chat_session_left',
|
||||||
|
data: {
|
||||||
|
realname: realname,
|
||||||
|
session_id: chat_session.session_id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
chat_session.send_to_recipients(message, @client_id)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -16,7 +16,11 @@ class Setting < ApplicationModel
|
||||||
@@current = {} # rubocop:disable Style/ClassVars
|
@@current = {} # rubocop:disable Style/ClassVars
|
||||||
@@change_id = nil # rubocop:disable Style/ClassVars
|
@@change_id = nil # rubocop:disable Style/ClassVars
|
||||||
@@lookup_at = nil # rubocop:disable Style/ClassVars
|
@@lookup_at = nil # rubocop:disable Style/ClassVars
|
||||||
|
if ENV['ZAMMAD_SETTING_TTL']
|
||||||
|
@@lookup_timeout = ENV['ZAMMAD_SETTING_TTL'].to_i # rubocop:disable Style/ClassVars
|
||||||
|
else
|
||||||
@@lookup_timeout = 2.minutes # rubocop:disable Style/ClassVars
|
@@lookup_timeout = 2.minutes # rubocop:disable Style/ClassVars
|
||||||
|
end
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,135 @@ do($ = window.jQuery, window) ->
|
||||||
scriptHost = myScript.src.match('.*://([^:/]*).*')[1]
|
scriptHost = myScript.src.match('.*://([^:/]*).*')[1]
|
||||||
|
|
||||||
# Define the plugin class
|
# Define the plugin class
|
||||||
class ZammadChat
|
class Base
|
||||||
|
defaults:
|
||||||
|
debug: false
|
||||||
|
|
||||||
|
constructor: (options) ->
|
||||||
|
@options = $.extend {}, @defaults, options
|
||||||
|
@log = new Log(debug: @options.debug, logPrefix: @options.logPrefix || @logPrefix)
|
||||||
|
|
||||||
|
class Log
|
||||||
|
defaults:
|
||||||
|
debug: false
|
||||||
|
|
||||||
|
constructor: (options) ->
|
||||||
|
@options = $.extend {}, @defaults, options
|
||||||
|
|
||||||
|
debug: (items...) =>
|
||||||
|
return if !@options.debug && level is 'debug'
|
||||||
|
@log('debug', items)
|
||||||
|
|
||||||
|
notice: (items...) =>
|
||||||
|
@log('notice', items)
|
||||||
|
|
||||||
|
error: (items...) =>
|
||||||
|
@log('error', items)
|
||||||
|
return if !@options.debug && level is 'debug'
|
||||||
|
items.unshift(level)
|
||||||
|
console.log.apply console, string
|
||||||
|
|
||||||
|
log: (level, items) =>
|
||||||
|
items.unshift('||')
|
||||||
|
items.unshift(level)
|
||||||
|
items.unshift(@options.logPrefix)
|
||||||
|
console.log.apply console, items
|
||||||
|
|
||||||
|
return if !@options.debug
|
||||||
|
logString = ''
|
||||||
|
for item in items
|
||||||
|
logString += ' '
|
||||||
|
if typeof item is 'object'
|
||||||
|
logString += JSON.stringify(item)
|
||||||
|
else if item && item.toString
|
||||||
|
logString += item.toString()
|
||||||
|
else
|
||||||
|
logString += item
|
||||||
|
$('.js-chatLogDisplay').prepend('<div>' + logString + '</div>')
|
||||||
|
|
||||||
|
class Timeout extends Base
|
||||||
|
timeoutStartedAt: null
|
||||||
|
logPrefix: 'timeout'
|
||||||
|
defaults:
|
||||||
|
debug: false
|
||||||
|
timeout: 4
|
||||||
|
timeoutIntervallCheck: 0.5
|
||||||
|
|
||||||
|
constructor: (options) ->
|
||||||
|
super(options)
|
||||||
|
|
||||||
|
start: =>
|
||||||
|
@stop()
|
||||||
|
timeoutStartedAt = new Date
|
||||||
|
check = =>
|
||||||
|
timeLeft = new Date - new Date(timeoutStartedAt.getTime() + @options.timeout * 1000 * 60)
|
||||||
|
@log.debug "Timeout check for #{@options.timeout} minutes (left #{timeLeft/1000} sec.)"#, new Date
|
||||||
|
return if timeLeft < 0
|
||||||
|
@stop()
|
||||||
|
@options.callback()
|
||||||
|
@log.debug "Start timeout in #{@options.timeout} minutes"#, new Date
|
||||||
|
@intervallId = setInterval(check, @options.timeoutIntervallCheck * 1000 * 60)
|
||||||
|
|
||||||
|
stop: =>
|
||||||
|
return if !@intervallId
|
||||||
|
@log.debug "Stop timeout of #{@options.timeout} minutes at"#, new Date
|
||||||
|
clearInterval(@intervallId)
|
||||||
|
|
||||||
|
class Io extends Base
|
||||||
|
logPrefix: 'io'
|
||||||
|
constructor: (options) ->
|
||||||
|
super(options)
|
||||||
|
|
||||||
|
set: (params) =>
|
||||||
|
for key, value of params
|
||||||
|
@options[key] = value
|
||||||
|
|
||||||
|
detectHost: ->
|
||||||
|
protocol = 'ws://'
|
||||||
|
if window.location.protocol is 'https:'
|
||||||
|
protocol = 'wss://'
|
||||||
|
@options.host = "#{ protocol }#{ scriptHost }/ws"
|
||||||
|
|
||||||
|
connect: =>
|
||||||
|
@detectHost() if !@options.host
|
||||||
|
|
||||||
|
@log.debug "Connecting to #{@options.host}"
|
||||||
|
@ws = new window.WebSocket("#{@options.host}")
|
||||||
|
@ws.onopen = (e) =>
|
||||||
|
@log.debug 'on open', e
|
||||||
|
@options.onOpen(e)
|
||||||
|
|
||||||
|
@ws.onmessage = (e) =>
|
||||||
|
pipes = JSON.parse(e.data)
|
||||||
|
@log.debug 'on message', e.data
|
||||||
|
if @options.onMessage
|
||||||
|
@options.onMessage(pipes)
|
||||||
|
|
||||||
|
@ws.onclose = (e) =>
|
||||||
|
@log.debug 'close websocket connection'
|
||||||
|
if @options.onClose
|
||||||
|
@options.onClose(e)
|
||||||
|
|
||||||
|
@ws.onerror = (e) =>
|
||||||
|
@log.debug 'onerror', e
|
||||||
|
if @options.onError
|
||||||
|
@options.onError(e)
|
||||||
|
|
||||||
|
close: =>
|
||||||
|
@ws.close()
|
||||||
|
|
||||||
|
reconnect: =>
|
||||||
|
@ws.close()
|
||||||
|
@connect()
|
||||||
|
|
||||||
|
send: (event, data = {}) =>
|
||||||
|
@log.debug 'send', event, data
|
||||||
|
msg = JSON.stringify
|
||||||
|
event: event
|
||||||
|
data: data
|
||||||
|
@ws.send msg
|
||||||
|
|
||||||
|
class ZammadChat extends Base
|
||||||
defaults:
|
defaults:
|
||||||
chatId: undefined
|
chatId: undefined
|
||||||
show: true
|
show: true
|
||||||
|
@ -21,9 +148,14 @@ do($ = window.jQuery, window) ->
|
||||||
buttonClass: 'open-zammad-chat'
|
buttonClass: 'open-zammad-chat'
|
||||||
inactiveClass: 'is-inactive'
|
inactiveClass: 'is-inactive'
|
||||||
title: '<strong>Chat</strong> with us!'
|
title: '<strong>Chat</strong> with us!'
|
||||||
idleTimeout: 4
|
idleTimeout: 8
|
||||||
inactiveTimeout: 20
|
idleTimeoutIntervallCheck: 0.5
|
||||||
|
inactiveTimeout: 8
|
||||||
|
inactiveTimeoutIntervallCheck: 0.5
|
||||||
|
waitingListTimeout: 4
|
||||||
|
waitingListTimeoutIntervallCheck: 0.5
|
||||||
|
|
||||||
|
logPrefix: 'chat'
|
||||||
_messageCount: 0
|
_messageCount: 0
|
||||||
isOpen: true
|
isOpen: true
|
||||||
blinkOnlineInterval: null
|
blinkOnlineInterval: null
|
||||||
|
@ -35,7 +167,6 @@ do($ = window.jQuery, window) ->
|
||||||
isTyping: false
|
isTyping: false
|
||||||
state: 'offline'
|
state: 'offline'
|
||||||
initialQueueDelay: 10000
|
initialQueueDelay: 10000
|
||||||
wsReconnectEnable: true
|
|
||||||
translations:
|
translations:
|
||||||
de:
|
de:
|
||||||
'<strong>Chat</strong> with us!': '<strong>Chat</strong> mit uns!'
|
'<strong>Chat</strong> with us!': '<strong>Chat</strong> mit uns!'
|
||||||
|
@ -57,22 +188,17 @@ do($ = window.jQuery, window) ->
|
||||||
T: (string, items...) =>
|
T: (string, items...) =>
|
||||||
if @options.lang && @options.lang isnt 'en'
|
if @options.lang && @options.lang isnt 'en'
|
||||||
if !@translations[@options.lang]
|
if !@translations[@options.lang]
|
||||||
@log 'notice', "Translation '#{@options.lang}' needed!"
|
@log.notice "Translation '#{@options.lang}' needed!"
|
||||||
else
|
else
|
||||||
translations = @translations[@options.lang]
|
translations = @translations[@options.lang]
|
||||||
if !translations[string]
|
if !translations[string]
|
||||||
@log 'notice', "Translation needed for '#{string}'"
|
@log.notice "Translation needed for '#{string}'"
|
||||||
string = translations[string] || string
|
string = translations[string] || string
|
||||||
if items
|
if items
|
||||||
for item in items
|
for item in items
|
||||||
string = string.replace(/%s/, item)
|
string = string.replace(/%s/, item)
|
||||||
string
|
string
|
||||||
|
|
||||||
log: (level, string...) =>
|
|
||||||
return if !@options.debug && level is 'debug'
|
|
||||||
string.unshift(level)
|
|
||||||
console.log.apply console, string
|
|
||||||
|
|
||||||
view: (name) =>
|
view: (name) =>
|
||||||
return (options) =>
|
return (options) =>
|
||||||
if !options
|
if !options
|
||||||
|
@ -86,19 +212,20 @@ do($ = window.jQuery, window) ->
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
@options = $.extend {}, @defaults, options
|
@options = $.extend {}, @defaults, options
|
||||||
|
super(@options)
|
||||||
|
|
||||||
# check prerequisites
|
# check prerequisites
|
||||||
if !$
|
if !$
|
||||||
@state = 'unsupported'
|
@state = 'unsupported'
|
||||||
@log 'notice', 'Chat: no jquery found!'
|
@log.notice 'Chat: no jquery found!'
|
||||||
return
|
return
|
||||||
if !window.WebSocket or !sessionStorage
|
if !window.WebSocket or !sessionStorage
|
||||||
@state = 'unsupported'
|
@state = 'unsupported'
|
||||||
@log 'notice', 'Chat: Browser not supported!'
|
@log.notice 'Chat: Browser not supported!'
|
||||||
return
|
return
|
||||||
if !@options.chatId
|
if !@options.chatId
|
||||||
@state = 'unsupported'
|
@state = 'unsupported'
|
||||||
@log 'error', 'Chat: need chatId as option!'
|
@log.error 'Chat: need chatId as option!'
|
||||||
return
|
return
|
||||||
|
|
||||||
# detect language
|
# detect language
|
||||||
|
@ -106,8 +233,19 @@ do($ = window.jQuery, window) ->
|
||||||
@options.lang = $('html').attr('lang')
|
@options.lang = $('html').attr('lang')
|
||||||
if @options.lang
|
if @options.lang
|
||||||
@options.lang = @options.lang.replace(/-.+?$/, '') # replace "-xx" of xx-xx
|
@options.lang = @options.lang.replace(/-.+?$/, '') # replace "-xx" of xx-xx
|
||||||
@log 'debug', "lang: #{@options.lang}"
|
@log.debug "lang: #{@options.lang}"
|
||||||
|
|
||||||
|
@loadCss()
|
||||||
|
|
||||||
|
@io = new Io(@options)
|
||||||
|
@io.set(
|
||||||
|
onOpen: @render
|
||||||
|
onClose: @hide
|
||||||
|
onMessage: @onWebSocketMessage
|
||||||
|
)
|
||||||
|
@io.connect()
|
||||||
|
|
||||||
|
render: =>
|
||||||
@el = $(@view('chat')(
|
@el = $(@view('chat')(
|
||||||
title: @options.title
|
title: @options.title
|
||||||
))
|
))
|
||||||
|
@ -124,10 +262,20 @@ do($ = window.jQuery, window) ->
|
||||||
@input.on
|
@input.on
|
||||||
keydown: @checkForEnter
|
keydown: @checkForEnter
|
||||||
input: @onInput
|
input: @onInput
|
||||||
|
$(window).on('beforeunload', =>
|
||||||
|
@onLeaveTemporary()
|
||||||
|
)
|
||||||
|
@setAgentOnlineState 'online'
|
||||||
|
|
||||||
@wsConnect()
|
@log.debug 'widget rendered'
|
||||||
|
|
||||||
@loadCss()
|
@startTimeoutObservers()
|
||||||
|
@idleTimeout.start()
|
||||||
|
|
||||||
|
# get current chat status
|
||||||
|
@sessionId = sessionStorage.getItem('sessionId')
|
||||||
|
@send 'chat_status_customer',
|
||||||
|
session_id: @sessionId
|
||||||
|
|
||||||
checkForEnter: (event) =>
|
checkForEnter: (event) =>
|
||||||
if not event.shiftKey and event.keyCode is 13
|
if not event.shiftKey and event.keyCode is 13
|
||||||
|
@ -136,22 +284,16 @@ do($ = window.jQuery, window) ->
|
||||||
|
|
||||||
send: (event, data = {}) =>
|
send: (event, data = {}) =>
|
||||||
data.chat_id = @options.chatId
|
data.chat_id = @options.chatId
|
||||||
@log 'debug', 'ws:send', event, data
|
@io.send(event, data)
|
||||||
pipe = JSON.stringify
|
|
||||||
event: event
|
|
||||||
data: data
|
|
||||||
@ws.send pipe
|
|
||||||
|
|
||||||
onWebSocketMessage: (e) =>
|
|
||||||
pipes = JSON.parse( e.data )
|
|
||||||
|
|
||||||
|
onWebSocketMessage: (pipes) =>
|
||||||
for pipe in pipes
|
for pipe in pipes
|
||||||
@log 'debug', 'ws:onmessage', pipe
|
@log.debug 'ws:onmessage', pipe
|
||||||
switch pipe.event
|
switch pipe.event
|
||||||
when 'chat_error'
|
when 'chat_error'
|
||||||
@log 'notice', pipe.data
|
@log.notice pipe.data
|
||||||
if pipe.data && pipe.data.state is 'chat_disabled'
|
if pipe.data && pipe.data.state is 'chat_disabled'
|
||||||
@wsClose()
|
@destroy(hide: true)
|
||||||
when 'chat_session_message'
|
when 'chat_session_message'
|
||||||
return if pipe.data.self_written
|
return if pipe.data.self_written
|
||||||
@receiveMessage pipe.data
|
@receiveMessage pipe.data
|
||||||
|
@ -171,38 +313,35 @@ do($ = window.jQuery, window) ->
|
||||||
when 'online'
|
when 'online'
|
||||||
@sessionId = undefined
|
@sessionId = undefined
|
||||||
@onReady()
|
@onReady()
|
||||||
@log 'debug', 'Zammad Chat: ready'
|
|
||||||
when 'offline'
|
when 'offline'
|
||||||
@onError 'Zammad Chat: No agent online'
|
@onError 'Zammad Chat: No agent online'
|
||||||
@state = 'off'
|
@state = 'off'
|
||||||
@hide()
|
@destroy(hide: true)
|
||||||
@wsClose()
|
|
||||||
when 'chat_disabled'
|
when 'chat_disabled'
|
||||||
@onError 'Zammad Chat: Chat is disabled'
|
@onError 'Zammad Chat: Chat is disabled'
|
||||||
@state = 'off'
|
@state = 'off'
|
||||||
@hide()
|
@destroy(hide: true)
|
||||||
@wsClose()
|
|
||||||
when 'no_seats_available'
|
when 'no_seats_available'
|
||||||
@onError "Zammad Chat: Too many clients in queue. Clients in queue: #{pipe.data.queue}"
|
@onError "Zammad Chat: Too many clients in queue. Clients in queue: #{pipe.data.queue}"
|
||||||
@state = 'off'
|
@state = 'off'
|
||||||
@hide()
|
@destroy(hide: true)
|
||||||
@wsClose()
|
|
||||||
when 'reconnect'
|
when 'reconnect'
|
||||||
@log 'debug', 'old messages', pipe.data.session
|
@log.debug 'old messages', pipe.data.session
|
||||||
@reopenSession pipe.data
|
@reopenSession pipe.data
|
||||||
|
|
||||||
onReady: =>
|
onReady: =>
|
||||||
|
@log.debug 'widget ready for use'
|
||||||
$(".#{ @options.buttonClass }").click(@open).removeClass(@inactiveClass)
|
$(".#{ @options.buttonClass }").click(@open).removeClass(@inactiveClass)
|
||||||
|
|
||||||
if @options.show
|
if @options.show
|
||||||
@show()
|
@show()
|
||||||
|
|
||||||
onError: (message) =>
|
onError: (message) =>
|
||||||
@log 'debug', message
|
@log.debug message
|
||||||
$(".#{ @options.buttonClass }").hide()
|
$(".#{ @options.buttonClass }").hide()
|
||||||
|
|
||||||
reopenSession: (data) =>
|
reopenSession: (data) =>
|
||||||
@inactiveTimeoutStart()
|
@inactiveTimeout.start()
|
||||||
|
|
||||||
unfinishedMessage = sessionStorage.getItem 'unfinished_message'
|
unfinishedMessage = sessionStorage.getItem 'unfinished_message'
|
||||||
|
|
||||||
|
@ -246,7 +385,7 @@ do($ = window.jQuery, window) ->
|
||||||
@isTyping = new Date()
|
@isTyping = new Date()
|
||||||
@send 'chat_session_typing',
|
@send 'chat_session_typing',
|
||||||
session_id: @sessionId
|
session_id: @sessionId
|
||||||
@inactiveTimeoutStart()
|
@inactiveTimeout.start()
|
||||||
|
|
||||||
onSubmit: (event) =>
|
onSubmit: (event) =>
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
@ -256,7 +395,7 @@ do($ = window.jQuery, window) ->
|
||||||
message = @input.val()
|
message = @input.val()
|
||||||
return if !message
|
return if !message
|
||||||
|
|
||||||
@inactiveTimeoutStart()
|
@inactiveTimeout.start()
|
||||||
|
|
||||||
sessionStorage.removeItem 'unfinished_message'
|
sessionStorage.removeItem 'unfinished_message'
|
||||||
|
|
||||||
|
@ -286,7 +425,7 @@ do($ = window.jQuery, window) ->
|
||||||
session_id: @sessionId
|
session_id: @sessionId
|
||||||
|
|
||||||
receiveMessage: (data) =>
|
receiveMessage: (data) =>
|
||||||
@inactiveTimeoutStart()
|
@inactiveTimeout.start()
|
||||||
|
|
||||||
# hide writing indicator
|
# hide writing indicator
|
||||||
@onAgentTypingEnd()
|
@onAgentTypingEnd()
|
||||||
|
@ -305,6 +444,7 @@ do($ = window.jQuery, window) ->
|
||||||
@scrollToBottom()
|
@scrollToBottom()
|
||||||
|
|
||||||
open: =>
|
open: =>
|
||||||
|
@log.debug 'open widget'
|
||||||
if @isOpen
|
if @isOpen
|
||||||
@show()
|
@show()
|
||||||
|
|
||||||
|
@ -322,12 +462,14 @@ do($ = window.jQuery, window) ->
|
||||||
@isOpen = true
|
@isOpen = true
|
||||||
|
|
||||||
if !@sessionId
|
if !@sessionId
|
||||||
@sessionInit()
|
@send('chat_session_init')
|
||||||
|
|
||||||
onOpenAnimationEnd: =>
|
onOpenAnimationEnd: =>
|
||||||
@idleTimeoutStop()
|
@idleTimeout.stop()
|
||||||
|
|
||||||
close: (event) =>
|
close: (event) =>
|
||||||
|
@log.debug 'close widget'
|
||||||
|
|
||||||
return @state if @state is 'off' or @state is 'unsupported'
|
return @state if @state is 'off' or @state is 'unsupported'
|
||||||
event.stopPropagation() if event
|
event.stopPropagation() if event
|
||||||
|
|
||||||
|
@ -339,7 +481,8 @@ do($ = window.jQuery, window) ->
|
||||||
session_id: @sessionId
|
session_id: @sessionId
|
||||||
|
|
||||||
# stop timer
|
# stop timer
|
||||||
@inactiveTimeoutStop()
|
@inactiveTimeout.stop()
|
||||||
|
@waitingListTimeout.stop()
|
||||||
|
|
||||||
# delete input store
|
# delete input store
|
||||||
sessionStorage.removeItem 'unfinished_message'
|
sessionStorage.removeItem 'unfinished_message'
|
||||||
|
@ -360,13 +503,19 @@ do($ = window.jQuery, window) ->
|
||||||
|
|
||||||
onCloseAnimationEnd: =>
|
onCloseAnimationEnd: =>
|
||||||
@el.removeClass('zammad-chat-is-visible')
|
@el.removeClass('zammad-chat-is-visible')
|
||||||
@disconnect()
|
|
||||||
|
@showLoader()
|
||||||
|
@el.find('.zammad-chat-welcome').removeClass('zammad-chat-is-hidden')
|
||||||
|
@el.find('.zammad-chat-agent').addClass('zammad-chat-is-hidden')
|
||||||
|
@el.find('.zammad-chat-agent-status').addClass('zammad-chat-is-hidden')
|
||||||
|
|
||||||
@isOpen = false
|
@isOpen = false
|
||||||
|
|
||||||
# restart connection
|
# restart connection
|
||||||
@onWebSocketOpen()
|
@io.reconnect()
|
||||||
|
|
||||||
hide: ->
|
hide: ->
|
||||||
|
if @el
|
||||||
@el.removeClass('zammad-chat-is-shown')
|
@el.removeClass('zammad-chat-is-shown')
|
||||||
|
|
||||||
show: ->
|
show: ->
|
||||||
|
@ -397,6 +546,9 @@ do($ = window.jQuery, window) ->
|
||||||
# delay initial queue position, show connecting first
|
# delay initial queue position, show connecting first
|
||||||
show = =>
|
show = =>
|
||||||
@onQueue data
|
@onQueue data
|
||||||
|
console.log('onQueueScreen')
|
||||||
|
@waitingListTimeout.start()
|
||||||
|
|
||||||
if @initialQueueDelay && !@onInitialQueueDelayId
|
if @initialQueueDelay && !@onInitialQueueDelayId
|
||||||
@onInitialQueueDelayId = setTimeout(show, @initialQueueDelay)
|
@onInitialQueueDelayId = setTimeout(show, @initialQueueDelay)
|
||||||
return
|
return
|
||||||
|
@ -409,7 +561,7 @@ do($ = window.jQuery, window) ->
|
||||||
show()
|
show()
|
||||||
|
|
||||||
onQueue: (data) =>
|
onQueue: (data) =>
|
||||||
@log 'notice', 'onQueue', data.position
|
@log.notice 'onQueue', data.position
|
||||||
@inQueue = true
|
@inQueue = true
|
||||||
|
|
||||||
@el.find('.zammad-chat-body').html @view('waiting')
|
@el.find('.zammad-chat-body').html @view('waiting')
|
||||||
|
@ -432,6 +584,11 @@ do($ = window.jQuery, window) ->
|
||||||
onAgentTypingEnd: =>
|
onAgentTypingEnd: =>
|
||||||
@el.find('.zammad-chat-message--typing').remove()
|
@el.find('.zammad-chat-message--typing').remove()
|
||||||
|
|
||||||
|
onLeaveTemporary: =>
|
||||||
|
return if !@sessionId
|
||||||
|
@send 'chat_session_leave_temporary',
|
||||||
|
session_id: @sessionId
|
||||||
|
|
||||||
maybeAddTimestamp: ->
|
maybeAddTimestamp: ->
|
||||||
timestamp = Date.now()
|
timestamp = Date.now()
|
||||||
|
|
||||||
|
@ -470,59 +627,38 @@ do($ = window.jQuery, window) ->
|
||||||
scrollToBottom: ->
|
scrollToBottom: ->
|
||||||
@el.find('.zammad-chat-body').scrollTop($('.zammad-chat-body').prop('scrollHeight'))
|
@el.find('.zammad-chat-body').scrollTop($('.zammad-chat-body').prop('scrollHeight'))
|
||||||
|
|
||||||
sessionInit: ->
|
|
||||||
@send('chat_session_init')
|
|
||||||
|
|
||||||
detectHost: ->
|
detectHost: ->
|
||||||
protocol = 'ws://'
|
protocol = 'ws://'
|
||||||
if window.location.protocol is 'https:'
|
if window.location.protocol is 'https:'
|
||||||
protocol = 'wss://'
|
protocol = 'wss://'
|
||||||
@options.host = "#{ protocol }#{ scriptHost }/ws"
|
@options.host = "#{ protocol }#{ scriptHost }/ws"
|
||||||
|
|
||||||
wsConnect: =>
|
destroy: (params = {}) =>
|
||||||
@detectHost() if !@options.host
|
@log.debug 'destroy widget'
|
||||||
|
console.log('el', @el)
|
||||||
|
if params.hide
|
||||||
|
if @el
|
||||||
|
@el.remove()
|
||||||
|
@wsReconnectStop()
|
||||||
|
@io.close()
|
||||||
|
|
||||||
@log 'debug', "Connecting to #{@options.host}"
|
wsReconnectStart: =>
|
||||||
@ws = new window.WebSocket("#{@options.host}")
|
@wsReconnectStop()
|
||||||
@ws.onopen = @onWebSocketOpen
|
|
||||||
|
|
||||||
@ws.onmessage = @onWebSocketMessage
|
|
||||||
|
|
||||||
@ws.onclose = (e) =>
|
|
||||||
@log 'debug', 'close websocket connection'
|
|
||||||
if @wsReconnectEnable
|
|
||||||
@reconnect()
|
|
||||||
|
|
||||||
@ws.onerror = (e) =>
|
|
||||||
@log 'debug', 'ws:onerror', e
|
|
||||||
|
|
||||||
wsClose: =>
|
|
||||||
@wsReconnectEnable = false
|
|
||||||
@ws.close()
|
|
||||||
|
|
||||||
wsReconnect: =>
|
|
||||||
if @reconnectDelayId
|
if @reconnectDelayId
|
||||||
clearTimeout(@reconnectDelayId)
|
clearTimeout(@reconnectDelayId)
|
||||||
@reconnectDelayId = setTimeout(@wsConnect, 5000)
|
@reconnectDelayId = setTimeout(@io.connect(), 5000)
|
||||||
|
|
||||||
onWebSocketOpen: =>
|
wsReconnectStop: =>
|
||||||
@idleTimeoutStart()
|
if @reconnectDelayId
|
||||||
@sessionId = sessionStorage.getItem('sessionId')
|
clearTimeout(@reconnectDelayId)
|
||||||
@log 'debug', 'ws connected'
|
|
||||||
|
|
||||||
@send 'chat_status_customer',
|
|
||||||
session_id: @sessionId
|
|
||||||
|
|
||||||
@setAgentOnlineState 'online'
|
|
||||||
|
|
||||||
reconnect: =>
|
reconnect: =>
|
||||||
# set status to connecting
|
# set status to connecting
|
||||||
@log 'notice', 'reconnecting'
|
@log.notice 'reconnecting'
|
||||||
@disableInput()
|
@disableInput()
|
||||||
@lastAddedType = 'status'
|
@lastAddedType = 'status'
|
||||||
@setAgentOnlineState 'connecting'
|
@setAgentOnlineState 'connecting'
|
||||||
@addStatus @T('Connection lost')
|
@addStatus @T('Connection lost')
|
||||||
@wsReconnect()
|
|
||||||
|
|
||||||
onConnectionReestablished: =>
|
onConnectionReestablished: =>
|
||||||
# set status back to online
|
# set status back to online
|
||||||
|
@ -534,13 +670,7 @@ do($ = window.jQuery, window) ->
|
||||||
@addStatus @T('Chat closed by %s', data.realname)
|
@addStatus @T('Chat closed by %s', data.realname)
|
||||||
@disableInput()
|
@disableInput()
|
||||||
@setAgentOnlineState 'offline'
|
@setAgentOnlineState 'offline'
|
||||||
@inactiveTimeoutStop()
|
@inactiveTimeout.stop()
|
||||||
|
|
||||||
disconnect: ->
|
|
||||||
@showLoader()
|
|
||||||
@el.find('.zammad-chat-welcome').removeClass('zammad-chat-is-hidden')
|
|
||||||
@el.find('.zammad-chat-agent').addClass('zammad-chat-is-hidden')
|
|
||||||
@el.find('.zammad-chat-agent-status').addClass('zammad-chat-is-hidden')
|
|
||||||
|
|
||||||
setSessionId: (id) =>
|
setSessionId: (id) =>
|
||||||
@sessionId = id
|
@sessionId = id
|
||||||
|
@ -573,8 +703,12 @@ do($ = window.jQuery, window) ->
|
||||||
|
|
||||||
@setAgentOnlineState 'online'
|
@setAgentOnlineState 'online'
|
||||||
|
|
||||||
showTimeout: ->
|
@waitingListTimeout.stop()
|
||||||
@el.find('.zammad-chat-body').html @view('timeout')
|
@idleTimeout.stop()
|
||||||
|
@inactiveTimeout.start()
|
||||||
|
|
||||||
|
showCustomerTimeout: ->
|
||||||
|
@el.find('.zammad-chat-body').html @view('customer_timeout')
|
||||||
agent: @agent.name
|
agent: @agent.name
|
||||||
delay: @options.inactiveTimeout
|
delay: @options.inactiveTimeout
|
||||||
@close()
|
@close()
|
||||||
|
@ -582,6 +716,14 @@ do($ = window.jQuery, window) ->
|
||||||
location.reload()
|
location.reload()
|
||||||
@el.find('.js-restart').click reload
|
@el.find('.js-restart').click reload
|
||||||
|
|
||||||
|
showWaitingListTimeout: ->
|
||||||
|
@el.find('.zammad-chat-body').html @view('waiting_list_timeout')
|
||||||
|
delay: @options.watingListTimeout
|
||||||
|
@close()
|
||||||
|
reload = ->
|
||||||
|
location.reload()
|
||||||
|
@el.find('.js-restart').click reload
|
||||||
|
|
||||||
showLoader: ->
|
showLoader: ->
|
||||||
@el.find('.zammad-chat-body').html @view('loader')()
|
@el.find('.zammad-chat-body').html @view('loader')()
|
||||||
|
|
||||||
|
@ -603,42 +745,47 @@ do($ = window.jQuery, window) ->
|
||||||
.replace(/\/ws/i, '')
|
.replace(/\/ws/i, '')
|
||||||
url += '/assets/chat/chat.css'
|
url += '/assets/chat/chat.css'
|
||||||
|
|
||||||
@log 'debug', "load css from '#{url}'"
|
@log.debug "load css from '#{url}'"
|
||||||
styles = "@import url('#{url}');"
|
styles = "@import url('#{url}');"
|
||||||
newSS = document.createElement('link')
|
newSS = document.createElement('link')
|
||||||
newSS.rel = 'stylesheet'
|
newSS.rel = 'stylesheet'
|
||||||
newSS.href = 'data:text/css,' + escape(styles)
|
newSS.href = 'data:text/css,' + escape(styles)
|
||||||
document.getElementsByTagName('head')[0].appendChild(newSS)
|
document.getElementsByTagName('head')[0].appendChild(newSS)
|
||||||
|
|
||||||
inactiveTimeoutStart: =>
|
startTimeoutObservers: =>
|
||||||
@inactiveTimeoutStop()
|
@idleTimeout = new Timeout(
|
||||||
delay = =>
|
logPrefix: 'idleTimeout'
|
||||||
@log 'debug', "Inactive timeout of #{@options.inactiveTimeout} minutes, show timeout screen.", new Date
|
debug: @options.debug
|
||||||
|
timeout: @options.idleTimeout
|
||||||
|
timeoutIntervallCheck: @options.idleTimeoutIntervallCheck
|
||||||
|
callback: =>
|
||||||
|
@log.debug 'Idle timeout reached, hide widget', new Date
|
||||||
|
@state = 'off'
|
||||||
|
@destroy(hide: true)
|
||||||
|
)
|
||||||
|
@inactiveTimeout = new Timeout(
|
||||||
|
logPrefix: 'inactiveTimeout'
|
||||||
|
debug: @options.debug
|
||||||
|
timeout: @options.inactiveTimeout
|
||||||
|
timeoutIntervallCheck: @options.inactiveTimeoutIntervallCheck
|
||||||
|
callback: =>
|
||||||
|
@log.debug 'Inactive timeout reached, show timeout screen.', new Date
|
||||||
@state = 'off'
|
@state = 'off'
|
||||||
@setAgentOnlineState 'offline'
|
@setAgentOnlineState 'offline'
|
||||||
@showTimeout()
|
@showCustomerTimeout()
|
||||||
@wsClose()
|
@destroy(hide:false)
|
||||||
@log 'debug', "Start inactive timeout in #{@options.inactiveTimeout} minutes", new Date
|
)
|
||||||
@inactiveTimeoutStopDelayId = setTimeout(delay, @options.inactiveTimeout * 1000 * 60)
|
@waitingListTimeout = new Timeout(
|
||||||
|
logPrefix: 'waitingListTimeout'
|
||||||
inactiveTimeoutStop: =>
|
debug: @options.debug
|
||||||
return if !@inactiveTimeoutStopDelayId
|
timeout: @options.waitingListTimeout
|
||||||
@log 'debug', "Stop inactive timeout of #{@options.inactiveTimeout} minutes at", new Date
|
timeoutIntervallCheck: @options.waitingListTimeoutIntervallCheck
|
||||||
clearTimeout(@inactiveTimeoutStopDelayId)
|
callback: =>
|
||||||
|
@log.debug 'Waiting list timeout reached, show timeout screen.', new Date
|
||||||
idleTimeoutStart: =>
|
|
||||||
@idleTimeoutStop()
|
|
||||||
delay = =>
|
|
||||||
@log 'debug', "Idle timeout of #{@options.idleTimeout} minutes, hide widget", new Date
|
|
||||||
@state = 'off'
|
@state = 'off'
|
||||||
@hide()
|
@setAgentOnlineState 'offline'
|
||||||
@wsClose()
|
@showWaitingListTimeout()
|
||||||
@log 'debug', "Start idle timeout in #{@options.idleTimeout} minutes", new Date
|
@destroy(hide:false)
|
||||||
@idleTimeoutStopDelayId = setTimeout(delay, @options.idleTimeout * 1000 * 60)
|
)
|
||||||
|
|
||||||
idleTimeoutStop: =>
|
|
||||||
return if !@idleTimeoutStopDelayId
|
|
||||||
@log 'debug', "Stop idle timeout of #{@options.idleTimeout} minutes at", new Date
|
|
||||||
clearTimeout(@idleTimeoutStopDelayId)
|
|
||||||
|
|
||||||
window.ZammadChat = ZammadChat
|
window.ZammadChat = ZammadChat
|
||||||
|
|
File diff suppressed because it is too large
Load diff
3
public/assets/chat/chat.min.js
vendored
3
public/assets/chat/chat.min.js
vendored
File diff suppressed because one or more lines are too long
7
public/assets/chat/views/waiting_list_timeout.eco
Normal file
7
public/assets/chat/views/waiting_list_timeout.eco
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<div class="zammad-chat-modal">
|
||||||
|
<div class="zammad-chat-modal-text">
|
||||||
|
<%- @T('We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you!') %>
|
||||||
|
<br>
|
||||||
|
<div class="zammad-chat-button js-restart"<%= " style='background: #{ @background }'" if @background %>><%- @T('Start new conversation') %></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -25,6 +25,7 @@
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
box-shadow: 0 3px 10px rgba(0,0,0,.3);
|
box-shadow: 0 3px 10px rgba(0,0,0,.3);
|
||||||
|
width: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings input {
|
.settings input {
|
||||||
|
@ -39,6 +40,12 @@
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table td.log {
|
||||||
|
text-align: left;
|
||||||
|
padding-right: 0;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
@ -73,8 +80,8 @@
|
||||||
<div class="settings">
|
<div class="settings">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
|
||||||
<td><h2>Settings</h2>
|
<td><h2>Settings</h2>
|
||||||
|
<td>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<input id="flat" type="checkbox" data-option="flat">
|
<input id="flat" type="checkbox" data-option="flat">
|
||||||
|
@ -100,6 +107,11 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<td><button class="open-zammad-chat">Open Chat</button>
|
<td><button class="open-zammad-chat">Open Chat</button>
|
||||||
|
<tr>
|
||||||
|
<td class="log"><h2>Log</h2>
|
||||||
|
<td>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="log js-chatLogDisplay">
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -113,7 +125,13 @@
|
||||||
debug: true,
|
debug: true,
|
||||||
background: '#494d52',
|
background: '#494d52',
|
||||||
flat: true,
|
flat: true,
|
||||||
shown: false
|
shown: false,
|
||||||
|
idleTimeout: 1,
|
||||||
|
idleTimeoutIntervallCheck: 0.5,
|
||||||
|
inactiveTimeout: 2,
|
||||||
|
inactiveTimeoutIntervallCheck: 0.5,
|
||||||
|
waitingListTimeout: 1.2,
|
||||||
|
waitingListTimeoutIntervallCheck: 0.5,
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.settings :input').on({
|
$('.settings :input').on({
|
||||||
|
|
|
@ -257,6 +257,7 @@ EventMachine.run {
|
||||||
}
|
}
|
||||||
|
|
||||||
elsif data['event']
|
elsif data['event']
|
||||||
|
log 'notice', "execute event '#{data['event']}'", client_id
|
||||||
message = Sessions::Event.run(data['event'], data, @clients[client_id][:session], client_id)
|
message = Sessions::Event.run(data['event'], data, @clients[client_id][:session], client_id)
|
||||||
if message
|
if message
|
||||||
websocket_send(client_id, message)
|
websocket_send(client_id, message)
|
||||||
|
|
|
@ -1,128 +1,428 @@
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
# rubocop:disable all
|
|
||||||
require 'browser_test_helper'
|
require 'browser_test_helper'
|
||||||
|
|
||||||
class ChatTest < TestCase
|
class ChatTest < TestCase
|
||||||
def test_websocket
|
|
||||||
return # TODO: temp disable
|
def test_basic
|
||||||
message = 'message 1äöüß ' + rand(99_999_999_999_999_999).to_s
|
agent = browser_instance
|
||||||
tests = [
|
login(
|
||||||
{
|
browser: agent,
|
||||||
name: 'start',
|
username: 'master@example.com',
|
||||||
instance1: browser_instance,
|
password: 'test',
|
||||||
instance2: browser_instance,
|
|
||||||
instance1_username: 'master@example.com',
|
|
||||||
instance1_password: 'test',
|
|
||||||
instance2_username: 'agent1@example.com',
|
|
||||||
instance2_password: 'test',
|
|
||||||
url: browser_url,
|
url: browser_url,
|
||||||
action: [
|
)
|
||||||
{
|
tasks_close_all(
|
||||||
where: :instance1,
|
browser: agent,
|
||||||
execute: 'check',
|
)
|
||||||
css: '#login',
|
|
||||||
result: false,
|
# disable chat
|
||||||
},
|
click(
|
||||||
{
|
browser: agent,
|
||||||
where: :instance2,
|
css: 'a[href="#manage"]',
|
||||||
execute: 'check',
|
)
|
||||||
css: '#login',
|
click(
|
||||||
result: false,
|
browser: agent,
|
||||||
},
|
css: 'a[href="#channels/chat"]',
|
||||||
{
|
)
|
||||||
execute: 'wait',
|
switch(
|
||||||
value: 1,
|
browser: agent,
|
||||||
},
|
css: '#content .js-chatSetting',
|
||||||
{
|
type: 'off',
|
||||||
where: :instance1,
|
)
|
||||||
execute: 'click',
|
sleep 25 # wait for rerendering
|
||||||
css: '#chat_toogle',
|
click(
|
||||||
},
|
browser: agent,
|
||||||
{
|
css: 'a[href="#customer_chat"]',
|
||||||
execute: 'wait',
|
)
|
||||||
value: 8,
|
match(
|
||||||
},
|
browser: agent,
|
||||||
{
|
css: '.active.content',
|
||||||
where: :instance1,
|
value: 'disabled',
|
||||||
execute: 'click',
|
)
|
||||||
css: '#chat_toogle',
|
|
||||||
},
|
customer = browser_instance
|
||||||
{
|
location(
|
||||||
execute: 'wait',
|
browser: customer,
|
||||||
value: 4,
|
url: "#{browser_url}/assets/chat/znuny.html",
|
||||||
},
|
)
|
||||||
{
|
sleep 4
|
||||||
where: :instance2,
|
exists_not(
|
||||||
execute: 'click',
|
browser: customer,
|
||||||
css: '#chat_toogle',
|
css: '.zammad-chat',
|
||||||
},
|
)
|
||||||
{
|
match(
|
||||||
where: :instance1,
|
browser: customer,
|
||||||
execute: 'click',
|
css: '.settings',
|
||||||
css: '#chat_toogle',
|
value: '{"state":"chat_disabled"}',
|
||||||
},
|
)
|
||||||
{
|
click(
|
||||||
execute: 'wait',
|
browser: agent,
|
||||||
value: 2,
|
css: 'a[href="#manage"]',
|
||||||
},
|
)
|
||||||
{
|
click(
|
||||||
where: :instance1,
|
browser: agent,
|
||||||
execute: 'set',
|
css: 'a[href="#channels/chat"]',
|
||||||
css: 'input[name="chat_message"]',
|
)
|
||||||
value: message,
|
switch(
|
||||||
},
|
browser: agent,
|
||||||
{
|
css: '#content .js-chatSetting',
|
||||||
where: :instance1,
|
type: 'on',
|
||||||
execute: 'send_key',
|
)
|
||||||
css: 'input[name="chat_message"]',
|
sleep 15 # wait for rerendering
|
||||||
value: :enter,
|
switch(
|
||||||
},
|
browser: agent,
|
||||||
{
|
css: '#navigation .js-switch',
|
||||||
execute: 'wait',
|
type: 'off',
|
||||||
value: 6,
|
)
|
||||||
},
|
click(
|
||||||
{
|
browser: agent,
|
||||||
where: :instance1,
|
css: 'a[href="#customer_chat"]',
|
||||||
execute: 'match',
|
wait: 2,
|
||||||
css: '#chat_log_container',
|
)
|
||||||
value: message,
|
match_not(
|
||||||
match_result: true,
|
browser: agent,
|
||||||
},
|
css: '.active.content',
|
||||||
{
|
value: 'disabled',
|
||||||
where: :instance2,
|
)
|
||||||
execute: 'match',
|
|
||||||
css: '#chat_log_container',
|
reload(
|
||||||
value: message,
|
browser: customer,
|
||||||
match_result: true,
|
)
|
||||||
},
|
sleep 4
|
||||||
{
|
exists_not(
|
||||||
where: :instance1,
|
browser: customer,
|
||||||
execute: 'navigate',
|
css: '.zammad-chat',
|
||||||
to: browser_url,
|
)
|
||||||
},
|
match_not(
|
||||||
{
|
browser: customer,
|
||||||
execute: 'wait',
|
css: '.settings',
|
||||||
value: 8,
|
value: '{"state":"chat_disabled"}',
|
||||||
},
|
)
|
||||||
{
|
match(
|
||||||
where: :instance1,
|
browser: customer,
|
||||||
execute: 'click',
|
css: '.settings',
|
||||||
css: '#chat_toogle',
|
value: '{"event":"chat_status_customer","data":{"state":"offline"}}',
|
||||||
},
|
)
|
||||||
{
|
click(
|
||||||
execute: 'wait',
|
browser: agent,
|
||||||
value: 8,
|
css: 'a[href="#customer_chat"]',
|
||||||
},
|
)
|
||||||
{
|
switch(
|
||||||
where: :instance1,
|
browser: agent,
|
||||||
execute: 'match',
|
css: '#navigation .js-switch',
|
||||||
css: '#chat_log_container',
|
type: 'on',
|
||||||
value: message,
|
)
|
||||||
match_result: true,
|
reload(
|
||||||
},
|
browser: customer,
|
||||||
],
|
)
|
||||||
},
|
watch_for(
|
||||||
]
|
browser: customer,
|
||||||
browser_double_test(tests)
|
css: '.zammad-chat',
|
||||||
|
timeout: 5,
|
||||||
|
)
|
||||||
|
match_not(
|
||||||
|
browser: customer,
|
||||||
|
css: '.settings',
|
||||||
|
value: '{"state":"chat_disabled"}',
|
||||||
|
)
|
||||||
|
match_not(
|
||||||
|
browser: customer,
|
||||||
|
css: '.settings',
|
||||||
|
value: '{"event":"chat_status_customer","data":{"state":"offline"}}',
|
||||||
|
)
|
||||||
|
match(
|
||||||
|
browser: customer,
|
||||||
|
css: '.settings',
|
||||||
|
value: '"data":{"state":"online"}',
|
||||||
|
)
|
||||||
|
|
||||||
|
# init chat
|
||||||
|
click(
|
||||||
|
browser: customer,
|
||||||
|
css: '.js-chat-open',
|
||||||
|
)
|
||||||
|
exists(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat-is-shown',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
value: '(waiting|warte)',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: agent,
|
||||||
|
css: '.js-chatMenuItem .counter',
|
||||||
|
value: '1',
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: customer,
|
||||||
|
css: '.js-chat-close',
|
||||||
|
)
|
||||||
|
watch_for_disappear(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
value: '(waiting|warte)',
|
||||||
|
)
|
||||||
|
watch_for_disappear(
|
||||||
|
browser: agent,
|
||||||
|
css: '.js-chatMenuItem .counter',
|
||||||
|
)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_basic_usecase1
|
||||||
|
agent = browser_instance
|
||||||
|
login(
|
||||||
|
browser: agent,
|
||||||
|
username: 'master@example.com',
|
||||||
|
password: 'test',
|
||||||
|
url: browser_url,
|
||||||
|
)
|
||||||
|
tasks_close_all(
|
||||||
|
browser: agent,
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: agent,
|
||||||
|
css: 'a[href="#customer_chat"]',
|
||||||
|
)
|
||||||
|
agent.find_elements( { css: '.active .chat-window .js-close' } ).each(&:click)
|
||||||
|
|
||||||
|
customer = browser_instance
|
||||||
|
location(
|
||||||
|
browser: customer,
|
||||||
|
url: "#{browser_url}/assets/chat/znuny.html",
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
timeout: 5,
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: customer,
|
||||||
|
css: '.js-chat-open',
|
||||||
|
)
|
||||||
|
exists(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat-is-shown',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
value: '(waiting|warte)',
|
||||||
|
)
|
||||||
|
|
||||||
|
click(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .js-acceptChat',
|
||||||
|
)
|
||||||
|
sleep 2
|
||||||
|
exists_not(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window .chat-status.is-modified',
|
||||||
|
)
|
||||||
|
set(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window .js-customerChatInput',
|
||||||
|
value: 'my name is me',
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window .js-send',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat .zammad-chat-agent-status',
|
||||||
|
value: 'online',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
value: 'my name is me',
|
||||||
|
)
|
||||||
|
set(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat .zammad-chat-input',
|
||||||
|
value: 'my name is customer',
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat .zammad-chat-send',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window',
|
||||||
|
value: 'my name is customer',
|
||||||
|
)
|
||||||
|
exists(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window .chat-status.is-modified',
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window .js-customerChatInput',
|
||||||
|
)
|
||||||
|
exists_not(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window .chat-status.is-modified',
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: customer,
|
||||||
|
css: '.js-chat-close',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window',
|
||||||
|
value: 'has left the conversation',
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_basic_usecase2
|
||||||
|
agent = browser_instance
|
||||||
|
login(
|
||||||
|
browser: agent,
|
||||||
|
username: 'master@example.com',
|
||||||
|
password: 'test',
|
||||||
|
url: browser_url,
|
||||||
|
)
|
||||||
|
tasks_close_all(
|
||||||
|
browser: agent,
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: agent,
|
||||||
|
css: 'a[href="#customer_chat"]',
|
||||||
|
)
|
||||||
|
agent.find_elements( { css: '.active .chat-window .js-close' } ).each(&:click)
|
||||||
|
|
||||||
|
customer = browser_instance
|
||||||
|
location(
|
||||||
|
browser: customer,
|
||||||
|
url: "#{browser_url}/assets/chat/znuny.html",
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
timeout: 5,
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: customer,
|
||||||
|
css: '.js-chat-open',
|
||||||
|
)
|
||||||
|
exists(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat-is-shown',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
value: '(waiting|warte)',
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .js-acceptChat',
|
||||||
|
)
|
||||||
|
sleep 2
|
||||||
|
exists_not(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window .chat-status.is-modified',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat .zammad-chat-agent-status',
|
||||||
|
value: 'online',
|
||||||
|
)
|
||||||
|
set(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat .zammad-chat-input',
|
||||||
|
value: 'my name is customer',
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat .zammad-chat-send',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window',
|
||||||
|
value: 'my name is customer',
|
||||||
|
)
|
||||||
|
exists(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window .chat-status.is-modified',
|
||||||
|
)
|
||||||
|
set(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window .js-customerChatInput',
|
||||||
|
value: 'my name is me',
|
||||||
|
)
|
||||||
|
exists_not(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window .chat-status.is-modified',
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window .js-send',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
value: 'my name is me',
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: agent,
|
||||||
|
css: '.active .chat-window .js-close',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat .zammad-chat-agent-status',
|
||||||
|
value: 'offline',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
value: 'Chat closed by',
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_timeouts
|
||||||
|
customer = browser_instance
|
||||||
|
location(
|
||||||
|
browser: customer,
|
||||||
|
url: "#{browser_url}/assets/chat/znuny.html",
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
timeout: 5,
|
||||||
|
)
|
||||||
|
watch_for_disappear(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
timeout: 75,
|
||||||
|
)
|
||||||
|
reload(
|
||||||
|
browser: customer,
|
||||||
|
)
|
||||||
|
exists(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
)
|
||||||
|
click(
|
||||||
|
browser: customer,
|
||||||
|
css: '.js-chat-open',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
value: '(waiting|warte)',
|
||||||
|
timeout: 35,
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: customer,
|
||||||
|
css: '.zammad-chat',
|
||||||
|
value: '(takes longer|dauert länger)',
|
||||||
|
timeout: 90,
|
||||||
|
)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue