@shopify/ui-extensions-server-kit 0.0.0-experimental-20260902222805
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +157 -0
- package/README.md +75 -0
- package/dist/ExtensionServerClient/ExtensionServerClient.d.ts +27 -0
- package/dist/ExtensionServerClient/ExtensionServerClient.test.d.ts +9 -0
- package/dist/ExtensionServerClient/index.d.ts +2 -0
- package/dist/ExtensionServerClient/server-types.d.ts +66 -0
- package/dist/ExtensionServerClient/types.d.ts +124 -0
- package/dist/_virtual/jsx-runtime.cjs.js +1 -0
- package/dist/_virtual/jsx-runtime.cjs2.js +1 -0
- package/dist/_virtual/jsx-runtime.es.js +5 -0
- package/dist/_virtual/jsx-runtime.es2.js +4 -0
- package/dist/_virtual/react-jsx-runtime.development.cjs.js +1 -0
- package/dist/_virtual/react-jsx-runtime.development.es.js +4 -0
- package/dist/_virtual/react-jsx-runtime.production.min.cjs.js +1 -0
- package/dist/_virtual/react-jsx-runtime.production.min.es.js +4 -0
- package/dist/context/ExtensionServerProvider.d.ts +3 -0
- package/dist/context/ExtensionServerProvider.test.d.ts +1 -0
- package/dist/context/constants.d.ts +2 -0
- package/dist/context/index.d.ts +3 -0
- package/dist/context/types.d.ts +12 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/useExtensionServerContext.d.ts +1 -0
- package/dist/hooks/useExtensionServerState.d.ts +1 -0
- package/dist/hooks/useIsomorphicLayoutEffect.d.ts +2 -0
- package/dist/i18n.d.ts +68 -0
- package/dist/i18n.test.d.ts +1 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.cjs2.js +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.es.js +46 -0
- package/dist/index.es2.js +8 -0
- package/dist/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.development.cjs.js +22 -0
- package/dist/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.development.es.js +604 -0
- package/dist/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.cjs.js +9 -0
- package/dist/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.es.js +28 -0
- package/dist/node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.cjs.js +1 -0
- package/dist/node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.es.js +10 -0
- package/dist/packages/ui-extensions-server-kit/src/ExtensionServerClient/ExtensionServerClient.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/ExtensionServerClient/ExtensionServerClient.es.js +136 -0
- package/dist/packages/ui-extensions-server-kit/src/ExtensionServerClient/types.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/ExtensionServerClient/types.es.js +4 -0
- package/dist/packages/ui-extensions-server-kit/src/context/ExtensionServerProvider.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/context/ExtensionServerProvider.es.js +30 -0
- package/dist/packages/ui-extensions-server-kit/src/context/constants.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/context/constants.es.js +13 -0
- package/dist/packages/ui-extensions-server-kit/src/hooks/useExtensionServerContext.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/hooks/useExtensionServerContext.es.js +7 -0
- package/dist/packages/ui-extensions-server-kit/src/hooks/useExtensionServerState.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/hooks/useExtensionServerState.es.js +9 -0
- package/dist/packages/ui-extensions-server-kit/src/hooks/useIsomorphicLayoutEffect.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/hooks/useIsomorphicLayoutEffect.es.js +5 -0
- package/dist/packages/ui-extensions-server-kit/src/i18n.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/i18n.es.js +60 -0
- package/dist/packages/ui-extensions-server-kit/src/state/actions/actions.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/state/actions/actions.es.js +37 -0
- package/dist/packages/ui-extensions-server-kit/src/state/reducers/constants.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/state/reducers/constants.es.js +7 -0
- package/dist/packages/ui-extensions-server-kit/src/state/reducers/extensionServerReducer.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/state/reducers/extensionServerReducer.es.js +57 -0
- package/dist/packages/ui-extensions-server-kit/src/testing/MockExtensionServerProvider.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/testing/MockExtensionServerProvider.es.js +25 -0
- package/dist/packages/ui-extensions-server-kit/src/testing/app.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/testing/app.es.js +16 -0
- package/dist/packages/ui-extensions-server-kit/src/testing/extensions.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/testing/extensions.es.js +66 -0
- package/dist/packages/ui-extensions-server-kit/src/types.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/types.es.js +4 -0
- package/dist/packages/ui-extensions-server-kit/src/utilities/assetToString.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/utilities/assetToString.es.js +7 -0
- package/dist/packages/ui-extensions-server-kit/src/utilities/isUIExtension.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/utilities/isUIExtension.es.js +7 -0
- package/dist/packages/ui-extensions-server-kit/src/utilities/isValidSurface.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/utilities/isValidSurface.es.js +7 -0
- package/dist/packages/ui-extensions-server-kit/src/utilities/noop.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/utilities/noop.es.js +5 -0
- package/dist/packages/ui-extensions-server-kit/src/utilities/replaceUpdated.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/utilities/replaceUpdated.es.js +14 -0
- package/dist/packages/ui-extensions-server-kit/src/utilities/set.cjs.js +1 -0
- package/dist/packages/ui-extensions-server-kit/src/utilities/set.es.js +18 -0
- package/dist/state/actions/actions.d.ts +6 -0
- package/dist/state/actions/index.d.ts +2 -0
- package/dist/state/actions/types.d.ts +21 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/reducers/constants.d.ts +2 -0
- package/dist/state/reducers/extensionServerReducer.d.ts +3 -0
- package/dist/state/reducers/extensionServerReducer.test.d.ts +1 -0
- package/dist/state/reducers/index.d.ts +3 -0
- package/dist/state/reducers/types.d.ts +6 -0
- package/dist/testing/MockExtensionServerProvider.d.ts +8 -0
- package/dist/testing/app.d.ts +2 -0
- package/dist/testing/extensions.d.ts +6 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/types.d.ts +196 -0
- package/dist/utilities/assetToString.d.ts +2 -0
- package/dist/utilities/assetToString.test.d.ts +1 -0
- package/dist/utilities/index.d.ts +6 -0
- package/dist/utilities/isUIExtension.d.ts +1 -0
- package/dist/utilities/isValidSurface.d.ts +2 -0
- package/dist/utilities/noop.d.ts +1 -0
- package/dist/utilities/replaceUpdated.d.ts +1 -0
- package/dist/utilities/replaceUpdated.test.d.ts +1 -0
- package/dist/utilities/set.d.ts +4 -0
- package/dist/utilities/set.test.d.ts +1 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.mjs +1 -0
- package/node_modules/@testing-library/react/LICENSE +20 -0
- package/node_modules/@testing-library/react/README.md +692 -0
- package/node_modules/@testing-library/react/dist/@testing-library/react.cjs.js +537 -0
- package/node_modules/@testing-library/react/dist/@testing-library/react.esm.js +497 -0
- package/node_modules/@testing-library/react/dist/@testing-library/react.pure.cjs.js +501 -0
- package/node_modules/@testing-library/react/dist/@testing-library/react.pure.esm.js +461 -0
- package/node_modules/@testing-library/react/dist/@testing-library/react.pure.umd.js +502 -0
- package/node_modules/@testing-library/react/dist/@testing-library/react.pure.umd.js.map +1 -0
- package/node_modules/@testing-library/react/dist/@testing-library/react.pure.umd.min.js +2 -0
- package/node_modules/@testing-library/react/dist/@testing-library/react.pure.umd.min.js.map +1 -0
- package/node_modules/@testing-library/react/dist/@testing-library/react.umd.js +538 -0
- package/node_modules/@testing-library/react/dist/@testing-library/react.umd.js.map +1 -0
- package/node_modules/@testing-library/react/dist/@testing-library/react.umd.min.js +2 -0
- package/node_modules/@testing-library/react/dist/@testing-library/react.umd.min.js.map +1 -0
- package/node_modules/@testing-library/react/dist/act-compat.js +80 -0
- package/node_modules/@testing-library/react/dist/config.js +33 -0
- package/node_modules/@testing-library/react/dist/fire-event.js +70 -0
- package/node_modules/@testing-library/react/dist/index.js +52 -0
- package/node_modules/@testing-library/react/dist/pure.js +356 -0
- package/node_modules/@testing-library/react/dont-cleanup-after-each.js +1 -0
- package/node_modules/@testing-library/react/package.json +116 -0
- package/node_modules/@testing-library/react/pure.d.ts +1 -0
- package/node_modules/@testing-library/react/pure.js +2 -0
- package/node_modules/@testing-library/react/types/index.d.ts +287 -0
- package/node_modules/@testing-library/react/types/pure.d.ts +1 -0
- package/node_modules/@types/react/LICENSE +21 -0
- package/node_modules/@types/react/README.md +15 -0
- package/node_modules/@types/react/canary.d.ts +166 -0
- package/node_modules/@types/react/experimental.d.ts +127 -0
- package/node_modules/@types/react/global.d.ts +160 -0
- package/node_modules/@types/react/index.d.ts +4543 -0
- package/node_modules/@types/react/jsx-dev-runtime.d.ts +45 -0
- package/node_modules/@types/react/jsx-runtime.d.ts +36 -0
- package/node_modules/@types/react/package.json +206 -0
- package/node_modules/@types/react/ts5.0/canary.d.ts +166 -0
- package/node_modules/@types/react/ts5.0/experimental.d.ts +127 -0
- package/node_modules/@types/react/ts5.0/global.d.ts +160 -0
- package/node_modules/@types/react/ts5.0/index.d.ts +4530 -0
- package/node_modules/@types/react/ts5.0/jsx-dev-runtime.d.ts +44 -0
- package/node_modules/@types/react/ts5.0/jsx-runtime.d.ts +35 -0
- package/node_modules/@vitejs/plugin-react/LICENSE +21 -0
- package/node_modules/@vitejs/plugin-react/README.md +159 -0
- package/node_modules/@vitejs/plugin-react/dist/index.d.ts +75 -0
- package/node_modules/@vitejs/plugin-react/dist/index.js +401 -0
- package/node_modules/@vitejs/plugin-react/dist/refresh-runtime.js +663 -0
- package/node_modules/@vitejs/plugin-react/package.json +77 -0
- package/node_modules/@vitejs/plugin-react/types/preamble.d.ts +1 -0
- package/package.json +64 -0
- package/project.json +40 -0
- package/scripts/create-entry-files.ts +44 -0
- package/src/ExtensionServerClient/ExtensionServerClient.test.ts +1238 -0
- package/src/ExtensionServerClient/ExtensionServerClient.ts +320 -0
- package/src/ExtensionServerClient/index.ts +2 -0
- package/src/ExtensionServerClient/server-types.ts +65 -0
- package/src/ExtensionServerClient/types.ts +159 -0
- package/src/context/ExtensionServerProvider.test.tsx +318 -0
- package/src/context/ExtensionServerProvider.tsx +48 -0
- package/src/context/constants.ts +16 -0
- package/src/context/index.ts +3 -0
- package/src/context/types.ts +14 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useExtensionServerContext.ts +4 -0
- package/src/hooks/useExtensionServerState.ts +6 -0
- package/src/hooks/useIsomorphicLayoutEffect.ts +6 -0
- package/src/i18n.test.ts +417 -0
- package/src/i18n.ts +205 -0
- package/src/index.ts +7 -0
- package/src/state/actions/actions.ts +36 -0
- package/src/state/actions/index.ts +2 -0
- package/src/state/actions/types.ts +26 -0
- package/src/state/index.ts +2 -0
- package/src/state/reducers/constants.ts +6 -0
- package/src/state/reducers/extensionServerReducer.test.ts +165 -0
- package/src/state/reducers/extensionServerReducer.ts +87 -0
- package/src/state/reducers/index.ts +3 -0
- package/src/state/reducers/types.ts +7 -0
- package/src/testing/MockExtensionServerProvider.tsx +36 -0
- package/src/testing/app.ts +15 -0
- package/src/testing/extensions.ts +71 -0
- package/src/testing/index.ts +3 -0
- package/src/types.ts +204 -0
- package/src/utilities/assetToString.test.ts +16 -0
- package/src/utilities/assetToString.ts +7 -0
- package/src/utilities/index.ts +6 -0
- package/src/utilities/isUIExtension.ts +8 -0
- package/src/utilities/isValidSurface.ts +6 -0
- package/src/utilities/noop.ts +1 -0
- package/src/utilities/replaceUpdated.test.ts +26 -0
- package/src/utilities/replaceUpdated.ts +15 -0
- package/src/utilities/set.test.ts +19 -0
- package/src/utilities/set.ts +35 -0
- package/testing.d.ts +1 -0
- package/testing.js +1 -0
- package/testing.mjs +1 -0
- package/tests/setup.ts +3 -0
- package/vite.config.mts +57 -0
|
@@ -0,0 +1,1238 @@
|
|
|
1
|
+
import {ExtensionServerClient} from './ExtensionServerClient'
|
|
2
|
+
import {DeepPartial} from '../types'
|
|
3
|
+
import {mockApp} from '../testing'
|
|
4
|
+
|
|
5
|
+
import {Localization} from 'i18n.js'
|
|
6
|
+
import {beforeEach, expect, test, vi, describe} from 'vitest'
|
|
7
|
+
|
|
8
|
+
// Mock React's act function because jest-websocket-mock tries to use it
|
|
9
|
+
vi.mock('react-dom/test-utils', () => ({
|
|
10
|
+
act: async (callback: () => Promise<void> | void) => {
|
|
11
|
+
return callback()
|
|
12
|
+
},
|
|
13
|
+
}))
|
|
14
|
+
|
|
15
|
+
// Create a custom mock WebSocket implementation to avoid using jest-websocket-mock
|
|
16
|
+
class MockWebSocketServer {
|
|
17
|
+
clients: MockWebSocket[] = []
|
|
18
|
+
messages: any[] = []
|
|
19
|
+
|
|
20
|
+
connect(socket: MockWebSocket) {
|
|
21
|
+
// OPEN state
|
|
22
|
+
this.clients.push(socket)
|
|
23
|
+
socket.readyState = 1
|
|
24
|
+
socket.onopen?.({} as Event)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
send(data: any) {
|
|
28
|
+
this.messages.push(data)
|
|
29
|
+
this.clients.forEach((client) => {
|
|
30
|
+
const event = new MessageEvent('message', {
|
|
31
|
+
data: typeof data === 'string' ? data : JSON.stringify(data),
|
|
32
|
+
})
|
|
33
|
+
client.onmessage?.(event)
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
close() {
|
|
38
|
+
// CLOSED state
|
|
39
|
+
this.clients.forEach((client) => {
|
|
40
|
+
client.readyState = 3
|
|
41
|
+
client.onclose?.({} as CloseEvent)
|
|
42
|
+
})
|
|
43
|
+
this.clients = []
|
|
44
|
+
this.messages = []
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class MockWebSocket implements Partial<WebSocket> {
|
|
49
|
+
url: string
|
|
50
|
+
readyState = 0
|
|
51
|
+
onopen: ((ev: Event) => any) | null = null
|
|
52
|
+
onmessage: ((ev: MessageEvent) => any) | null = null
|
|
53
|
+
onclose: ((ev: CloseEvent) => any) | null = null
|
|
54
|
+
server: MockWebSocketServer
|
|
55
|
+
private eventListeners: Record<string, Set<EventListener>> = {
|
|
56
|
+
open: new Set(),
|
|
57
|
+
message: new Set(),
|
|
58
|
+
close: new Set(),
|
|
59
|
+
error: new Set(),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
constructor(url: string, server: MockWebSocketServer) {
|
|
63
|
+
this.url = url
|
|
64
|
+
this.server = server
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
addEventListener(type: string, listener: EventListener): void {
|
|
68
|
+
if (!this.eventListeners[type]) {
|
|
69
|
+
this.eventListeners[type] = new Set()
|
|
70
|
+
}
|
|
71
|
+
this.eventListeners[type].add(listener)
|
|
72
|
+
|
|
73
|
+
// Map standard event handlers to addEventListener
|
|
74
|
+
if (type === 'open' && this.onopen === null) {
|
|
75
|
+
this.onopen = (event) => {
|
|
76
|
+
this.eventListeners.open.forEach((listener) => listener(event))
|
|
77
|
+
}
|
|
78
|
+
} else if (type === 'message' && this.onmessage === null) {
|
|
79
|
+
this.onmessage = (event) => {
|
|
80
|
+
this.eventListeners.message.forEach((listener) => listener(event))
|
|
81
|
+
}
|
|
82
|
+
} else if (type === 'close' && this.onclose === null) {
|
|
83
|
+
this.onclose = (event) => {
|
|
84
|
+
this.eventListeners.close.forEach((listener) => listener(event))
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
removeEventListener(type: string, listener: EventListener): void {
|
|
90
|
+
if (this.eventListeners[type]) {
|
|
91
|
+
this.eventListeners[type].delete(listener)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
dispatchEvent(event: Event): boolean {
|
|
96
|
+
const type = event.type
|
|
97
|
+
|
|
98
|
+
if (type === 'open' && this.onopen) {
|
|
99
|
+
this.onopen(event)
|
|
100
|
+
} else if (type === 'message' && this.onmessage) {
|
|
101
|
+
this.onmessage(event as MessageEvent)
|
|
102
|
+
} else if (type === 'close' && this.onclose) {
|
|
103
|
+
this.onclose(event as CloseEvent)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (this.eventListeners[type]) {
|
|
107
|
+
this.eventListeners[type].forEach((listener) => listener(event))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return true
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
send(data: string | ArrayBufferLike | Blob | ArrayBufferView) {
|
|
114
|
+
this.server.messages.push(data)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
close() {
|
|
118
|
+
this.readyState = 3
|
|
119
|
+
this.onclose?.({} as CloseEvent)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Update the connection interface to include automaticConnect
|
|
124
|
+
declare module './ExtensionServerClient' {
|
|
125
|
+
namespace ExtensionServer {
|
|
126
|
+
interface ConnectionOptions {
|
|
127
|
+
url?: string
|
|
128
|
+
automaticConnect?: boolean
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Test constants
|
|
134
|
+
const TEST_CONNECTION_URL = 'ws://example-host.com:8000/extensions/'
|
|
135
|
+
|
|
136
|
+
const defaultOptions = {
|
|
137
|
+
connection: {
|
|
138
|
+
url: TEST_CONNECTION_URL,
|
|
139
|
+
automaticConnect: true,
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
describe('ExtensionServerClient', () => {
|
|
144
|
+
let mockSocketServer: MockWebSocketServer
|
|
145
|
+
let mockSocket: MockWebSocket
|
|
146
|
+
|
|
147
|
+
// Create a WebSocket factory function that returns our mock
|
|
148
|
+
const createMockWebSocket = (url: string) => {
|
|
149
|
+
mockSocket = new MockWebSocket(url, mockSocketServer)
|
|
150
|
+
return mockSocket
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
beforeEach(() => {
|
|
154
|
+
// Set up mock socket server
|
|
155
|
+
mockSocketServer = new MockWebSocketServer()
|
|
156
|
+
|
|
157
|
+
// Mock the global WebSocket to use our implementation
|
|
158
|
+
vi.spyOn(globalThis, 'WebSocket').mockImplementation(function (urlParam: any) {
|
|
159
|
+
// Handle both string URLs and URL objects by extracting the string representation
|
|
160
|
+
const urlString = typeof urlParam === 'string' ? urlParam : urlParam.toString()
|
|
161
|
+
return createMockWebSocket(urlString) as unknown as WebSocket
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
describe('initialization', () => {
|
|
166
|
+
test('connects to the target websocket', async () => {
|
|
167
|
+
// Create client
|
|
168
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
169
|
+
|
|
170
|
+
// Connect the mock socket to simulate WebSocket connection
|
|
171
|
+
mockSocketServer.connect(mockSocket)
|
|
172
|
+
|
|
173
|
+
// Verify connection is established
|
|
174
|
+
expect(client.connection).toBeDefined()
|
|
175
|
+
expect(mockSocketServer.clients.length).toBe(1)
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
test('does not connect to the target websocket if "automaticConnect" is false', async () => {
|
|
179
|
+
// Create client with automaticConnect: false
|
|
180
|
+
const client = new ExtensionServerClient({
|
|
181
|
+
connection: {
|
|
182
|
+
url: TEST_CONNECTION_URL,
|
|
183
|
+
automaticConnect: false,
|
|
184
|
+
},
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
// Verify connection is not established
|
|
188
|
+
expect(client.connection).toBeUndefined()
|
|
189
|
+
expect(mockSocketServer.clients.length).toBe(0)
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
describe('on()', () => {
|
|
194
|
+
test('sends data with extensions filtered by surface option on "connected" event', async () => {
|
|
195
|
+
// Create client
|
|
196
|
+
const client = new ExtensionServerClient({
|
|
197
|
+
connection: {
|
|
198
|
+
url: TEST_CONNECTION_URL,
|
|
199
|
+
automaticConnect: true,
|
|
200
|
+
},
|
|
201
|
+
surface: 'admin',
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// Mock connection
|
|
205
|
+
mockSocketServer.connect(mockSocket)
|
|
206
|
+
|
|
207
|
+
// Create spy for the connected event
|
|
208
|
+
const connectSpy = vi.fn()
|
|
209
|
+
client.on('connected', connectSpy)
|
|
210
|
+
|
|
211
|
+
// Send connected event with mock data
|
|
212
|
+
const data = {
|
|
213
|
+
app: mockApp(),
|
|
214
|
+
extensions: [
|
|
215
|
+
{uuid: '123', surface: 'admin'},
|
|
216
|
+
{uuid: '456', surface: 'checkout'},
|
|
217
|
+
{uuid: '789', surface: '', extensionPoints: [{surface: 'admin'}]},
|
|
218
|
+
],
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Send the event
|
|
222
|
+
mockSocketServer.send({event: 'connected', data})
|
|
223
|
+
|
|
224
|
+
// Verify correct data is filtered and passed to the callback
|
|
225
|
+
expect(connectSpy).toHaveBeenCalledTimes(1)
|
|
226
|
+
expect(connectSpy).toHaveBeenCalledWith(
|
|
227
|
+
expect.objectContaining({
|
|
228
|
+
extensions: expect.arrayContaining([
|
|
229
|
+
expect.objectContaining({uuid: '123', surface: 'admin'}),
|
|
230
|
+
expect.objectContaining({uuid: '789'}),
|
|
231
|
+
]),
|
|
232
|
+
}),
|
|
233
|
+
)
|
|
234
|
+
// Verify checkout extension is filtered out
|
|
235
|
+
const calledWith = connectSpy.mock.calls[0][0]
|
|
236
|
+
const extensionIds = calledWith.extensions.map((ext: any) => ext.uuid)
|
|
237
|
+
expect(extensionIds).not.toContain('456')
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
test('sends data with all extensions when surface option is not valid on "connected" event', async () => {
|
|
241
|
+
// Create client with invalid surface
|
|
242
|
+
const client = new ExtensionServerClient({
|
|
243
|
+
connection: {
|
|
244
|
+
url: TEST_CONNECTION_URL,
|
|
245
|
+
automaticConnect: true,
|
|
246
|
+
},
|
|
247
|
+
surface: 'abc' as any,
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
// Mock connection
|
|
251
|
+
mockSocketServer.connect(mockSocket)
|
|
252
|
+
|
|
253
|
+
// Create spy for the connected event
|
|
254
|
+
const connectSpy = vi.fn()
|
|
255
|
+
client.on('connected', connectSpy)
|
|
256
|
+
|
|
257
|
+
// Send connected event with mock data
|
|
258
|
+
const data = {
|
|
259
|
+
app: mockApp(),
|
|
260
|
+
extensions: [
|
|
261
|
+
{uuid: '123', surface: 'admin'},
|
|
262
|
+
{uuid: '456', surface: 'checkout'},
|
|
263
|
+
],
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Send the event
|
|
267
|
+
mockSocketServer.send({event: 'connected', data})
|
|
268
|
+
|
|
269
|
+
// Verify all extensions are passed to the callback
|
|
270
|
+
expect(connectSpy).toHaveBeenCalledTimes(1)
|
|
271
|
+
expect(connectSpy).toHaveBeenCalledWith(
|
|
272
|
+
expect.objectContaining({
|
|
273
|
+
extensions: expect.arrayContaining([
|
|
274
|
+
expect.objectContaining({uuid: '123'}),
|
|
275
|
+
expect.objectContaining({uuid: '456'}),
|
|
276
|
+
]),
|
|
277
|
+
}),
|
|
278
|
+
)
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
test('sends data with translatable props as-is for UI extensions when locales option is not provided on "connected" event', async () => {
|
|
282
|
+
// Create client
|
|
283
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
284
|
+
|
|
285
|
+
// Mock connection
|
|
286
|
+
mockSocketServer.connect(mockSocket)
|
|
287
|
+
|
|
288
|
+
// Create spy
|
|
289
|
+
const connectSpy = vi.fn()
|
|
290
|
+
client.on('connected', connectSpy)
|
|
291
|
+
|
|
292
|
+
// Define localization data
|
|
293
|
+
const localization: Localization = {
|
|
294
|
+
defaultLocale: 'en',
|
|
295
|
+
translations: {
|
|
296
|
+
ja: {
|
|
297
|
+
welcome: 'いらっしゃいませ!',
|
|
298
|
+
},
|
|
299
|
+
en: {
|
|
300
|
+
welcome: 'Welcome!',
|
|
301
|
+
},
|
|
302
|
+
fr: {
|
|
303
|
+
welcome: 'Bienvenue!',
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
lastUpdated: 1684164163736,
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Mock data
|
|
310
|
+
const data = {
|
|
311
|
+
app: mockApp(),
|
|
312
|
+
extensions: [
|
|
313
|
+
{
|
|
314
|
+
uuid: '123',
|
|
315
|
+
type: 'ui_extension',
|
|
316
|
+
localization,
|
|
317
|
+
extensionPoints: [{localization}],
|
|
318
|
+
},
|
|
319
|
+
{uuid: '456', type: 'ui_extension', localization: null, extensionPoints: [{localization: null}]},
|
|
320
|
+
{uuid: '789', type: 'product_subscription'},
|
|
321
|
+
],
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Send event
|
|
325
|
+
mockSocketServer.send({event: 'connected', data})
|
|
326
|
+
|
|
327
|
+
// Verify props are passed as-is
|
|
328
|
+
expect(connectSpy).toHaveBeenCalledTimes(1)
|
|
329
|
+
expect(connectSpy).toHaveBeenCalledWith(
|
|
330
|
+
expect.objectContaining({
|
|
331
|
+
extensions: expect.arrayContaining(data.extensions),
|
|
332
|
+
}),
|
|
333
|
+
)
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
test('sends data as-is on "connected" event', async () => {
|
|
337
|
+
// Create client
|
|
338
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
339
|
+
|
|
340
|
+
// Mock connection
|
|
341
|
+
mockSocketServer.connect(mockSocket)
|
|
342
|
+
|
|
343
|
+
// Create spy
|
|
344
|
+
const connectSpy = vi.fn()
|
|
345
|
+
client.on('connected', connectSpy)
|
|
346
|
+
|
|
347
|
+
// Mock data
|
|
348
|
+
const data = {
|
|
349
|
+
app: mockApp(),
|
|
350
|
+
extensions: [
|
|
351
|
+
{uuid: '123', type: 'ui_extension', name: 'Extension 123'},
|
|
352
|
+
{uuid: '456', type: 'checkout_ui_extension', name: 'Extension 456'},
|
|
353
|
+
{uuid: '789', type: 'product_subscription', name: 'Extension 789'},
|
|
354
|
+
],
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Send event
|
|
358
|
+
mockSocketServer.send({event: 'connected', data})
|
|
359
|
+
|
|
360
|
+
// Verify props are passed as-is
|
|
361
|
+
expect(connectSpy).toHaveBeenCalledTimes(1)
|
|
362
|
+
expect(connectSpy).toHaveBeenCalledWith(
|
|
363
|
+
expect.objectContaining({
|
|
364
|
+
extensions: expect.arrayContaining(data.extensions),
|
|
365
|
+
}),
|
|
366
|
+
)
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
test('sends data with translated props for UI extensions when locales option is provided on "connected" event', async () => {
|
|
370
|
+
// Create client with locales using direct type assertion to DeepPartial
|
|
371
|
+
const client = new ExtensionServerClient({
|
|
372
|
+
...defaultOptions,
|
|
373
|
+
locales: {user: 'ja', shop: 'fr'} as unknown as DeepPartial<any>,
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
// Mock connection
|
|
377
|
+
mockSocketServer.connect(mockSocket)
|
|
378
|
+
|
|
379
|
+
// Create spy for the connected event
|
|
380
|
+
const connectSpy = vi.fn()
|
|
381
|
+
client.on('connected', connectSpy)
|
|
382
|
+
|
|
383
|
+
// Define localization data
|
|
384
|
+
const localization: Localization = {
|
|
385
|
+
defaultLocale: 'en',
|
|
386
|
+
translations: {
|
|
387
|
+
ja: {
|
|
388
|
+
welcome: 'いらっしゃいませ!',
|
|
389
|
+
description: '拡張子の説明',
|
|
390
|
+
},
|
|
391
|
+
en: {
|
|
392
|
+
welcome: 'Welcome!',
|
|
393
|
+
description: 'Extension description',
|
|
394
|
+
},
|
|
395
|
+
fr: {
|
|
396
|
+
welcome: 'Bienvenue!',
|
|
397
|
+
description: "Description de l'extension",
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
lastUpdated: 1684164163736,
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Create mock data - using type assertion to avoid DeepPartial errors
|
|
404
|
+
interface MockExtension {
|
|
405
|
+
uuid: string
|
|
406
|
+
type: string
|
|
407
|
+
name: string
|
|
408
|
+
description?: string
|
|
409
|
+
localization?: any
|
|
410
|
+
extensionPoints?: {
|
|
411
|
+
localization?: any
|
|
412
|
+
target?: string
|
|
413
|
+
surface?: string
|
|
414
|
+
name?: string
|
|
415
|
+
}[]
|
|
416
|
+
surface?: string
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Using a typed array to avoid DeepPartial issues
|
|
420
|
+
const mockExtensions: MockExtension[] = [
|
|
421
|
+
{
|
|
422
|
+
uuid: '123',
|
|
423
|
+
type: 'ui_extension',
|
|
424
|
+
name: 't:welcome',
|
|
425
|
+
description: 't:description',
|
|
426
|
+
localization,
|
|
427
|
+
extensionPoints: [{localization, target: 'admin.test', surface: 'admin'}],
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
uuid: '456',
|
|
431
|
+
type: 'ui_extension',
|
|
432
|
+
name: 'Fixed name t:',
|
|
433
|
+
localization: null,
|
|
434
|
+
extensionPoints: [{localization: null, name: 'Fixed name t:', target: 'admin.test', surface: 'admin'}],
|
|
435
|
+
},
|
|
436
|
+
{uuid: '789', type: 'product_subscription', name: 'Extension 789'},
|
|
437
|
+
]
|
|
438
|
+
|
|
439
|
+
// Send the connected event
|
|
440
|
+
mockSocketServer.send({
|
|
441
|
+
event: 'connected',
|
|
442
|
+
data: {
|
|
443
|
+
app: mockApp(),
|
|
444
|
+
extensions: mockExtensions,
|
|
445
|
+
store: 'test-store',
|
|
446
|
+
},
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
// Verify event was handled
|
|
450
|
+
expect(connectSpy).toHaveBeenCalledTimes(1)
|
|
451
|
+
|
|
452
|
+
// Assert on specific properties without using complex matchers that trigger type errors
|
|
453
|
+
const connectedData = connectSpy.mock.calls[0][0]
|
|
454
|
+
expect(connectedData).toBeDefined()
|
|
455
|
+
expect(connectedData.extensions).toHaveLength(3)
|
|
456
|
+
|
|
457
|
+
// Find the translated extension
|
|
458
|
+
const translatedExt = connectedData.extensions.find((ext: any) => ext.uuid === '123')
|
|
459
|
+
expect(translatedExt).toBeDefined()
|
|
460
|
+
|
|
461
|
+
// Check translation worked properly - name should be translated
|
|
462
|
+
expect(translatedExt.name).not.toBe('t:welcome')
|
|
463
|
+
// Description should be translated
|
|
464
|
+
expect(translatedExt.description).not.toBe('t:description')
|
|
465
|
+
|
|
466
|
+
// Verify extension points were also translated
|
|
467
|
+
const extensionPoint = translatedExt.extensionPoints[0]
|
|
468
|
+
expect(extensionPoint.localization).toBeDefined()
|
|
469
|
+
expect(extensionPoint.localization.extensionLocale).toBe('ja')
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
test('listens to persist events', async () => {
|
|
473
|
+
// Create client
|
|
474
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
475
|
+
|
|
476
|
+
// Mock connection
|
|
477
|
+
mockSocketServer.connect(mockSocket)
|
|
478
|
+
|
|
479
|
+
// Create spy
|
|
480
|
+
const updateSpy = vi.fn()
|
|
481
|
+
client.on('update', updateSpy)
|
|
482
|
+
|
|
483
|
+
// Mock data
|
|
484
|
+
const data = {
|
|
485
|
+
app: mockApp(),
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Send event
|
|
489
|
+
mockSocketServer.send({event: 'update', data})
|
|
490
|
+
|
|
491
|
+
// Verify data is passed to the callback
|
|
492
|
+
expect(updateSpy).toHaveBeenCalledTimes(1)
|
|
493
|
+
expect(updateSpy).toHaveBeenCalledWith(data)
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
test('unsubscribes from persist events', async () => {
|
|
497
|
+
// Create client
|
|
498
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
499
|
+
|
|
500
|
+
// Mock connection
|
|
501
|
+
mockSocketServer.connect(mockSocket)
|
|
502
|
+
|
|
503
|
+
// Set up spy and get unsubscribe function
|
|
504
|
+
const updateSpy = vi.fn()
|
|
505
|
+
const unsubscribe = client.on('update', updateSpy)
|
|
506
|
+
|
|
507
|
+
// Unsubscribe
|
|
508
|
+
unsubscribe()
|
|
509
|
+
|
|
510
|
+
// Send event
|
|
511
|
+
mockSocketServer.send({
|
|
512
|
+
event: 'update',
|
|
513
|
+
data: {
|
|
514
|
+
app: mockApp(),
|
|
515
|
+
},
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
// Verify event wasn't handled
|
|
519
|
+
expect(updateSpy).not.toHaveBeenCalled()
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
test('listens to dispatch events', async () => {
|
|
523
|
+
// Create client
|
|
524
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
525
|
+
|
|
526
|
+
// Mock connection
|
|
527
|
+
mockSocketServer.connect(mockSocket)
|
|
528
|
+
|
|
529
|
+
// Set up spy
|
|
530
|
+
const unfocusSpy = vi.fn()
|
|
531
|
+
client.on('unfocus', unfocusSpy)
|
|
532
|
+
|
|
533
|
+
// Send event
|
|
534
|
+
mockSocketServer.send({event: 'dispatch', data: {type: 'unfocus'}})
|
|
535
|
+
|
|
536
|
+
// Verify event was handled
|
|
537
|
+
expect(unfocusSpy).toHaveBeenCalledTimes(1)
|
|
538
|
+
expect(unfocusSpy).toHaveBeenCalledWith(undefined)
|
|
539
|
+
})
|
|
540
|
+
|
|
541
|
+
test('sends data with extensions filtered by surface option on "update" event', async () => {
|
|
542
|
+
// Create client
|
|
543
|
+
const client = new ExtensionServerClient({
|
|
544
|
+
connection: {
|
|
545
|
+
url: TEST_CONNECTION_URL,
|
|
546
|
+
automaticConnect: true,
|
|
547
|
+
},
|
|
548
|
+
surface: 'admin',
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
// Mock connection
|
|
552
|
+
mockSocketServer.connect(mockSocket)
|
|
553
|
+
|
|
554
|
+
// Create spy
|
|
555
|
+
const updateSpy = vi.fn()
|
|
556
|
+
client.on('update', updateSpy)
|
|
557
|
+
|
|
558
|
+
// Mock data
|
|
559
|
+
const data = {
|
|
560
|
+
app: mockApp(),
|
|
561
|
+
extensions: [
|
|
562
|
+
{uuid: '123', surface: 'admin'},
|
|
563
|
+
{uuid: '456', surface: 'checkout'},
|
|
564
|
+
{uuid: '789', surface: '', extensionPoints: [{surface: 'admin'}]},
|
|
565
|
+
],
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Send event
|
|
569
|
+
mockSocketServer.send({event: 'update', data})
|
|
570
|
+
|
|
571
|
+
// Verify correct data is filtered and passed to the callback
|
|
572
|
+
expect(updateSpy).toHaveBeenCalledTimes(1)
|
|
573
|
+
expect(updateSpy).toHaveBeenCalledWith(
|
|
574
|
+
expect.objectContaining({
|
|
575
|
+
extensions: expect.arrayContaining([
|
|
576
|
+
expect.objectContaining({uuid: '123'}),
|
|
577
|
+
expect.objectContaining({uuid: '789'}),
|
|
578
|
+
]),
|
|
579
|
+
}),
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
// Verify checkout extension is filtered out
|
|
583
|
+
const calledWith = updateSpy.mock.calls[0][0]
|
|
584
|
+
const extensionIds = calledWith.extensions.map((ext: any) => ext.uuid)
|
|
585
|
+
expect(extensionIds).not.toContain('456')
|
|
586
|
+
})
|
|
587
|
+
|
|
588
|
+
test('sends data with all extensions when surface option is not valid on "update" event', async () => {
|
|
589
|
+
// Create client with invalid surface
|
|
590
|
+
const client = new ExtensionServerClient({
|
|
591
|
+
connection: {
|
|
592
|
+
url: TEST_CONNECTION_URL,
|
|
593
|
+
automaticConnect: true,
|
|
594
|
+
},
|
|
595
|
+
surface: 'abc' as any,
|
|
596
|
+
})
|
|
597
|
+
|
|
598
|
+
// Mock connection
|
|
599
|
+
mockSocketServer.connect(mockSocket)
|
|
600
|
+
|
|
601
|
+
// Create spy
|
|
602
|
+
const updateSpy = vi.fn()
|
|
603
|
+
client.on('update', updateSpy)
|
|
604
|
+
|
|
605
|
+
// Mock data
|
|
606
|
+
const data = {
|
|
607
|
+
app: mockApp(),
|
|
608
|
+
extensions: [
|
|
609
|
+
{uuid: '123', surface: 'admin'},
|
|
610
|
+
{uuid: '456', surface: 'checkout'},
|
|
611
|
+
],
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Send event
|
|
615
|
+
mockSocketServer.send({event: 'update', data})
|
|
616
|
+
|
|
617
|
+
// Verify all extensions are included (not filtered)
|
|
618
|
+
expect(updateSpy).toHaveBeenCalledTimes(1)
|
|
619
|
+
expect(updateSpy).toHaveBeenCalledWith(
|
|
620
|
+
expect.objectContaining({
|
|
621
|
+
extensions: expect.arrayContaining(data.extensions),
|
|
622
|
+
}),
|
|
623
|
+
)
|
|
624
|
+
})
|
|
625
|
+
|
|
626
|
+
test('sends data with translatable props as-is when locales option is not provided on "update" event', async () => {
|
|
627
|
+
// Create client without locales
|
|
628
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
629
|
+
|
|
630
|
+
// Mock connection
|
|
631
|
+
mockSocketServer.connect(mockSocket)
|
|
632
|
+
|
|
633
|
+
// Create spy
|
|
634
|
+
const updateSpy = vi.fn()
|
|
635
|
+
client.on('update', updateSpy)
|
|
636
|
+
|
|
637
|
+
// Define localization data
|
|
638
|
+
const localization: Localization = {
|
|
639
|
+
defaultLocale: 'en',
|
|
640
|
+
translations: {
|
|
641
|
+
ja: {
|
|
642
|
+
welcome: 'いらっしゃいませ!',
|
|
643
|
+
description: '拡張子の説明',
|
|
644
|
+
},
|
|
645
|
+
en: {
|
|
646
|
+
welcome: 'Welcome!',
|
|
647
|
+
description: 'Extension description',
|
|
648
|
+
},
|
|
649
|
+
fr: {
|
|
650
|
+
welcome: 'Bienvenue!',
|
|
651
|
+
description: "Description de l'extension",
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
lastUpdated: 1684164163736,
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Mock data
|
|
658
|
+
const data = {
|
|
659
|
+
app: mockApp(),
|
|
660
|
+
extensions: [
|
|
661
|
+
{
|
|
662
|
+
uuid: '123',
|
|
663
|
+
type: 'ui_extension',
|
|
664
|
+
name: 't:welcome',
|
|
665
|
+
description: 't:description',
|
|
666
|
+
localization,
|
|
667
|
+
extensionPoints: [{localization}],
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
uuid: '456',
|
|
671
|
+
type: 'ui_extension',
|
|
672
|
+
name: 'Extension 456',
|
|
673
|
+
localization: null,
|
|
674
|
+
extensionPoints: [{localization: null}],
|
|
675
|
+
},
|
|
676
|
+
{uuid: '789', type: 'product_subscription'},
|
|
677
|
+
],
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Send event
|
|
681
|
+
mockSocketServer.send({event: 'update', data})
|
|
682
|
+
|
|
683
|
+
// Verify props are passed as-is
|
|
684
|
+
expect(updateSpy).toHaveBeenCalledTimes(1)
|
|
685
|
+
expect(updateSpy).toHaveBeenCalledWith(
|
|
686
|
+
expect.objectContaining({
|
|
687
|
+
extensions: expect.arrayContaining(data.extensions),
|
|
688
|
+
}),
|
|
689
|
+
)
|
|
690
|
+
})
|
|
691
|
+
|
|
692
|
+
test('sends data with translated props when locales option is provided on "update" event', async () => {
|
|
693
|
+
// Create client with locales using type assertion
|
|
694
|
+
const client = new ExtensionServerClient({
|
|
695
|
+
...defaultOptions,
|
|
696
|
+
locales: {user: 'ja', shop: 'fr'} as unknown as DeepPartial<any>,
|
|
697
|
+
})
|
|
698
|
+
|
|
699
|
+
// Mock connection
|
|
700
|
+
mockSocketServer.connect(mockSocket)
|
|
701
|
+
|
|
702
|
+
// Create spy
|
|
703
|
+
const updateSpy = vi.fn()
|
|
704
|
+
client.on('update', updateSpy)
|
|
705
|
+
|
|
706
|
+
// Define localization data
|
|
707
|
+
const localization: Localization = {
|
|
708
|
+
defaultLocale: 'en',
|
|
709
|
+
translations: {
|
|
710
|
+
ja: {
|
|
711
|
+
welcome: 'いらっしゃいませ!',
|
|
712
|
+
description: '拡張子の説明',
|
|
713
|
+
},
|
|
714
|
+
en: {
|
|
715
|
+
welcome: 'Welcome!',
|
|
716
|
+
description: 'Extension description',
|
|
717
|
+
},
|
|
718
|
+
fr: {
|
|
719
|
+
welcome: 'Bienvenue!',
|
|
720
|
+
description: "Description de l'extension",
|
|
721
|
+
},
|
|
722
|
+
},
|
|
723
|
+
lastUpdated: 1684164163736,
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// Create mock data - using type assertion to avoid DeepPartial errors
|
|
727
|
+
interface MockExtension {
|
|
728
|
+
uuid: string
|
|
729
|
+
type: string
|
|
730
|
+
name: string
|
|
731
|
+
description?: string
|
|
732
|
+
localization?: any
|
|
733
|
+
extensionPoints?: {
|
|
734
|
+
localization?: any
|
|
735
|
+
target?: string
|
|
736
|
+
surface?: string
|
|
737
|
+
name?: string
|
|
738
|
+
}[]
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// Using a typed array to avoid DeepPartial issues
|
|
742
|
+
const mockExtensions: MockExtension[] = [
|
|
743
|
+
{
|
|
744
|
+
uuid: '123',
|
|
745
|
+
type: 'ui_extension',
|
|
746
|
+
name: 't:welcome',
|
|
747
|
+
description: 't:description',
|
|
748
|
+
localization,
|
|
749
|
+
extensionPoints: [{localization, target: 'admin.test', surface: 'admin'}],
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
uuid: '456',
|
|
753
|
+
type: 'ui_extension',
|
|
754
|
+
name: 'Extension 456',
|
|
755
|
+
description: 'This is a test extension',
|
|
756
|
+
localization: null,
|
|
757
|
+
extensionPoints: [{localization: null, target: 'admin.test', surface: 'admin'}],
|
|
758
|
+
},
|
|
759
|
+
{uuid: '789', type: 'product_subscription', name: 'Extension 789'},
|
|
760
|
+
]
|
|
761
|
+
|
|
762
|
+
// Send the update event
|
|
763
|
+
mockSocketServer.send({
|
|
764
|
+
event: 'update',
|
|
765
|
+
data: {
|
|
766
|
+
app: mockApp(),
|
|
767
|
+
extensions: mockExtensions,
|
|
768
|
+
store: 'test-store',
|
|
769
|
+
},
|
|
770
|
+
})
|
|
771
|
+
|
|
772
|
+
// Verify event was handled
|
|
773
|
+
expect(updateSpy).toHaveBeenCalledTimes(1)
|
|
774
|
+
|
|
775
|
+
// Assert on specific properties without using complex matchers that trigger type errors
|
|
776
|
+
const updatedData = updateSpy.mock.calls[0][0]
|
|
777
|
+
expect(updatedData).toBeDefined()
|
|
778
|
+
expect(updatedData.extensions).toHaveLength(3)
|
|
779
|
+
|
|
780
|
+
// Find the translated extension
|
|
781
|
+
const translatedExt = updatedData.extensions.find((ext: any) => ext.uuid === '123')
|
|
782
|
+
expect(translatedExt).toBeDefined()
|
|
783
|
+
|
|
784
|
+
// Check translation worked properly - name should be translated
|
|
785
|
+
expect(translatedExt.name).not.toBe('t:welcome')
|
|
786
|
+
// Description should be translated
|
|
787
|
+
expect(translatedExt.description).not.toBe('t:description')
|
|
788
|
+
})
|
|
789
|
+
|
|
790
|
+
test('sends data with translated props when locales option is provided on subsequent "update" events', async () => {
|
|
791
|
+
// Create client with locales using type assertion
|
|
792
|
+
const client = new ExtensionServerClient({
|
|
793
|
+
...defaultOptions,
|
|
794
|
+
locales: {user: 'ja', shop: 'fr'} as unknown as DeepPartial<any>,
|
|
795
|
+
})
|
|
796
|
+
|
|
797
|
+
// Mock connection
|
|
798
|
+
mockSocketServer.connect(mockSocket)
|
|
799
|
+
|
|
800
|
+
// Create spy
|
|
801
|
+
const updateSpy = vi.fn()
|
|
802
|
+
client.on('update', updateSpy)
|
|
803
|
+
|
|
804
|
+
// Define localization data
|
|
805
|
+
const localization: Localization = {
|
|
806
|
+
defaultLocale: 'en',
|
|
807
|
+
translations: {
|
|
808
|
+
ja: {
|
|
809
|
+
welcome: 'いらっしゃいませ!',
|
|
810
|
+
description: '拡張子の説明',
|
|
811
|
+
},
|
|
812
|
+
en: {
|
|
813
|
+
welcome: 'Welcome!',
|
|
814
|
+
description: 'Extension description',
|
|
815
|
+
},
|
|
816
|
+
fr: {
|
|
817
|
+
welcome: 'Bienvenue!',
|
|
818
|
+
description: "Description de l'extension",
|
|
819
|
+
},
|
|
820
|
+
},
|
|
821
|
+
lastUpdated: 1684164163736,
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// Create mock data - using type assertion to avoid DeepPartial errors
|
|
825
|
+
interface MockExtension {
|
|
826
|
+
uuid: string
|
|
827
|
+
type: string
|
|
828
|
+
name: string
|
|
829
|
+
description?: string
|
|
830
|
+
localization?: any
|
|
831
|
+
extensionPoints?: {
|
|
832
|
+
localization?: any
|
|
833
|
+
target?: string
|
|
834
|
+
surface?: string
|
|
835
|
+
name?: string
|
|
836
|
+
}[]
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// Using a typed array to avoid DeepPartial issues
|
|
840
|
+
const mockExtensions: MockExtension[] = [
|
|
841
|
+
{
|
|
842
|
+
uuid: '123',
|
|
843
|
+
type: 'ui_extension',
|
|
844
|
+
name: 't:welcome',
|
|
845
|
+
description: 't:description',
|
|
846
|
+
localization,
|
|
847
|
+
extensionPoints: [{localization, target: 'admin.test', surface: 'admin'}],
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
uuid: '456',
|
|
851
|
+
type: 'ui_extension',
|
|
852
|
+
name: 'Extension 456',
|
|
853
|
+
description: 'This is a test extension',
|
|
854
|
+
localization: null,
|
|
855
|
+
extensionPoints: [{localization: null, target: 'admin.test', surface: 'admin'}],
|
|
856
|
+
},
|
|
857
|
+
{uuid: '789', type: 'product_subscription', name: 'Extension 789'},
|
|
858
|
+
]
|
|
859
|
+
|
|
860
|
+
// Send the update event twice
|
|
861
|
+
mockSocketServer.send({
|
|
862
|
+
event: 'update',
|
|
863
|
+
data: {
|
|
864
|
+
app: mockApp(),
|
|
865
|
+
extensions: mockExtensions,
|
|
866
|
+
store: 'test-store',
|
|
867
|
+
},
|
|
868
|
+
})
|
|
869
|
+
|
|
870
|
+
mockSocketServer.send({
|
|
871
|
+
event: 'update',
|
|
872
|
+
data: {
|
|
873
|
+
app: mockApp(),
|
|
874
|
+
extensions: mockExtensions,
|
|
875
|
+
store: 'test-store',
|
|
876
|
+
},
|
|
877
|
+
})
|
|
878
|
+
|
|
879
|
+
// Verify event was handled twice
|
|
880
|
+
expect(updateSpy).toHaveBeenCalledTimes(2)
|
|
881
|
+
|
|
882
|
+
// Check the second call - make sure translations still work
|
|
883
|
+
const secondCallData = updateSpy.mock.calls[1][0]
|
|
884
|
+
expect(secondCallData).toBeDefined()
|
|
885
|
+
expect(secondCallData.extensions).toHaveLength(3)
|
|
886
|
+
|
|
887
|
+
// Find the translated extension
|
|
888
|
+
const translatedExt = secondCallData.extensions.find((ext: any) => ext.uuid === '123')
|
|
889
|
+
expect(translatedExt).toBeDefined()
|
|
890
|
+
|
|
891
|
+
// Check translation worked properly - name should be translated
|
|
892
|
+
expect(translatedExt.name).not.toBe('t:welcome')
|
|
893
|
+
// Description should be translated
|
|
894
|
+
expect(translatedExt.description).not.toBe('t:description')
|
|
895
|
+
})
|
|
896
|
+
|
|
897
|
+
test('handles localized extension props correctly', async () => {
|
|
898
|
+
// Create mock extension with translations
|
|
899
|
+
interface MockExtension {
|
|
900
|
+
uuid: string
|
|
901
|
+
type: string
|
|
902
|
+
name: string
|
|
903
|
+
description?: string
|
|
904
|
+
localization?: any
|
|
905
|
+
extensionPoints?: {
|
|
906
|
+
localization?: any
|
|
907
|
+
target?: string
|
|
908
|
+
surface?: string
|
|
909
|
+
name?: string
|
|
910
|
+
}[]
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
const mockExtension: MockExtension = {
|
|
914
|
+
uuid: '123',
|
|
915
|
+
type: 'ui_extension',
|
|
916
|
+
name: 't:extension.name',
|
|
917
|
+
description: 't:extension.description',
|
|
918
|
+
localization: {
|
|
919
|
+
translations: JSON.stringify({
|
|
920
|
+
'extension.name': 'Extension Name',
|
|
921
|
+
'extension.description': 'Extension Description',
|
|
922
|
+
}),
|
|
923
|
+
default: 'en',
|
|
924
|
+
},
|
|
925
|
+
extensionPoints: [
|
|
926
|
+
{
|
|
927
|
+
target: 'admin.product.item.action',
|
|
928
|
+
name: 't:extension.point.name',
|
|
929
|
+
},
|
|
930
|
+
],
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// Create client with locales option
|
|
934
|
+
const client = new ExtensionServerClient({
|
|
935
|
+
connection: {
|
|
936
|
+
url: TEST_CONNECTION_URL,
|
|
937
|
+
automaticConnect: true,
|
|
938
|
+
},
|
|
939
|
+
locales: {user: 'en', shop: 'en'} as unknown as DeepPartial<any>,
|
|
940
|
+
})
|
|
941
|
+
|
|
942
|
+
// Mock connection
|
|
943
|
+
mockSocketServer.connect(mockSocket)
|
|
944
|
+
|
|
945
|
+
// Create spy for translating extension
|
|
946
|
+
const translateSpy = vi.spyOn(client as any, '_getLocalizedValue')
|
|
947
|
+
translateSpy.mockImplementation((translations, key) => {
|
|
948
|
+
if (key === 't:extension.name') return 'Extension Name'
|
|
949
|
+
if (key === 't:extension.description') return 'Extension Description'
|
|
950
|
+
return key
|
|
951
|
+
})
|
|
952
|
+
|
|
953
|
+
// Create spy for the connected event
|
|
954
|
+
const connectSpy = vi.fn()
|
|
955
|
+
client.on('connected', connectSpy)
|
|
956
|
+
|
|
957
|
+
// Send connected event with mock data
|
|
958
|
+
const data = {
|
|
959
|
+
app: mockApp(),
|
|
960
|
+
extensions: [mockExtension],
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// Send the event
|
|
964
|
+
mockSocketServer.send({event: 'connected', data})
|
|
965
|
+
|
|
966
|
+
// Verify translated props
|
|
967
|
+
expect(connectSpy).toHaveBeenCalledTimes(1)
|
|
968
|
+
const callData = connectSpy.mock.calls[0][0]
|
|
969
|
+
const extension = callData.extensions[0]
|
|
970
|
+
|
|
971
|
+
// Check that the translation worked
|
|
972
|
+
expect(extension.name).toBe('Extension Name')
|
|
973
|
+
expect(extension.description).toBe('Extension Description')
|
|
974
|
+
|
|
975
|
+
// Clean up
|
|
976
|
+
translateSpy.mockRestore()
|
|
977
|
+
})
|
|
978
|
+
})
|
|
979
|
+
|
|
980
|
+
describe('emit()', () => {
|
|
981
|
+
test('emits an event', async () => {
|
|
982
|
+
// Create client
|
|
983
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
984
|
+
|
|
985
|
+
// Mock connection
|
|
986
|
+
mockSocketServer.connect(mockSocket)
|
|
987
|
+
|
|
988
|
+
// Emit an event
|
|
989
|
+
client.emit('unfocus')
|
|
990
|
+
|
|
991
|
+
// Verify the correct message was sent
|
|
992
|
+
expect(mockSocketServer.messages.length).toBe(1)
|
|
993
|
+
|
|
994
|
+
// Parse the JSON if it's a string
|
|
995
|
+
const message =
|
|
996
|
+
typeof mockSocketServer.messages[0] === 'string'
|
|
997
|
+
? JSON.parse(mockSocketServer.messages[0])
|
|
998
|
+
: mockSocketServer.messages[0]
|
|
999
|
+
|
|
1000
|
+
expect(message).toMatchObject({
|
|
1001
|
+
event: 'dispatch',
|
|
1002
|
+
data: {type: 'unfocus'},
|
|
1003
|
+
})
|
|
1004
|
+
})
|
|
1005
|
+
|
|
1006
|
+
test('warns if trying to "emit" a persist event', async () => {
|
|
1007
|
+
// Spy on console.warn
|
|
1008
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
|
|
1009
|
+
|
|
1010
|
+
// Create client
|
|
1011
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
1012
|
+
|
|
1013
|
+
// Mock connection
|
|
1014
|
+
mockSocketServer.connect(mockSocket)
|
|
1015
|
+
|
|
1016
|
+
// Try to emit an invalid event type
|
|
1017
|
+
client.emit('update' as any)
|
|
1018
|
+
|
|
1019
|
+
// Verify warning was shown
|
|
1020
|
+
expect(warnSpy).toHaveBeenCalled()
|
|
1021
|
+
|
|
1022
|
+
// Restore console.warn
|
|
1023
|
+
warnSpy.mockRestore()
|
|
1024
|
+
})
|
|
1025
|
+
})
|
|
1026
|
+
|
|
1027
|
+
describe('persist()', () => {
|
|
1028
|
+
test('persists a mutation', async () => {
|
|
1029
|
+
// Create client
|
|
1030
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
1031
|
+
|
|
1032
|
+
// Mock connection
|
|
1033
|
+
mockSocketServer.connect(mockSocket)
|
|
1034
|
+
|
|
1035
|
+
// Persist data
|
|
1036
|
+
const extensionData = {extensions: [{uuid: '123'}]} as any
|
|
1037
|
+
client.persist('update', extensionData)
|
|
1038
|
+
|
|
1039
|
+
// Verify the correct message was sent
|
|
1040
|
+
expect(mockSocketServer.messages.length).toBe(1)
|
|
1041
|
+
|
|
1042
|
+
// Parse the JSON if it's a string
|
|
1043
|
+
const message =
|
|
1044
|
+
typeof mockSocketServer.messages[0] === 'string'
|
|
1045
|
+
? JSON.parse(mockSocketServer.messages[0])
|
|
1046
|
+
: mockSocketServer.messages[0]
|
|
1047
|
+
|
|
1048
|
+
expect(message).toMatchObject({
|
|
1049
|
+
event: 'update',
|
|
1050
|
+
data: extensionData,
|
|
1051
|
+
})
|
|
1052
|
+
})
|
|
1053
|
+
|
|
1054
|
+
test('warns if trying to "persist" a dispatch event', async () => {
|
|
1055
|
+
// Spy on console.warn
|
|
1056
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
|
|
1057
|
+
|
|
1058
|
+
// Create client
|
|
1059
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
1060
|
+
|
|
1061
|
+
// Mock connection
|
|
1062
|
+
mockSocketServer.connect(mockSocket)
|
|
1063
|
+
|
|
1064
|
+
// Try to persist an invalid event type
|
|
1065
|
+
client.persist('unfocus' as any, {})
|
|
1066
|
+
|
|
1067
|
+
// Verify warning was shown
|
|
1068
|
+
expect(warnSpy).toHaveBeenCalled()
|
|
1069
|
+
|
|
1070
|
+
// Restore console.warn
|
|
1071
|
+
warnSpy.mockRestore()
|
|
1072
|
+
})
|
|
1073
|
+
|
|
1074
|
+
test('remove translated props from the UI extensions payload when locales are provided in the client options', async () => {
|
|
1075
|
+
// Create client with locales using type assertion
|
|
1076
|
+
const client = new ExtensionServerClient({
|
|
1077
|
+
...defaultOptions,
|
|
1078
|
+
locales: {user: 'ja', shop: 'fr'} as unknown as DeepPartial<any>,
|
|
1079
|
+
})
|
|
1080
|
+
|
|
1081
|
+
// Mock connection
|
|
1082
|
+
mockSocketServer.connect(mockSocket)
|
|
1083
|
+
|
|
1084
|
+
// Create mock data with translated fields
|
|
1085
|
+
const extensionData = {
|
|
1086
|
+
extensions: [
|
|
1087
|
+
{
|
|
1088
|
+
uuid: '123',
|
|
1089
|
+
type: 'ui_extension',
|
|
1090
|
+
name: 'いらっしゃいませ!',
|
|
1091
|
+
description: '拡張子の説明',
|
|
1092
|
+
localization: {},
|
|
1093
|
+
extensionPoints: [{localization: {}, name: 'いらっしゃいませ!', description: '拡張子の説明'}],
|
|
1094
|
+
},
|
|
1095
|
+
],
|
|
1096
|
+
} as any
|
|
1097
|
+
|
|
1098
|
+
// Persist the data
|
|
1099
|
+
client.persist('update', extensionData)
|
|
1100
|
+
|
|
1101
|
+
// Verify the message was sent and normalized (translation fields removed)
|
|
1102
|
+
expect(mockSocketServer.messages.length).toBe(1)
|
|
1103
|
+
|
|
1104
|
+
// Parse the JSON if it's a string
|
|
1105
|
+
const message =
|
|
1106
|
+
typeof mockSocketServer.messages[0] === 'string'
|
|
1107
|
+
? JSON.parse(mockSocketServer.messages[0])
|
|
1108
|
+
: mockSocketServer.messages[0]
|
|
1109
|
+
|
|
1110
|
+
// Check translation fields were removed
|
|
1111
|
+
expect(message).toMatchObject({
|
|
1112
|
+
event: 'update',
|
|
1113
|
+
data: {
|
|
1114
|
+
extensions: [{uuid: '123', type: 'ui_extension', extensionPoints: [{}]}],
|
|
1115
|
+
},
|
|
1116
|
+
})
|
|
1117
|
+
})
|
|
1118
|
+
|
|
1119
|
+
test('leave translatable props as-is in the UI extensions payload when locales are not provided in the client options', async () => {
|
|
1120
|
+
// Create client without locales
|
|
1121
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
1122
|
+
|
|
1123
|
+
// Mock connection
|
|
1124
|
+
mockSocketServer.connect(mockSocket)
|
|
1125
|
+
|
|
1126
|
+
// Create data with localization fields
|
|
1127
|
+
const extensionData = {
|
|
1128
|
+
extensions: [{uuid: '123', type: 'ui_extension', localization: {}, extensionPoints: [{localization: {}}]}],
|
|
1129
|
+
} as any
|
|
1130
|
+
|
|
1131
|
+
// Persist the data
|
|
1132
|
+
client.persist('update', extensionData)
|
|
1133
|
+
|
|
1134
|
+
// Verify the message was sent without changes
|
|
1135
|
+
expect(mockSocketServer.messages.length).toBe(1)
|
|
1136
|
+
|
|
1137
|
+
// Parse the JSON if it's a string
|
|
1138
|
+
const message =
|
|
1139
|
+
typeof mockSocketServer.messages[0] === 'string'
|
|
1140
|
+
? JSON.parse(mockSocketServer.messages[0])
|
|
1141
|
+
: mockSocketServer.messages[0]
|
|
1142
|
+
|
|
1143
|
+
// Check fields were preserved
|
|
1144
|
+
expect(message).toMatchObject({
|
|
1145
|
+
event: 'update',
|
|
1146
|
+
data: extensionData,
|
|
1147
|
+
})
|
|
1148
|
+
})
|
|
1149
|
+
})
|
|
1150
|
+
|
|
1151
|
+
describe('connect()', () => {
|
|
1152
|
+
test('updates the client options', () => {
|
|
1153
|
+
// Create client without initial options
|
|
1154
|
+
const client = new ExtensionServerClient()
|
|
1155
|
+
|
|
1156
|
+
// Then connect with options
|
|
1157
|
+
client.connect({connection: {automaticConnect: false}})
|
|
1158
|
+
|
|
1159
|
+
// Verify options were updated
|
|
1160
|
+
expect(client.options.connection).toMatchObject({
|
|
1161
|
+
automaticConnect: false,
|
|
1162
|
+
})
|
|
1163
|
+
})
|
|
1164
|
+
|
|
1165
|
+
test('does not attempt to connect if the URL is undefined', () => {
|
|
1166
|
+
// Create client
|
|
1167
|
+
const client = new ExtensionServerClient()
|
|
1168
|
+
|
|
1169
|
+
// Try to connect without URL
|
|
1170
|
+
client.connect()
|
|
1171
|
+
|
|
1172
|
+
// Verify no connection was made
|
|
1173
|
+
expect(client.connection).toBeUndefined()
|
|
1174
|
+
})
|
|
1175
|
+
|
|
1176
|
+
test('does not attempt to connect if the URL is empty', () => {
|
|
1177
|
+
// Create client with empty URL
|
|
1178
|
+
const client = new ExtensionServerClient({connection: {url: ''}})
|
|
1179
|
+
|
|
1180
|
+
// Try to connect
|
|
1181
|
+
client.connect()
|
|
1182
|
+
|
|
1183
|
+
// Verify no connection was made
|
|
1184
|
+
expect(client.connection).toBeUndefined()
|
|
1185
|
+
})
|
|
1186
|
+
|
|
1187
|
+
test('re-use existing connection if connect options have not changed', async () => {
|
|
1188
|
+
// Create client
|
|
1189
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
1190
|
+
|
|
1191
|
+
// Mock connection
|
|
1192
|
+
mockSocketServer.connect(mockSocket)
|
|
1193
|
+
|
|
1194
|
+
// Get initial connection
|
|
1195
|
+
const initialConnection = client.connection
|
|
1196
|
+
expect(initialConnection).toBeDefined()
|
|
1197
|
+
|
|
1198
|
+
// Try to connect with the same URL
|
|
1199
|
+
client.connect({connection: {url: TEST_CONNECTION_URL}})
|
|
1200
|
+
|
|
1201
|
+
// Connection should be the same object (reused)
|
|
1202
|
+
expect(client.connection).toBe(initialConnection)
|
|
1203
|
+
|
|
1204
|
+
// Only one connection should exist
|
|
1205
|
+
expect(mockSocketServer.clients.length).toBe(1)
|
|
1206
|
+
})
|
|
1207
|
+
|
|
1208
|
+
test('creates a new connection if the URL has changed', async () => {
|
|
1209
|
+
// Create client
|
|
1210
|
+
const client = new ExtensionServerClient(defaultOptions)
|
|
1211
|
+
|
|
1212
|
+
// Connect the mock socket
|
|
1213
|
+
mockSocketServer.connect(mockSocket)
|
|
1214
|
+
|
|
1215
|
+
// Store the original connection
|
|
1216
|
+
const originalConnection = client.connection
|
|
1217
|
+
|
|
1218
|
+
// Create a WebSocket spy to track new instances
|
|
1219
|
+
const webSocketSpy = vi.spyOn(globalThis, 'WebSocket')
|
|
1220
|
+
const initialCallCount = webSocketSpy.mock.calls.length
|
|
1221
|
+
|
|
1222
|
+
// Change the URL
|
|
1223
|
+
const newUrl = 'ws://new-host.com:9000/extensions/'
|
|
1224
|
+
client.connect({
|
|
1225
|
+
connection: {
|
|
1226
|
+
url: newUrl,
|
|
1227
|
+
},
|
|
1228
|
+
})
|
|
1229
|
+
|
|
1230
|
+
// Verify a new WebSocket was created
|
|
1231
|
+
expect(webSocketSpy).toHaveBeenCalledTimes(initialCallCount + 1)
|
|
1232
|
+
expect(webSocketSpy).toHaveBeenLastCalledWith(newUrl, [])
|
|
1233
|
+
|
|
1234
|
+
// Verify connection is different
|
|
1235
|
+
expect(client.connection).not.toBe(originalConnection)
|
|
1236
|
+
})
|
|
1237
|
+
})
|
|
1238
|
+
})
|