@retailcrm/embed-ui-v1-testing 0.4.5
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/README.md +16 -0
- package/lib/createHandler.ts +33 -0
- package/lib/rpc.ts +93 -0
- package/package.json +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# `@retailcrm/embed-ui-v1-testing`
|
|
2
|
+
|
|
3
|
+
Утилиты для упрощения тестирования логики JS-расширений RetailCRM
|
|
4
|
+
|
|
5
|
+
## Установка
|
|
6
|
+
|
|
7
|
+
npm:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i --save-dev @retailcrm/embed-ui-v1-testing
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
yarn:
|
|
14
|
+
```bash
|
|
15
|
+
yarn add -D @retailcrm/embed-ui-v1-testing
|
|
16
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ContextSchema,
|
|
3
|
+
EventHandler,
|
|
4
|
+
EventMap,
|
|
5
|
+
TypeOf,
|
|
6
|
+
FieldGetters,
|
|
7
|
+
} from '@retailcrm/embed-ui-v1-types/context'
|
|
8
|
+
|
|
9
|
+
import { retain } from '@remote-ui/rpc'
|
|
10
|
+
import { watch } from 'vue'
|
|
11
|
+
|
|
12
|
+
const keysOf = <T extends object>(o: T): (keyof T)[] => Object.keys(o) as (keyof T)[]
|
|
13
|
+
|
|
14
|
+
export const createHandler = <S extends ContextSchema>(id: string, getters: FieldGetters<S>) => {
|
|
15
|
+
type EventName = keyof EventMap<S>
|
|
16
|
+
type FieldName = keyof S
|
|
17
|
+
|
|
18
|
+
const map = keysOf(getters).reduce((eventMap, field) => {
|
|
19
|
+
eventMap[`change:${String(field)}` as EventName] = field as EventMap<S>[EventName]
|
|
20
|
+
|
|
21
|
+
return eventMap
|
|
22
|
+
}, {} as EventMap<S>)
|
|
23
|
+
|
|
24
|
+
return <E extends keyof EventMap<S>>(event: E, handler: EventHandler<S, E>) => {
|
|
25
|
+
if (!(event in map)) {
|
|
26
|
+
throw new Error(`[crm:embed:host] Event ${String(event)} is not supported in context ${id}`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
retain(handler)
|
|
30
|
+
|
|
31
|
+
watch(getters[map[event] as FieldName], handler as (payload: TypeOf<S[FieldName]>) => void)
|
|
32
|
+
}
|
|
33
|
+
}
|
package/lib/rpc.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
class MessagePortPolyfill implements MessagePort {
|
|
2
|
+
onmessageerror: EventListener | null = null
|
|
3
|
+
|
|
4
|
+
otherPort!: MessagePortPolyfill
|
|
5
|
+
private listeners = new Set<EventListener>()
|
|
6
|
+
|
|
7
|
+
// MessagePort does not send messages unless it is started via start() or attaching .onmessage
|
|
8
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/start
|
|
9
|
+
private started = false
|
|
10
|
+
private _onmessage: EventListener | null = null
|
|
11
|
+
|
|
12
|
+
// If the port is not yet started, messages will be queued for sending.
|
|
13
|
+
private eventQueue: Event[] = []
|
|
14
|
+
|
|
15
|
+
get onmessage() {
|
|
16
|
+
return this._onmessage
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
set onmessage(listener: EventListener | null) {
|
|
20
|
+
// setting onmessage will start the port, even if the listener is null.
|
|
21
|
+
this._onmessage = listener
|
|
22
|
+
this.start()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
dispatchEvent(event: Event) {
|
|
26
|
+
if (!this.started) {
|
|
27
|
+
this.eventQueue.push(event)
|
|
28
|
+
return true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (this._onmessage) {
|
|
32
|
+
this._onmessage(event)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
for (const listener of this.listeners) {
|
|
36
|
+
listener(event)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
postMessage(message: unknown) {
|
|
43
|
+
if (!this.otherPort) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.otherPort.dispatchEvent({ data: message } as unknown as Event)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
addEventListener(type: string, listener: EventListener) {
|
|
51
|
+
if (type !== 'message') {
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.listeners.add(listener)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
removeEventListener(type: string, listener: EventListener) {
|
|
59
|
+
if (type !== 'message') {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.listeners.delete(listener)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
start() {
|
|
67
|
+
this.started = true
|
|
68
|
+
while (this.eventQueue.length > 0) {
|
|
69
|
+
const event = this.eventQueue.shift()
|
|
70
|
+
if (event) {
|
|
71
|
+
this.dispatchEvent(event)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
close() {
|
|
77
|
+
this.started = false
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
class MessageChannelPolyfill implements MessageChannel {
|
|
82
|
+
readonly port1: MessagePortPolyfill
|
|
83
|
+
readonly port2: MessagePortPolyfill
|
|
84
|
+
|
|
85
|
+
constructor() {
|
|
86
|
+
this.port1 = new MessagePortPolyfill()
|
|
87
|
+
this.port2 = new MessagePortPolyfill()
|
|
88
|
+
this.port1.otherPort = this.port2
|
|
89
|
+
this.port2.otherPort = this.port1
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export { MessageChannelPolyfill as MessageChannel }
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@retailcrm/embed-ui-v1-testing",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.4.5",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "RetailDriverLLC <integration@retailcrm.ru>",
|
|
7
|
+
"repository": "git@github.com:retailcrm/embed-ui.git",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": null,
|
|
10
|
+
"./*": "./*"
|
|
11
|
+
},
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"@omnicajs/vue-remote": "^0.2.3",
|
|
14
|
+
"@remote-ui/rpc": "^1.4.5",
|
|
15
|
+
"vue": "^3.5"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@retailcrm/embed-ui-v1-types": "^0.4.5"
|
|
19
|
+
}
|
|
20
|
+
}
|