@raydiant/playlist-window-manager 0.20.0 → 0.21.0

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.
@@ -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: [{ name: 'url', encode: false }]
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
+ });
@@ -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 = applicationVersion.presentationProperties.find(
42
- p => p.name === variableName
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) {
@@ -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<{ name: string; encode?: boolean }>;
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
- sendMessage(windowId: string, message: string): void;
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;