@applicaster/zapp-react-native-utils 14.0.0-alpha.1054425138 → 14.0.0-alpha.1235043154

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.
Files changed (48) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +82 -53
  2. package/analyticsUtils/AnalyticsEvents/sendHeaderClickEvent.ts +1 -1
  3. package/analyticsUtils/AnalyticsEvents/sendMenuClickEvent.ts +2 -1
  4. package/analyticsUtils/index.tsx +3 -4
  5. package/analyticsUtils/manager.ts +1 -1
  6. package/appUtils/accessibilityManager/index.ts +4 -1
  7. package/appUtils/contextKeysManager/contextResolver.ts +1 -15
  8. package/arrayUtils/__tests__/isEmptyArray.test.ts +63 -0
  9. package/arrayUtils/__tests__/isFilledArray.test.ts +1 -1
  10. package/arrayUtils/index.ts +7 -2
  11. package/audioPlayerUtils/__tests__/getArtworkImage.test.ts +144 -0
  12. package/audioPlayerUtils/__tests__/getBackgroundImage.test.ts +72 -0
  13. package/audioPlayerUtils/__tests__/getImageFromEntry.test.ts +110 -0
  14. package/audioPlayerUtils/assets/index.ts +2 -0
  15. package/audioPlayerUtils/index.ts +242 -0
  16. package/conf/player/__tests__/selectors.test.ts +34 -0
  17. package/conf/player/selectors.ts +10 -0
  18. package/configurationUtils/__tests__/configurationUtils.test.js +0 -31
  19. package/configurationUtils/__tests__/getMediaItems.test.ts +65 -0
  20. package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +34 -0
  21. package/configurationUtils/index.ts +63 -34
  22. package/manifestUtils/_internals/getDefaultConfiguration.js +28 -0
  23. package/manifestUtils/{_internals.js → _internals/index.js} +2 -25
  24. package/manifestUtils/createConfig.js +4 -1
  25. package/manifestUtils/defaultManifestConfigurations/player.js +1231 -200
  26. package/manifestUtils/progressBar/__tests__/mobileProgressBar.test.js +0 -30
  27. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +1 -2
  28. package/package.json +2 -2
  29. package/playerUtils/__tests__/configurationUtils.test.ts +1 -65
  30. package/playerUtils/__tests__/getPlayerActionButtons.test.ts +54 -0
  31. package/playerUtils/_internals/__tests__/utils.test.ts +71 -0
  32. package/playerUtils/_internals/index.ts +1 -0
  33. package/playerUtils/_internals/utils.ts +31 -0
  34. package/playerUtils/configurationUtils.ts +0 -44
  35. package/playerUtils/getPlayerActionButtons.ts +17 -0
  36. package/playerUtils/index.ts +8 -0
  37. package/playerUtils/useValidatePlayerConfig.tsx +22 -19
  38. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +12 -13
  39. package/reactHooks/cell-click/index.ts +1 -5
  40. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +39 -88
  41. package/reactHooks/feed/useBatchLoading.ts +2 -2
  42. package/reactHooks/feed/useFeedLoader.tsx +6 -13
  43. package/reactHooks/navigation/index.ts +2 -2
  44. package/testUtils/index.tsx +7 -8
  45. package/utils/index.ts +12 -1
  46. package/actionsExecutor/ScreenActions.ts +0 -90
  47. package/actionsExecutor/StorageActions.ts +0 -110
  48. package/playerUtils/configurationGenerator.ts +0 -2572
@@ -16,6 +16,7 @@ import { QUICK_BRICK_EVENTS } from "@applicaster/zapp-react-native-bridge/QuickB
16
16
  import { showConfirmationDialog } from "../alertUtils";
17
17
  import { createCloudEvent, sendCloudEvent } from "../cloudEventsUtils";
18
18
  import { createLogger } from "../logger";
19
+ import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
19
20
  import { ACTIVE_LAYOUT_ID_STORAGE_KEY } from "@applicaster/quick-brick-core/App/remoteContextReloader/consts";
20
21
  import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
21
22
  import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
@@ -24,20 +25,15 @@ import {
24
25
  resolveObjectValues,
25
26
  } from "../appUtils/contextKeysManager/contextResolver";
26
27
  import { useNavigation } from "../reactHooks";
28
+ import { get } from "lodash";
27
29
 
28
30
  import {
29
31
  useContentTypes,
30
32
  usePickFromState,
31
33
  } from "@applicaster/zapp-react-native-redux/hooks";
32
- import { useSubscriberFor } from "../reactHooks/useSubscriberFor";
34
+ import { TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT } from "./consts";
35
+ import { postEvent, useSubscriberFor } from "../reactHooks/useSubscriberFor";
33
36
  import { APP_EVENTS } from "../appUtils/events";
34
- import {
35
- localStorageToggleFlag,
36
- sessionStorageToggleFlag,
37
- } from "./StorageActions";
38
-
39
- import { ScreenSingleValueProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/ScreenSingleValueProvider";
40
- import { screenSetVariable, screenToggleFlag } from "./ScreenActions";
41
37
 
42
38
  export const { log_error, log_info, log_debug } = createLogger({
43
39
  subsystem: "ActionExecutorContext",
@@ -86,6 +82,19 @@ function findParentComponent(
86
82
  return null;
87
83
  }
88
84
 
85
+ // send all data just in case (like for message string formatting)
86
+ // Type is not exported for now
87
+ type MaxTagsReachedEvent = {
88
+ selectedItems: string[];
89
+ maxItems: number;
90
+ tag: string;
91
+ keyNamespace: string;
92
+ };
93
+
94
+ async function onMaxTagsReached(data: MaxTagsReachedEvent) {
95
+ postEvent(TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT, [data]);
96
+ }
97
+
89
98
  const prepareDefaultActions = (actionExecutor) => {
90
99
  actionExecutor.registerAction("localStorageSet", async (action) => {
91
100
  const namespaces = action.options.content;
@@ -169,16 +178,10 @@ const prepareDefaultActions = (actionExecutor) => {
169
178
 
170
179
  const entry = context?.entry || {};
171
180
  const entryResolver = new EntryResolver(entry);
172
- const route = context.screenRoute;
173
- const screenData = ScreenSingleValueProvider.getState(route) as any;
174
- const screenResolver = new EntryResolver(screenData || {});
175
181
 
176
182
  const data =
177
183
  options?.data && options.inflateData
178
- ? await resolveObjectValues(options.data, {
179
- entry: entryResolver,
180
- screen: screenResolver,
181
- })
184
+ ? await resolveObjectValues(options.data, { entry: entryResolver })
182
185
  : options?.data || entry;
183
186
 
184
187
  const cloudEvent = await createCloudEvent({
@@ -212,55 +215,81 @@ const prepareDefaultActions = (actionExecutor) => {
212
215
  return ActionResult.Error;
213
216
  });
214
217
 
215
- actionExecutor.registerAction(
216
- "sessionStorageToggleFlag",
217
- async (
218
- action: ActionType,
219
- context?: Record<string, any>
220
- ): Promise<ActionResult> => {
221
- return await sessionStorageToggleFlag(context, action);
222
- }
223
- );
224
-
225
218
  actionExecutor.registerAction(
226
219
  "localStorageToggleFlag",
227
220
  async (
228
221
  action: ActionType,
229
222
  context?: Record<string, any>
230
223
  ): Promise<ActionResult> => {
231
- return await localStorageToggleFlag(context, action);
232
- }
233
- );
224
+ if (!context) {
225
+ log_error(
226
+ "handleAction: localStorageToggleFlag action missing context"
227
+ );
234
228
 
235
- actionExecutor.registerAction(
236
- "screenSetVariable",
237
- async (
238
- action: ActionType,
239
- context?: Record<string, any>
240
- ): Promise<ActionResult> => {
241
- const route = context?.screenRoute;
242
- const { key, value } = action.options;
243
- screenSetVariable(route, key, value);
229
+ return ActionResult.Error;
230
+ }
244
231
 
245
- return Promise.resolve(ActionResult.Success);
246
- }
247
- );
232
+ const entry = context?.entry as ZappEntry;
248
233
 
249
- actionExecutor.registerAction(
250
- "screenToggleFlag",
251
- async (
252
- action: ActionType,
253
- context?: Record<string, any>
254
- ): Promise<ActionResult> => {
255
- const screenRoute = context?.screenRoute;
234
+ if (!entry) {
235
+ log_error(
236
+ "handleAction: localStorageToggleFlag action missing entry. Entry is required to get the tag."
237
+ );
256
238
 
257
- await screenToggleFlag(
258
- screenRoute,
259
- { entry: context?.entry, options: action.options },
260
- action
261
- );
239
+ return ActionResult.Error;
240
+ }
241
+
242
+ const tag = action.options?.selector
243
+ ? get(entry, action.options.selector)
244
+ : (entry.extensions?.tag ?? entry.id);
245
+
246
+ const keyNamespace = action.options?.key;
247
+
248
+ if (keyNamespace && tag) {
249
+ const multiSelectProvider =
250
+ StorageMultiSelectProvider.getProvider(keyNamespace);
251
+
252
+ const selectedItems = await multiSelectProvider.getSelectedAsync();
253
+ const isTagInSelectedItems = selectedItems.includes(tag);
254
+
255
+ log_info(
256
+ `handleAction: localStorageToggleFlag event will ${
257
+ isTagInSelectedItems ? "remove" : "add"
258
+ } tag: ${tag} for keyNamespace: ${keyNamespace}, current selectedItems: ${selectedItems}`
259
+ );
262
260
 
263
- return Promise.resolve(ActionResult.Success);
261
+ if (selectedItems.includes(tag)) {
262
+ await multiSelectProvider.removeItem(tag);
263
+ } else {
264
+ const maxItems = action.options?.max_items;
265
+
266
+ if (maxItems && selectedItems.length >= maxItems) {
267
+ log_info(
268
+ `handleAction: localStorageToggleFlag event reached max items limit: ${maxItems}, cannot add tag: ${tag}`
269
+ );
270
+
271
+ await onMaxTagsReached({
272
+ selectedItems,
273
+ maxItems,
274
+ tag,
275
+ keyNamespace,
276
+ });
277
+
278
+ return ActionResult.Cancel;
279
+ }
280
+
281
+ await multiSelectProvider.addItem(tag);
282
+ }
283
+ } else {
284
+ log_error(
285
+ "handleAction: localStorageToggleFlag event missing keyNamespace or tag",
286
+ { keyNamespace, tag }
287
+ );
288
+
289
+ return ActionResult.Error;
290
+ }
291
+
292
+ return ActionResult.Success;
264
293
  }
265
294
  );
266
295
  };
@@ -4,7 +4,7 @@ import { postAnalyticEvent } from "../manager";
4
4
  import { ANALYTICS_CORE_EVENTS } from "../events";
5
5
 
6
6
  type SendHeaderClickEventProps = {
7
- extraProps: ExtraProps;
7
+ extraProps: Record<string, any>;
8
8
  component?: ZappUIComponent;
9
9
  zappPipesData?: ZappPipesData;
10
10
  item?: ZappEntry;
@@ -1,10 +1,11 @@
1
+ /// <reference types="../../" />
1
2
  import { log_error, log_debug } from "../logger";
2
3
  import { replaceAnalyticsPropsNils } from "./helper";
3
4
  import { postAnalyticEvent } from "../manager";
4
5
 
5
6
  import { ANALYTICS_CORE_EVENTS } from "../events";
6
7
 
7
- declare type AnalyticsDefaultHelperProperties = {
8
+ type AnalyticsDefaultHelperProperties = {
8
9
  analyticsScreenData: AnalyticsScreenProperties;
9
10
  extraProps: any;
10
11
  props;
@@ -1,4 +1,3 @@
1
- /// <reference types="@applicaster/zapp-react-native-utils" />
2
1
  import * as R from "ramda";
3
2
  import * as React from "react";
4
3
  import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
@@ -31,7 +30,7 @@ import { ANALYTICS_CORE_EVENTS } from "./events";
31
30
  import { noop } from "../functionUtils";
32
31
 
33
32
  type ComponentWithChildrenProps = {
34
- children: React.ReactChildren;
33
+ children: React.ReactElement;
35
34
  };
36
35
 
37
36
  export function sendSelectCellEvent(item, component, headerTitle, itemIndex) {
@@ -120,11 +119,11 @@ export function getAnalyticsFunctions({
120
119
  export const AnalyticsContext =
121
120
  React.createContext<GetAnalyticsFunctions>(noop);
122
121
 
123
- export function AnalyticsProvider(props: ComponentWithChildrenProps) {
122
+ export function AnalyticsProvider({ children }: ComponentWithChildrenProps) {
124
123
  return (
125
124
  // @ts-ignore - this is a valid context provider
126
125
  <AnalyticsContext.Provider value={getAnalyticsFunctions}>
127
- {props?.children}
126
+ {children}
128
127
  </AnalyticsContext.Provider>
129
128
  );
130
129
  }
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-use-before-define */
2
2
  import * as R from "ramda";
3
3
  import { NativeModules } from "react-native";
4
- import { ANALYTICS_CORE_EVENTS } from "@applicaster/zapp-react-native-utils/analyticsUtils/events";
4
+ import { ANALYTICS_CORE_EVENTS } from "./events";
5
5
 
6
6
  import { analyticsUtilsLogger } from "./logger";
7
7
 
@@ -3,6 +3,7 @@ import { accessibilityManagerLogger as logger } from "./logger";
3
3
  import { TTSManager } from "../platform/platformUtils";
4
4
  import { BUTTON_ACCESSIBILITY_KEYS } from "./const";
5
5
  import { AccessibilityRole } from "react-native";
6
+ import _ from "lodash";
6
7
 
7
8
  export class AccessibilityManager {
8
9
  private static _instance: AccessibilityManager | null = null;
@@ -135,7 +136,9 @@ export class AccessibilityManager {
135
136
  });
136
137
  }
137
138
 
138
- public getButtonAccessibilityProps(buttonName: string): AccessibilityProps {
139
+ public getButtonAccessibilityProps(name: string): AccessibilityProps {
140
+ const buttonName = _.toString(name);
141
+
139
142
  const buttonConfig = BUTTON_ACCESSIBILITY_KEYS[buttonName];
140
143
 
141
144
  if (!buttonConfig) {
@@ -1,12 +1,10 @@
1
1
  import { ContextKeysManager } from "./index";
2
2
  import * as R from "ramda";
3
- import { screenStates } from "@applicaster/zapp-react-native-bridge/ZappStorage/ScreenSingleValueProvider";
4
3
 
5
- export interface IResolver {
4
+ interface IResolver {
6
5
  resolve: (string) => Promise<string | number | object>;
7
6
  }
8
7
 
9
- // TODO: Rename to ObjectKeyResolver or similar
10
8
  export class EntryResolver implements IResolver {
11
9
  entry: ZappEntry;
12
10
 
@@ -23,18 +21,6 @@ export class EntryResolver implements IResolver {
23
21
  }
24
22
  }
25
23
 
26
- // TODO: Move to proper place
27
-
28
- export class ScreenStateResolver implements IResolver {
29
- constructor(private route: string) {}
30
-
31
- async resolve(key: string) {
32
- const screenState = screenStates[this.route];
33
-
34
- return screenState?.[key];
35
- }
36
- }
37
-
38
24
  export class ContextResolver implements IResolver {
39
25
  resolve = async (compositeKey: string) =>
40
26
  ContextKeysManager.instance.getKey(compositeKey);
@@ -0,0 +1,63 @@
1
+ import { isEmptyArray } from "..";
2
+
3
+ describe("isEmptyArray", () => {
4
+ it("non-empty array is not empty", () => {
5
+ const value = [1, 2, 3];
6
+
7
+ expect(isEmptyArray(value)).toBe(false);
8
+ });
9
+
10
+ it("empty array is empty", () => {
11
+ const value = [];
12
+
13
+ expect(isEmptyArray(value)).toBe(true);
14
+ });
15
+
16
+ it("number is not array", () => {
17
+ const value = 123;
18
+
19
+ expect(isEmptyArray(value)).toBe(false);
20
+ });
21
+
22
+ it("string is not array", () => {
23
+ const value = "vfnjdk";
24
+
25
+ expect(isEmptyArray(value)).toBe(false);
26
+ });
27
+
28
+ it("empty string is not array", () => {
29
+ const value = "";
30
+
31
+ expect(isEmptyArray(value)).toBe(false);
32
+ });
33
+
34
+ it("NaN is not array", () => {
35
+ const value = NaN;
36
+
37
+ expect(isEmptyArray(value)).toBe(false);
38
+ });
39
+
40
+ it("object is not array", () => {
41
+ const value = { test: 1 };
42
+
43
+ expect(isEmptyArray(value)).toBe(false);
44
+ });
45
+
46
+ it("empty object is not array", () => {
47
+ const value = {};
48
+
49
+ expect(isEmptyArray(value)).toBe(false);
50
+ });
51
+
52
+ it("undefined is not array", () => {
53
+ const value = undefined;
54
+
55
+ expect(isEmptyArray(value)).toBe(false);
56
+ });
57
+
58
+ it("null is not array", () => {
59
+ const value = null;
60
+
61
+ expect(isEmptyArray(value)).toBe(false);
62
+ });
63
+ });
@@ -32,7 +32,7 @@ describe("isFilledArray", () => {
32
32
  });
33
33
 
34
34
  it("NaN is not array", () => {
35
- const value = "";
35
+ const value = NaN;
36
36
 
37
37
  expect(isFilledArray(value)).toBe(false);
38
38
  });
@@ -99,12 +99,17 @@ export const makeListOf = (value: unknown, size: number): number[] => {
99
99
 
100
100
  /** Checks if a value is a non-empty array */
101
101
  export function isFilledArray(value: unknown): boolean {
102
- return R.is(Array, value) && R.length(value) > 0;
102
+ return Array.isArray(value) && value.length > 0;
103
+ }
104
+
105
+ /** Checks if a value is a empty array */
106
+ export function isEmptyArray(value: unknown): boolean {
107
+ return Array.isArray(value) && value.length === 0;
103
108
  }
104
109
 
105
110
  // get random item from the list
106
111
  export const sample = (xs: unknown[]): unknown => {
107
- invariant(R.is(Array, xs), `input value is not a array: ${xs}`);
112
+ invariant(Array.isArray(xs), `input value is not an array: ${xs}`);
108
113
  invariant(isFilledArray(xs), `input array is empty: ${xs}`);
109
114
 
110
115
  const index = Math.floor(Math.random() * xs.length);
@@ -0,0 +1,144 @@
1
+ import { getArtworkImage } from "..";
2
+ import { DEFAULT_IMAGE } from "../assets";
3
+
4
+ describe("getArtworkImage", () => {
5
+ const entryWithImage = {
6
+ media_group: [
7
+ {
8
+ type: "image",
9
+ media_item: [
10
+ { key: "artwork_key", src: "image_from_entry" },
11
+ { key: "other_key", src: "other_image" },
12
+ ],
13
+ },
14
+ ],
15
+ extensions: {
16
+ artwork_key: "artwork_key",
17
+ },
18
+ };
19
+
20
+ const entryWithoutImage = {
21
+ media_group: [
22
+ {
23
+ type: "image",
24
+ media_item: [{ key: "other_key", src: "other_image" }],
25
+ },
26
+ ],
27
+ extensions: {
28
+ artwork_key: "artwork_key",
29
+ },
30
+ };
31
+
32
+ const pluginConfigWithImage = {
33
+ artwork_key: "plugin_artwork_key",
34
+ };
35
+
36
+ it("returns image from entry extensions key", () => {
37
+ const result = getArtworkImage({
38
+ key: "artwork_key",
39
+ entry: entryWithImage,
40
+ plugin_configuration: {},
41
+ });
42
+
43
+ expect(result).toBe("image_from_entry");
44
+ });
45
+
46
+ it("returns image from plugin configuration key if not in entry", () => {
47
+ const entryNoMatch = {
48
+ ...entryWithoutImage,
49
+ extensions: { artwork_key: "not_found_key" },
50
+ };
51
+
52
+ const pluginConfig = {
53
+ artwork_key: "plugin_artwork_key",
54
+ };
55
+
56
+ const entryWithPluginImage = {
57
+ ...entryNoMatch,
58
+ media_group: [
59
+ {
60
+ type: "image",
61
+ media_item: [{ key: "plugin_artwork_key", src: "image_from_plugin" }],
62
+ },
63
+ ],
64
+ };
65
+
66
+ const result = getArtworkImage({
67
+ key: "artwork_key",
68
+ entry: entryWithPluginImage,
69
+ plugin_configuration: pluginConfig,
70
+ });
71
+
72
+ expect(result).toBe("image_from_plugin");
73
+ });
74
+
75
+ it("returns DEFAULT_IMAGE if neither entry nor plugin configuration has image", () => {
76
+ const entryNoImage = {
77
+ media_group: [
78
+ {
79
+ type: "image",
80
+ media_item: [{ key: "other_key", src: "other_image" }],
81
+ },
82
+ ],
83
+ extensions: { artwork_key: "not_found_key" },
84
+ };
85
+
86
+ const pluginConfig = { artwork_key: "not_found_key" };
87
+
88
+ const result = getArtworkImage({
89
+ key: "artwork_key",
90
+ entry: entryNoImage,
91
+ plugin_configuration: pluginConfig,
92
+ });
93
+
94
+ expect(result).toBe(DEFAULT_IMAGE);
95
+ });
96
+
97
+ it("handles undefined key gracefully", () => {
98
+ const result = getArtworkImage({
99
+ key: undefined,
100
+ entry: entryWithImage,
101
+ plugin_configuration: pluginConfigWithImage,
102
+ });
103
+
104
+ expect(result).toBe(DEFAULT_IMAGE);
105
+ });
106
+
107
+ it("handles missing entry or plugin_configuration gracefully", () => {
108
+ const result = getArtworkImage({
109
+ key: "artwork_key",
110
+ entry: undefined,
111
+ plugin_configuration: undefined,
112
+ });
113
+
114
+ expect(result).toBe(DEFAULT_IMAGE);
115
+ });
116
+
117
+ it("handles empty media_group", () => {
118
+ const entryEmptyMedia = {
119
+ media_group: [],
120
+ extensions: { artwork_key: "artwork_key" },
121
+ };
122
+
123
+ const result = getArtworkImage({
124
+ key: "artwork_key",
125
+ entry: entryEmptyMedia,
126
+ plugin_configuration: pluginConfigWithImage,
127
+ });
128
+
129
+ expect(result).toBe(DEFAULT_IMAGE);
130
+ });
131
+
132
+ it("handles missing extensions/config keys", () => {
133
+ const entryNoExt = { media_group: [], extensions: {} };
134
+ const pluginConfig = {};
135
+
136
+ const result = getArtworkImage({
137
+ key: "artwork_key",
138
+ entry: entryNoExt,
139
+ plugin_configuration: pluginConfig,
140
+ });
141
+
142
+ expect(result).toBe(DEFAULT_IMAGE);
143
+ });
144
+ });
@@ -0,0 +1,72 @@
1
+ import { getBackgroundImage } from "..";
2
+ import { DEFAULT_IMAGE } from "../assets";
3
+
4
+ describe("getBackgroundImage", () => {
5
+ const entryBase = {
6
+ media_group: [
7
+ {
8
+ media_item: [
9
+ { key: "image_key_1", src: "image_src_1" },
10
+ { key: "image_key_2", src: "image_src_2" },
11
+ ],
12
+ type: "image",
13
+ },
14
+ ],
15
+ extensions: {},
16
+ };
17
+
18
+ const pluginConfigBase = {};
19
+
20
+ it("returns image from entry.extensions.image_key if present", () => {
21
+ const entry = {
22
+ ...entryBase,
23
+ extensions: { image_key: "image_key_2" },
24
+ };
25
+
26
+ const result = getBackgroundImage({
27
+ entry,
28
+ plugin_configuration: pluginConfigBase,
29
+ });
30
+
31
+ expect(result).toBe("image_src_2");
32
+ });
33
+
34
+ it("returns audio_player_background_image from entry.extensions if present and no image_key image", () => {
35
+ const entry = {
36
+ ...entryBase,
37
+ extensions: { audio_player_background_image: "audio_img_ext" },
38
+ };
39
+
40
+ const result = getBackgroundImage({
41
+ entry,
42
+ plugin_configuration: pluginConfigBase,
43
+ });
44
+
45
+ expect(result).toBe("audio_img_ext");
46
+ });
47
+
48
+ it("returns image from plugin_configuration.audio_player_image_key if present and not found in entry.extensions", () => {
49
+ const entry = { ...entryBase };
50
+ const plugin_configuration = { audio_player_image_key: "image_key_1" };
51
+ const result = getBackgroundImage({ entry, plugin_configuration });
52
+ expect(result).toBe("image_src_1");
53
+ });
54
+
55
+ it("returns audio_player_background_image from plugin_configuration if present and not found in entry/extensions", () => {
56
+ const entry = { ...entryBase };
57
+
58
+ const plugin_configuration = {
59
+ audio_player_background_image: "audio_img_conf",
60
+ };
61
+
62
+ const result = getBackgroundImage({ entry, plugin_configuration });
63
+ expect(result).toBe("audio_img_conf");
64
+ });
65
+
66
+ it("returns DEFAULT_IMAGE if nothing is found", () => {
67
+ const entry = { media_group: [], extensions: {} };
68
+ const plugin_configuration = {};
69
+ const result = getBackgroundImage({ entry, plugin_configuration });
70
+ expect(result).toBe(DEFAULT_IMAGE);
71
+ });
72
+ });