test(HH-558): complete WebChannel sender display regression matrix (#141)

* chore(agent): baseline — uncommitted work from the local directory

* test(HH-558): cover widget sender display matrix

---------

Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
Rogee
2026-08-24 00:16:38 +08:00
committed by GitHub
co-authored by rogee
parent 391fe98ad6
commit 88ac30d0b8
5 changed files with 174 additions and 10 deletions
+34 -2
View File
@@ -8,6 +8,8 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"sync"
"testing"
@@ -416,7 +418,7 @@ func TestHub_SendToAccountSanitizesVisitorIdentity(t *testing.T) {
hub.subscribeClient(visitor.ID, accountRoomName(10))
visitor.SubscribedRooms[accountRoomName(10)] = true
hub.SendToAccount(10, []byte(`{"event":"message.created","data":{"content":"same reply","sender_type":"AgentBot","sender_id":7,"ai_takeover_active":true,"additional_attributes":{"agent_name":"Captain"}}}`))
hub.SendToAccount(10, []byte(`{"event":"message.created","data":{"content":"same reply","message_type":"outgoing","sender":{"available_name":"Captain","name":"Captain","agent_name":"Captain"},"sender_type":"AgentBot","sender_id":7,"ai_takeover_active":true,"additional_attributes":{"agent_name":"Captain","sender_name":"Captain"}}}`))
var agentMessage, visitorMessage []byte
select {
@@ -438,6 +440,31 @@ func TestHub_SendToAccountSanitizesVisitorIdentity(t *testing.T) {
assert.NotContains(t, string(visitorMessage), "Captain")
}
func TestEventDataForClient_VisitorMessageKeepsPublicFieldsOnly(t *testing.T) {
hub := NewHubSimple()
visitor := NewClient(2, 10, nil, hub)
visitor.IsContact = true
data := eventDataForClient(visitor, []byte(`{"event":"message.created","data":{"id":12,"content":"Dashboard reply","message_type":1,"conversation_id":42,"sender":{"available_name":"Captain","name":"Captain","agent_name":"Captain"},"sender_type":"AgentBot","sender_id":7,"ai_takeover_active":true,"additional_attributes":{"sender_name":"Captain","agent_name":"Captain"}}}`))
fixture, err := os.ReadFile(filepath.Join("..", "..", "..", "..", "frontend", "testdata", "visitor_ws_payload.json"))
require.NoError(t, err)
assert.JSONEq(t, string(fixture), string(data))
var payload map[string]any
require.NoError(t, json.Unmarshal(data, &payload))
assert.Equal(t, "message.created", payload["event"])
message, ok := payload["data"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "Dashboard reply", message["content"])
assert.Equal(t, float64(1), message["message_type"])
assert.Equal(t, float64(42), message["conversation_id"])
assert.NotContains(t, message, "sender")
assert.NotContains(t, message, "sender_type")
assert.NotContains(t, message, "sender_id")
assert.NotContains(t, message, "ai_takeover_active")
assert.Equal(t, map[string]any{}, message["additional_attributes"])
}
func TestHub_VisitorRoomAndClientDeliverySanitizeIdentity(t *testing.T) {
hub := NewHubSimple()
visitor := NewClient(2, 10, nil, hub)
@@ -809,7 +836,7 @@ func TestServeCable_WidgetReceivesTokenRoomEvent(t *testing.T) {
require.NoError(t, json.Unmarshal(confirmation, &confirm))
assert.Equal(t, ServerConfirmSubscribe, confirm.Type)
hub.SendToRoom(pubsubTokenRoomName("visitor-token"), []byte(`{"event":"message.created","data":{"id":12,"content":"Dashboard reply","message_type":1,"conversation_id":42}}`))
hub.SendToRoom(pubsubTokenRoomName("visitor-token"), []byte(`{"event":"message.created","data":{"id":12,"content":"Dashboard reply","message_type":1,"conversation_id":42,"sender":{"available_name":"Captain","name":"Captain"},"sender_type":"AgentBot","sender_id":7,"ai_takeover_active":true,"additional_attributes":{"sender_name":"Captain","agent_name":"Captain"}}}`))
_, message, err := conn.ReadMessage()
require.NoError(t, err)
var delivered struct {
@@ -825,6 +852,11 @@ func TestServeCable_WidgetReceivesTokenRoomEvent(t *testing.T) {
assert.Equal(t, "Dashboard reply", payload["content"])
assert.Equal(t, float64(1), payload["message_type"])
assert.Equal(t, float64(42), payload["conversation_id"])
assert.NotContains(t, payload, "sender")
assert.NotContains(t, payload, "sender_type")
assert.NotContains(t, payload, "sender_id")
assert.NotContains(t, payload, "ai_takeover_active")
assert.Equal(t, map[string]interface{}{}, payload["additional_attributes"])
}
func TestDashboardOutgoingReachesDashboardAndReconnectedWidget(t *testing.T) {
+8
View File
@@ -43,6 +43,14 @@ services:
- "8025:8025" # Web UI
# Reference: Chatwoot docker-compose includes mailhog for email testing
web-channel-test:
image: nginx:alpine
container_name: gochat-web-channel-test
ports:
- "8081:80"
volumes:
- ./web-channel-test/index.html:/usr/share/nginx/html/index.html:ro
gochat:
build:
context: ../..
+27
View File
@@ -0,0 +1,27 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GoChat WEB CHANNEL 测试</title>
</head>
<body>
<h1>GoChat WEB CHANNEL 测试</h1>
<script>
window.chatwootSettings = { "position": "right", "type": "standard", "launcherTitle": "" };
(function (d, t) {
var BASE_URL = "https://gochat.yqbmb.com";
var g = d.createElement(t), s = d.getElementsByTagName(t)[0];
g.src = BASE_URL + "/sdk/js/sdk.js";
g.async = true;
s.parentNode.insertBefore(g, s);
g.onload = function () {
window.chatwootSDK.run({
websiteToken: "1e26e9f9866a6e7d988d85fa",
baseUrl: BASE_URL
});
};
})(document, "script");
</script>
</body>
</html>
@@ -1,26 +1,113 @@
import { mount } from '@vue/test-utils';
import VueDOMPurifyHTML from 'vue-dompurify-html';
import visitorPayload from '../../../../../testdata/visitor_ws_payload.json';
import AgentMessage from '../AgentMessage.vue';
import UnreadMessage from '../UnreadMessage.vue';
const context = {
channelConfig: { websiteName: 'GoChat' },
channelConfig: {
websiteName: 'GoChat',
enabledFeatures: [],
avatarUrl: '',
},
useInboxAvatarForBot: false,
$t: key => key,
};
describe('widget agent name', () => {
it('uses the inbox name when a conversation message has no public sender', () => {
expect(
AgentMessage.computed.agentName.call({ ...context, message: {} })
).toBe('GoChat');
window.chatwootWebChannel = context.channelConfig;
const mountAgentMessage = message =>
mount(AgentMessage, {
props: {
message: { ...message, showAvatar: true },
},
global: {
plugins: [VueDOMPurifyHTML],
stubs: {
AgentMessageBubble: { template: '<div />' },
Avatar: { template: '<div />' },
FileBubble: { template: '<div />' },
ImageBubble: { template: '<div />' },
MessageReplyButton: { template: '<div />' },
ReplyToChip: { template: '<div />' },
UserMessage: { template: '<div />' },
VideoBubble: { template: '<div />' },
},
},
});
it('uses the inbox name when an unread message has no public sender', () => {
describe('widget agent name', () => {
it.each([
[
'available_name',
{
sender: { available_name: 'Available', name: 'Name' },
additional_attributes: { sender_name: 'Visitor service' },
},
'Available',
],
[
'name',
{
sender: { name: 'Name' },
additional_attributes: { sender_name: 'Visitor service' },
},
'Name',
],
[
'message sender_name',
{ additional_attributes: { sender_name: 'Visitor service' } },
'Visitor service',
],
['inbox name', {}, 'GoChat'],
])('conversation message prefers %s', (_source, message, expected) => {
expect(AgentMessage.computed.agentName.call({ ...context, message })).toBe(
expected
);
});
it('returns an empty name when the sanitized inbox name is unavailable', () => {
expect(
AgentMessage.computed.agentName.call({
...context,
channelConfig: { ...context.channelConfig, websiteName: '' },
message: {},
})
).toBe('');
});
it.each([
[
'available_name',
{ available_name: 'Available', name: 'Name' },
'Available',
],
['name', { name: 'Name' }, 'Name'],
['inbox name', {}, 'GoChat'],
])('unread message prefers %s', (_source, sender, expected) => {
expect(
UnreadMessage.computed.agentName.call({
...context,
sender,
isSenderExist: UnreadMessage.methods.isSenderExist,
})
).toBe(expected);
});
it('returns an empty unread name when the inbox name is unavailable', () => {
expect(
UnreadMessage.computed.agentName.call({
...context,
channelConfig: { ...context.channelConfig, websiteName: '' },
sender: {},
isSenderExist: UnreadMessage.methods.isSenderExist,
})
).toBe('GoChat');
).toBe('');
});
it('mounts the visitor WS output with the inbox name', () => {
const wrapper = mountAgentMessage(visitorPayload.data);
expect(wrapper.get('.agent-name').text()).toBe('GoChat');
});
});
+10
View File
@@ -0,0 +1,10 @@
{
"event": "message.created",
"data": {
"id": 12,
"content": "Dashboard reply",
"message_type": 1,
"conversation_id": 42,
"additional_attributes": {}
}
}