@raydiant/playlist-window-manager 0.20.0 → 0.21.1
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/build/BrowserViewRenderer.d.ts +3 -2
- package/build/BrowserViewRenderer.js +47 -23
- package/build/BrowserWindowRenderer.d.ts +3 -2
- package/build/BrowserWindowRenderer.js +66 -42
- package/build/IFrameRenderer.d.ts +3 -2
- package/build/IFrameRenderer.js +38 -28
- package/build/PlaylistWindowManager.js +14 -11
- package/build/PlaylistWindowManagerState.js +3 -3
- package/build/PresentationWindow.d.ts +9 -2
- package/build/PresentationWindow.js +241 -86
- package/build/PresentationWindow.test.d.ts +1 -0
- package/build/PresentationWindow.test.js +241 -0
- package/build/WindowLoadError.js +3 -1
- package/build/WindowTimeoutError.js +3 -1
- package/build/WindowUnresponsiveError.js +3 -1
- package/build/embeddedApps.js +7 -0
- package/build/embeddedApps.test.js +42 -32
- package/build/index.js +19 -6
- package/build/testUtils.d.ts +43 -0
- package/build/testUtils.js +208 -0
- package/build/types.d.ts +7 -2
- package/package.json +2 -2
- package/src/BrowserViewRenderer.ts +18 -7
- package/src/BrowserWindowRenderer.ts +29 -17
- package/src/IFrameRenderer.ts +27 -10
- package/src/PresentationWindow.test.ts +221 -0
- package/src/PresentationWindow.ts +232 -59
- package/src/embeddedApps.test.ts +39 -20
- package/src/embeddedApps.ts +13 -4
- package/src/testUtils.ts +159 -0
- package/src/types.ts +14 -3
- package/tasks/npm-publish.sh +1 -1
package/src/embeddedApps.test.ts
CHANGED
|
@@ -13,11 +13,11 @@ const createPresentation = (
|
|
|
13
13
|
r: {
|
|
14
14
|
resource: {
|
|
15
15
|
updatedAt: '',
|
|
16
|
-
createdAt: ''
|
|
16
|
+
createdAt: '',
|
|
17
17
|
},
|
|
18
|
-
presentationsPlaylists: []
|
|
18
|
+
presentationsPlaylists: [],
|
|
19
19
|
},
|
|
20
|
-
...overrides
|
|
20
|
+
...overrides,
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
const createApplicationVersion = (
|
|
@@ -30,14 +30,14 @@ const createApplicationVersion = (
|
|
|
30
30
|
configurableDuration: false,
|
|
31
31
|
sourceUrl: null,
|
|
32
32
|
presentationProperties: [],
|
|
33
|
-
...overrides
|
|
33
|
+
...overrides,
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
const createMetadata = (
|
|
37
37
|
overrides: Partial<EmbeddedAppMetadata> = {}
|
|
38
38
|
): EmbeddedAppMetadata => ({
|
|
39
39
|
renderMode: 'live',
|
|
40
|
-
...overrides
|
|
40
|
+
...overrides,
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
test('Replaces variable in query parameteers', () => {
|
|
@@ -45,7 +45,7 @@ test('Replaces variable in query parameteers', () => {
|
|
|
45
45
|
createEmbeddedApp(
|
|
46
46
|
createPresentation({ applicationVariables: { foo: 'bar' } }),
|
|
47
47
|
createApplicationVersion({
|
|
48
|
-
embeddedUrlFormat: 'https://example.com?foo=:foo'
|
|
48
|
+
embeddedUrlFormat: 'https://example.com?foo=:foo',
|
|
49
49
|
}),
|
|
50
50
|
createMetadata()
|
|
51
51
|
)
|
|
@@ -57,7 +57,7 @@ test('Replaces variable in path', () => {
|
|
|
57
57
|
createEmbeddedApp(
|
|
58
58
|
createPresentation({ applicationVariables: { foo: 'bar' } }),
|
|
59
59
|
createApplicationVersion({
|
|
60
|
-
embeddedUrlFormat: 'https://example.com/:foo'
|
|
60
|
+
embeddedUrlFormat: 'https://example.com/:foo',
|
|
61
61
|
}),
|
|
62
62
|
createMetadata()
|
|
63
63
|
)
|
|
@@ -69,7 +69,7 @@ test('Replaces undefined variable with empty string', () => {
|
|
|
69
69
|
createEmbeddedApp(
|
|
70
70
|
createPresentation({ applicationVariables: {} }),
|
|
71
71
|
createApplicationVersion({
|
|
72
|
-
embeddedUrlFormat: 'https://example.com/:foo'
|
|
72
|
+
embeddedUrlFormat: 'https://example.com/:foo',
|
|
73
73
|
}),
|
|
74
74
|
createMetadata()
|
|
75
75
|
)
|
|
@@ -81,7 +81,7 @@ test('Replaces variable with underscore', () => {
|
|
|
81
81
|
createEmbeddedApp(
|
|
82
82
|
createPresentation({ applicationVariables: { foo_1: 'bar' } }),
|
|
83
83
|
createApplicationVersion({
|
|
84
|
-
embeddedUrlFormat: 'https://example.com/:foo_1'
|
|
84
|
+
embeddedUrlFormat: 'https://example.com/:foo_1',
|
|
85
85
|
}),
|
|
86
86
|
createMetadata()
|
|
87
87
|
)
|
|
@@ -93,7 +93,7 @@ test('Replaces variable with hyphen', () => {
|
|
|
93
93
|
createEmbeddedApp(
|
|
94
94
|
createPresentation({ applicationVariables: { 'foo-1': 'bar' } }),
|
|
95
95
|
createApplicationVersion({
|
|
96
|
-
embeddedUrlFormat: 'https://example.com/:foo-1'
|
|
96
|
+
embeddedUrlFormat: 'https://example.com/:foo-1',
|
|
97
97
|
}),
|
|
98
98
|
createMetadata()
|
|
99
99
|
)
|
|
@@ -105,7 +105,7 @@ test('Does not replace variable that is a number', () => {
|
|
|
105
105
|
createEmbeddedApp(
|
|
106
106
|
createPresentation({ applicationVariables: { foo: 'bar' } }),
|
|
107
107
|
createApplicationVersion({
|
|
108
|
-
embeddedUrlFormat: 'http://localhost:8080?foo=:foo'
|
|
108
|
+
embeddedUrlFormat: 'http://localhost:8080?foo=:foo',
|
|
109
109
|
}),
|
|
110
110
|
createMetadata()
|
|
111
111
|
)
|
|
@@ -121,7 +121,7 @@ test('Encodes a variables containing special characters', () => {
|
|
|
121
121
|
createEmbeddedApp(
|
|
122
122
|
createPresentation({ applicationVariables: { foo: fooValue } }),
|
|
123
123
|
createApplicationVersion({
|
|
124
|
-
embeddedUrlFormat: `http://localhost:8080?foo=:foo
|
|
124
|
+
embeddedUrlFormat: `http://localhost:8080?foo=:foo`,
|
|
125
125
|
}),
|
|
126
126
|
createMetadata()
|
|
127
127
|
)
|
|
@@ -132,11 +132,13 @@ test('Does not encode value if presentation property disables encoding', () => {
|
|
|
132
132
|
expect(
|
|
133
133
|
createEmbeddedApp(
|
|
134
134
|
createPresentation({
|
|
135
|
-
applicationVariables: { url: 'https://raydiant.com' }
|
|
135
|
+
applicationVariables: { url: 'https://raydiant.com' },
|
|
136
136
|
}),
|
|
137
137
|
createApplicationVersion({
|
|
138
138
|
embeddedUrlFormat: `:url`,
|
|
139
|
-
presentationProperties: [
|
|
139
|
+
presentationProperties: [
|
|
140
|
+
{ type: 'string', name: 'url', encode: false },
|
|
141
|
+
],
|
|
140
142
|
}),
|
|
141
143
|
createMetadata()
|
|
142
144
|
)
|
|
@@ -152,7 +154,7 @@ test('Encodes a variables containing an array', () => {
|
|
|
152
154
|
createEmbeddedApp(
|
|
153
155
|
createPresentation({ applicationVariables: { foo: fooValue } }),
|
|
154
156
|
createApplicationVersion({
|
|
155
|
-
embeddedUrlFormat: `http://localhost:8080?foo=:foo
|
|
157
|
+
embeddedUrlFormat: `http://localhost:8080?foo=:foo`,
|
|
156
158
|
}),
|
|
157
159
|
createMetadata()
|
|
158
160
|
)
|
|
@@ -162,7 +164,7 @@ test('Encodes a variables containing an array', () => {
|
|
|
162
164
|
test('Replaces $renderMode with metadata', () => {
|
|
163
165
|
const presentation = createPresentation();
|
|
164
166
|
const applicationVersion = createApplicationVersion({
|
|
165
|
-
embeddedUrlFormat: 'https://example.com?renderMode=:$renderMode'
|
|
167
|
+
embeddedUrlFormat: 'https://example.com?renderMode=:$renderMode',
|
|
166
168
|
});
|
|
167
169
|
|
|
168
170
|
expect(
|
|
@@ -185,7 +187,7 @@ test('Replaces $renderMode with metadata', () => {
|
|
|
185
187
|
test('Replaces undefined metadata with empty string', () => {
|
|
186
188
|
const presentation = createPresentation();
|
|
187
189
|
const applicationVersion = createApplicationVersion({
|
|
188
|
-
embeddedUrlFormat: 'https://example.com?invalid=:$invalid'
|
|
190
|
+
embeddedUrlFormat: 'https://example.com?invalid=:$invalid',
|
|
189
191
|
});
|
|
190
192
|
|
|
191
193
|
expect(
|
|
@@ -200,13 +202,30 @@ test('Replace protocol, ip and port', () => {
|
|
|
200
202
|
applicationVariables: {
|
|
201
203
|
protocol: 'http',
|
|
202
204
|
ip: '192.168.1.0',
|
|
203
|
-
port: 3000
|
|
204
|
-
}
|
|
205
|
+
port: 3000,
|
|
206
|
+
},
|
|
205
207
|
}),
|
|
206
208
|
createApplicationVersion({
|
|
207
|
-
embeddedUrlFormat: `:protocol://:ip::port
|
|
209
|
+
embeddedUrlFormat: `:protocol://:ip::port`,
|
|
208
210
|
}),
|
|
209
211
|
createMetadata()
|
|
210
212
|
)
|
|
211
213
|
).toBe(`http://192.168.1.0:3000`);
|
|
212
214
|
});
|
|
215
|
+
|
|
216
|
+
test('File input uses url as value', () => {
|
|
217
|
+
expect(
|
|
218
|
+
createEmbeddedApp(
|
|
219
|
+
createPresentation({
|
|
220
|
+
applicationVariables: { file: { url: 'https://image.png' } },
|
|
221
|
+
}),
|
|
222
|
+
createApplicationVersion({
|
|
223
|
+
embeddedUrlFormat: `https://example.come?image=:file`,
|
|
224
|
+
presentationProperties: [{ type: 'file', name: 'file' }],
|
|
225
|
+
}),
|
|
226
|
+
createMetadata()
|
|
227
|
+
)
|
|
228
|
+
).toBe(
|
|
229
|
+
`https://example.come?image=${encodeURIComponent('https://image.png')}`
|
|
230
|
+
);
|
|
231
|
+
});
|
package/src/embeddedApps.ts
CHANGED
|
@@ -19,7 +19,7 @@ export default function createEmbeddedApp(
|
|
|
19
19
|
|
|
20
20
|
// replace
|
|
21
21
|
let url = applicationVersion.embeddedUrlFormat || '';
|
|
22
|
-
matches.forEach(m => {
|
|
22
|
+
matches.forEach((m) => {
|
|
23
23
|
const queryParamVariableName = m[1]; // Where the value of your input will be injected. E.g. ":location"
|
|
24
24
|
const queryParamName = m[2]; // The query param name. E.g. "location"
|
|
25
25
|
|
|
@@ -38,9 +38,10 @@ export default function createEmbeddedApp(
|
|
|
38
38
|
// port numbers in the embedded app url.
|
|
39
39
|
if (!isNaN(Number(variableName))) return;
|
|
40
40
|
|
|
41
|
-
const presentationProperty =
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
const presentationProperty =
|
|
42
|
+
applicationVersion.presentationProperties.find(
|
|
43
|
+
(p) => p.name === variableName
|
|
44
|
+
);
|
|
44
45
|
|
|
45
46
|
// If the presentation property is not found (this shouldn't happen), default to encoding the value.
|
|
46
47
|
// Otherwise, only skip encoding if the presentation property's encode property is false.
|
|
@@ -52,6 +53,14 @@ export default function createEmbeddedApp(
|
|
|
52
53
|
if (Array.isArray(value)) {
|
|
53
54
|
value = JSON.stringify(value);
|
|
54
55
|
}
|
|
56
|
+
|
|
57
|
+
// Presentation property specific handling.
|
|
58
|
+
if (presentationProperty) {
|
|
59
|
+
// If the presentation property is a file, use the url property as the value.
|
|
60
|
+
if (presentationProperty.type === 'file' && value && value.url) {
|
|
61
|
+
value = value.url;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
55
64
|
}
|
|
56
65
|
|
|
57
66
|
if (shouldEncodeValue) {
|
package/src/testUtils.ts
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import * as re from '@raydiant/playlist-rule-engine';
|
|
2
|
+
import PresentationWindow, {
|
|
3
|
+
PresentationWindowOptions,
|
|
4
|
+
} from './PresentationWindow';
|
|
5
|
+
import PlaylistWindowManagerState, {
|
|
6
|
+
PlaylistWindowManagerStateOptions,
|
|
7
|
+
} from './PlaylistWindowManagerState';
|
|
8
|
+
import {
|
|
9
|
+
Presentation,
|
|
10
|
+
ApplicationVersion,
|
|
11
|
+
Logger,
|
|
12
|
+
CreateWindowOptions,
|
|
13
|
+
} from './types';
|
|
14
|
+
|
|
15
|
+
export function createPresentationWindow(
|
|
16
|
+
overrides: Partial<PresentationWindowOptions> = {}
|
|
17
|
+
): PresentationWindow {
|
|
18
|
+
return new PresentationWindow({
|
|
19
|
+
windowId: overrides.windowId ?? 'test',
|
|
20
|
+
presentation: createPresentation(overrides.presentation),
|
|
21
|
+
applicationVersion: createApplicationVersion(overrides.applicationVersion),
|
|
22
|
+
publishedAt: overrides.publishedAt ?? 'publishedAt',
|
|
23
|
+
playlistWindowManagerState:
|
|
24
|
+
overrides.playlistWindowManagerState ??
|
|
25
|
+
createPlaylistWindowManagerState(),
|
|
26
|
+
ruleEngineState: overrides.ruleEngineState ?? createRuleEngineState(),
|
|
27
|
+
logger: createLogger(),
|
|
28
|
+
workerState: overrides.workerState ?? {},
|
|
29
|
+
renderer: overrides.renderer ?? {
|
|
30
|
+
createWindow() {},
|
|
31
|
+
async loadWindow() {},
|
|
32
|
+
updateWindowBounds() {},
|
|
33
|
+
showWindow() {},
|
|
34
|
+
hideWindow() {},
|
|
35
|
+
async removeWindow() {},
|
|
36
|
+
sendMessageString() {},
|
|
37
|
+
sendMessageObject() {},
|
|
38
|
+
muteWindow() {},
|
|
39
|
+
unMuteWindow() {},
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function createPresentation(
|
|
45
|
+
overrides: Partial<Presentation> = {}
|
|
46
|
+
): Presentation {
|
|
47
|
+
return {
|
|
48
|
+
id: overrides.id ?? 'id',
|
|
49
|
+
name: overrides.name ?? 'name',
|
|
50
|
+
applicationDeploymentId:
|
|
51
|
+
overrides.applicationDeploymentId ?? 'applicationDeploymentId',
|
|
52
|
+
themeId: overrides.themeId ?? null,
|
|
53
|
+
applicationVariables: overrides.applicationVariables ?? {},
|
|
54
|
+
duration: overrides.duration ?? 1,
|
|
55
|
+
r: createPresentationRelationships(overrides.r),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function createPresentationRelationships(
|
|
60
|
+
overrides: Partial<Presentation['r']> = {}
|
|
61
|
+
): Presentation['r'] {
|
|
62
|
+
return {
|
|
63
|
+
resource: overrides.resource ?? {
|
|
64
|
+
updatedAt: 'updatedAt',
|
|
65
|
+
createdAt: 'createdAt',
|
|
66
|
+
},
|
|
67
|
+
presentationsPlaylists: overrides.presentationsPlaylists ?? [],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function createApplicationVersion(
|
|
72
|
+
overrides: Partial<ApplicationVersion> = {}
|
|
73
|
+
): ApplicationVersion {
|
|
74
|
+
return {
|
|
75
|
+
id: overrides.id ?? 'id',
|
|
76
|
+
name: overrides.name ?? 'name',
|
|
77
|
+
applicationId: overrides.applicationId ?? 'applicationId',
|
|
78
|
+
embeddedUrlFormat: overrides.embeddedUrlFormat ?? null,
|
|
79
|
+
configurableDuration: overrides.configurableDuration ?? false,
|
|
80
|
+
sourceUrl: overrides.sourceUrl ?? null,
|
|
81
|
+
presentationProperties: overrides.presentationProperties ?? [],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function createPlaylistWindowManagerState(
|
|
86
|
+
overrides: Partial<PlaylistWindowManagerStateOptions> = {}
|
|
87
|
+
): PlaylistWindowManagerState {
|
|
88
|
+
return new PlaylistWindowManagerState({
|
|
89
|
+
presentations: overrides.presentations ?? {},
|
|
90
|
+
applicationVersions: overrides.applicationVersions ?? {},
|
|
91
|
+
themes: overrides.themes ?? {},
|
|
92
|
+
publishedAt: overrides.publishedAt ?? 'publishedAt',
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function createRuleEngineState(
|
|
97
|
+
playlists: {
|
|
98
|
+
[index: string]: re.Playlist;
|
|
99
|
+
} = {},
|
|
100
|
+
presentations: {
|
|
101
|
+
[index: string]: re.Presentation;
|
|
102
|
+
} = {},
|
|
103
|
+
rulesDataStore: re.RulesDataStore = {},
|
|
104
|
+
isMultitimezoneEnabled: boolean = false
|
|
105
|
+
): re.State {
|
|
106
|
+
return new re.State(
|
|
107
|
+
playlists,
|
|
108
|
+
presentations,
|
|
109
|
+
rulesDataStore,
|
|
110
|
+
isMultitimezoneEnabled
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function createLogger(overrides: Partial<Logger> = {}) {
|
|
115
|
+
const noop = () => {};
|
|
116
|
+
return {
|
|
117
|
+
debug: overrides.debug ?? noop,
|
|
118
|
+
info: overrides.info ?? noop,
|
|
119
|
+
warn: overrides.warn ?? noop,
|
|
120
|
+
error: overrides.error ?? noop,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function createRenderer() {
|
|
125
|
+
let createWindowOpts: CreateWindowOptions | null = null;
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
createWindow: jest.fn((opts: CreateWindowOptions) => {
|
|
129
|
+
createWindowOpts = opts;
|
|
130
|
+
}),
|
|
131
|
+
loadWindow: jest.fn(),
|
|
132
|
+
updateWindowBounds: jest.fn(),
|
|
133
|
+
showWindow: jest.fn(),
|
|
134
|
+
hideWindow: jest.fn(),
|
|
135
|
+
removeWindow: jest.fn(),
|
|
136
|
+
sendMessageString: jest.fn(),
|
|
137
|
+
sendMessageObject: jest.fn(),
|
|
138
|
+
muteWindow: jest.fn(),
|
|
139
|
+
unMuteWindow: jest.fn(),
|
|
140
|
+
runReceiveMessageCallback(type: string, payload: any) {
|
|
141
|
+
if (createWindowOpts?.receiveMessageCallback) {
|
|
142
|
+
createWindowOpts.receiveMessageCallback(type, payload);
|
|
143
|
+
} else {
|
|
144
|
+
throw new Error(
|
|
145
|
+
'No receiveMessageCallback defined, make sure to call window.load first'
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
runErrorCallback(error: Error) {
|
|
150
|
+
if (createWindowOpts?.errorCallback) {
|
|
151
|
+
createWindowOpts.errorCallback(error);
|
|
152
|
+
} else {
|
|
153
|
+
throw new Error(
|
|
154
|
+
'No errorCallback defined, make sure to call window.load first'
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -25,7 +25,11 @@ export interface ApplicationVersion {
|
|
|
25
25
|
embeddedUrlFormat: string | null;
|
|
26
26
|
configurableDuration: boolean;
|
|
27
27
|
sourceUrl: string | null;
|
|
28
|
-
presentationProperties: Array<{
|
|
28
|
+
presentationProperties: Array<{
|
|
29
|
+
name: string;
|
|
30
|
+
type: string;
|
|
31
|
+
encode?: boolean;
|
|
32
|
+
}>;
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
export interface Font {
|
|
@@ -74,6 +78,10 @@ export interface WorkerState {
|
|
|
74
78
|
[key: string]: any;
|
|
75
79
|
}
|
|
76
80
|
|
|
81
|
+
export interface PreviewState {
|
|
82
|
+
inputs: Record<string, any>;
|
|
83
|
+
}
|
|
84
|
+
|
|
77
85
|
export interface CreateWindowOptions {
|
|
78
86
|
windowId: string;
|
|
79
87
|
parentWindowId?: string;
|
|
@@ -119,8 +127,11 @@ export interface PresentationWindowRenderer {
|
|
|
119
127
|
// when the window is finished unloading.
|
|
120
128
|
removeWindow(windowId: string): Promise<void>;
|
|
121
129
|
|
|
122
|
-
// Called when sending a message to the inner window.
|
|
123
|
-
|
|
130
|
+
// Called when sending a message string to the inner window.
|
|
131
|
+
sendMessageString(windowId: string, message: string): void;
|
|
132
|
+
|
|
133
|
+
// Called when sending a message object to the inner window.
|
|
134
|
+
sendMessageObject(windowId: string, type: string, payload?: any): void;
|
|
124
135
|
|
|
125
136
|
// Called when muting a window.
|
|
126
137
|
muteWindow(windowId: string): void;
|
package/tasks/npm-publish.sh
CHANGED