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

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 (49) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +3 -3
  2. package/actionsExecutor/ScreenActions.ts +6 -4
  3. package/actionsExecutor/StorageActions.ts +1 -1
  4. package/actionsExecutor/feedDecorator.ts +171 -0
  5. package/actionsExecutor/screenResolver.ts +11 -0
  6. package/appUtils/accessibilityManager/index.ts +4 -1
  7. package/appUtils/contextKeysManager/contextResolver.ts +2 -3
  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/cell-click/index.ts +3 -0
  39. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +20 -0
  40. package/reactHooks/feed/useBatchLoading.ts +2 -2
  41. package/reactHooks/feed/useFeedLoader.tsx +3 -3
  42. package/reactHooks/navigation/useRoute.ts +7 -2
  43. package/reactHooks/navigation/useScreenStateStore.ts +11 -0
  44. package/storage/ScreenSingleValueProvider.ts +198 -0
  45. package/storage/ScreenStateMultiSelectProvider.ts +293 -0
  46. package/storage/StorageMultiSelectProvider.ts +192 -0
  47. package/storage/StorageSingleSelectProvider.ts +108 -0
  48. package/utils/index.ts +12 -1
  49. package/playerUtils/configurationGenerator.ts +0 -2572
@@ -36,7 +36,6 @@ import {
36
36
  sessionStorageToggleFlag,
37
37
  } from "./StorageActions";
38
38
 
39
- import { ScreenSingleValueProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/ScreenSingleValueProvider";
40
39
  import { screenSetVariable, screenToggleFlag } from "./ScreenActions";
41
40
 
42
41
  export const { log_error, log_info, log_debug } = createLogger({
@@ -169,8 +168,7 @@ const prepareDefaultActions = (actionExecutor) => {
169
168
 
170
169
  const entry = context?.entry || {};
171
170
  const entryResolver = new EntryResolver(entry);
172
- const route = context.screenRoute;
173
- const screenData = ScreenSingleValueProvider.getState(route) as any;
171
+ const screenData = context?.screenStateStore.getState().data || {};
174
172
  const screenResolver = new EntryResolver(screenData || {});
175
173
 
176
174
  const data =
@@ -253,9 +251,11 @@ const prepareDefaultActions = (actionExecutor) => {
253
251
  context?: Record<string, any>
254
252
  ): Promise<ActionResult> => {
255
253
  const screenRoute = context?.screenRoute;
254
+ const screenStateStore = context?.screenStateStore;
256
255
 
257
256
  await screenToggleFlag(
258
257
  screenRoute,
258
+ screenStateStore,
259
259
  { entry: context?.entry, options: action.options },
260
260
  action
261
261
  );
@@ -4,8 +4,8 @@ import { log_error, log_info } from "./ActionExecutorContext";
4
4
  import { ActionResult } from "./ActionExecutor";
5
5
  import { get } from "lodash";
6
6
 
7
- import { ScreenMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/ScreenStateMultiSelectProvider";
8
7
  import { onMaxTagsReached } from "./StorageActions";
8
+ import { ScreenMultiSelectProvider } from "../storage/ScreenStateMultiSelectProvider";
9
9
 
10
10
  export const screenSetVariable = (
11
11
  _screenRoute: string,
@@ -15,11 +15,12 @@ export const screenSetVariable = (
15
15
 
16
16
  export const screenToggleFlag = async (
17
17
  screenRoute: string,
18
+ screenStateStore: ScreenStateStore,
18
19
  context: Record<string, any>,
19
20
  action: ActionType
20
21
  ) => {
21
22
  if (!context) {
22
- log_error("handleAction: localStorageToggleFlag action missing context");
23
+ log_error("handleAction: screenToggleFlag action missing context");
23
24
 
24
25
  return ActionResult.Error;
25
26
  }
@@ -28,7 +29,7 @@ export const screenToggleFlag = async (
28
29
 
29
30
  if (!entry) {
30
31
  log_error(
31
- "handleAction: localStorageToggleFlag action missing entry. Entry is required to get the tag."
32
+ "handleAction: screenToggleFlag action missing entry. Entry is required to get the tag."
32
33
  );
33
34
 
34
35
  return ActionResult.Error;
@@ -43,7 +44,8 @@ export const screenToggleFlag = async (
43
44
  if (keyNamespace && tag) {
44
45
  const multiSelectProvider = ScreenMultiSelectProvider.getProvider(
45
46
  keyNamespace,
46
- screenRoute
47
+ screenRoute,
48
+ screenStateStore
47
49
  );
48
50
 
49
51
  const selectedItems = await multiSelectProvider.getSelectedAsync();
@@ -1,6 +1,6 @@
1
1
  import { ActionResult } from "./ActionExecutor";
2
2
  import { get } from "lodash";
3
- import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
3
+ import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-utils/storage/StorageMultiSelectProvider";
4
4
  import { log_error, log_info } from "./ActionExecutorContext";
5
5
  import { postEvent } from "../reactHooks/useSubscriberFor";
6
6
  import { TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT } from "./consts";
@@ -0,0 +1,171 @@
1
+ import * as R from "ramda";
2
+ import { getNamespaceAndKey } from "../appUtils/contextKeysManager/utils";
3
+ import { createLogger } from "../logger";
4
+
5
+ const { log_error, log_verbose } = createLogger({
6
+ subsystem: "FeedDecorator",
7
+ category: "General",
8
+ });
9
+
10
+ function getScope(scope) {
11
+ if (scope === "screen") {
12
+ return scope;
13
+ } else if (!scope || scope === "ctx") {
14
+ return "ctx";
15
+ } else {
16
+ log_verbose(
17
+ `decorateFeed: Unsupported scope "${scope}" provided in preference_editor_options. Defaulting to "ctx".`
18
+ );
19
+
20
+ return "ctx";
21
+ }
22
+ }
23
+
24
+ function makeMultiSelect(feed: ZappFeed, key, decoratedFeed) {
25
+ const scope = getScope(
26
+ decoratedFeed.extensions?.preference_editor_options?.scope
27
+ );
28
+
29
+ const behavior = {
30
+ ...feed.extensions?.["behavior"],
31
+ select_mode: "multi",
32
+ current_selection: `@{${scope}/${key}}`,
33
+ };
34
+
35
+ if (!feed.extensions) {
36
+ decoratedFeed.extensions = {
37
+ behavior,
38
+ };
39
+ } else {
40
+ decoratedFeed.extensions.behavior = behavior;
41
+ }
42
+
43
+ const actionType =
44
+ decoratedFeed.extensions?.preference_editor_options?.scope === "screen"
45
+ ? "screenToggleFlag"
46
+ : decoratedFeed.extensions?.preference_editor_options?.scope === "session"
47
+ ? "sessionStorageToggleFlag"
48
+ : "localStorageToggleFlag";
49
+
50
+ decoratedFeed.entry?.forEach((entry: ZappEntry) => {
51
+ entry.type.value = "action";
52
+ entry.id = entry.extensions?.tag || entry.id;
53
+
54
+ entry.extensions = {
55
+ ...(entry.extensions || {}),
56
+ tap_actions: {
57
+ actions: [
58
+ {
59
+ type: actionType,
60
+ options: {
61
+ key,
62
+ },
63
+ },
64
+ ],
65
+ },
66
+ };
67
+ });
68
+
69
+ return decoratedFeed;
70
+ }
71
+
72
+ function makeSingleSelect(feed: ZappFeed, key, decoratedFeed) {
73
+ const scope = getScope(
74
+ decoratedFeed.extensions?.preference_editor_options?.scope
75
+ );
76
+
77
+ const behavior = {
78
+ ...feed.extensions?.["behavior"],
79
+ select_mode: "single",
80
+ current_selection: `@{${scope}/${key}}`,
81
+ };
82
+
83
+ if (!feed.extensions) {
84
+ decoratedFeed.extensions = {
85
+ behavior,
86
+ };
87
+ } else {
88
+ decoratedFeed.extensions.behavior = behavior;
89
+ }
90
+
91
+ const localStorageProducer = (entry: ZappFeed) => {
92
+ entry.id = entry.extensions?.tag || entry.id;
93
+ const { key: keyName, namespace } = getNamespaceAndKey(key);
94
+
95
+ const action =
96
+ decoratedFeed.extensions?.preference_editor_options?.scope === "session"
97
+ ? "sessionStorageSet"
98
+ : "localStorageSet";
99
+
100
+ return {
101
+ type: action,
102
+ options: {
103
+ content: {
104
+ [namespace]: {
105
+ [keyName]: entry.id,
106
+ },
107
+ },
108
+ },
109
+ };
110
+ };
111
+
112
+ const screenStateProducer = (entry: ZappFeed) => {
113
+ entry.id = entry.extensions?.tag || entry.id;
114
+
115
+ return {
116
+ type: "screenSetVariable",
117
+ options: {
118
+ key: `@{screen/${key}}`,
119
+ value: entry.id,
120
+ },
121
+ };
122
+ };
123
+
124
+ const producer =
125
+ decoratedFeed.extensions?.preference_editor_options?.scope === "screen"
126
+ ? screenStateProducer
127
+ : localStorageProducer;
128
+
129
+ decoratedFeed.entry?.forEach((entry: ZappEntry) => {
130
+ entry.type.value = "action";
131
+
132
+ entry.extensions = {
133
+ ...(entry.extensions || {}),
134
+ tap_actions: {
135
+ actions: [producer(entry)],
136
+ },
137
+ };
138
+ });
139
+
140
+ return decoratedFeed;
141
+ }
142
+
143
+ export const decorateFeed = (feed: ZappFeed) => {
144
+ if (!(feed.extensions?.["role"] === "preference_editor")) {
145
+ return feed;
146
+ }
147
+
148
+ const key = feed.extensions?.["preference_editor_options"]?.["key"];
149
+
150
+ if (!key) {
151
+ log_error(
152
+ `decorateFeed: No 'key' provided in feed titled "${feed.title}" with id "${feed.id}" in preference_editor_options. The preference_editor role requires a key for local storage.`
153
+ );
154
+
155
+ throw new Error(
156
+ "decorateFeed: No 'key' provided in feed preference_editor_options"
157
+ );
158
+ }
159
+
160
+ const decoratedFeed = R.clone(feed);
161
+
162
+ const isSingleSelect =
163
+ (feed.extensions?.["preference_editor_options"]?.select_mode ||
164
+ feed.extensions?.["behavior"]?.select_mode) === "single";
165
+
166
+ if (isSingleSelect) {
167
+ return makeSingleSelect(feed, key, decoratedFeed);
168
+ } else {
169
+ return makeMultiSelect(feed, key, decoratedFeed);
170
+ }
171
+ };
@@ -0,0 +1,11 @@
1
+ import { useMemo } from "react";
2
+ import { ScreenStateResolver } from "../appUtils/contextKeysManager/contextResolver";
3
+ import { useScreenStateStore } from "../reactHooks/navigation/useScreenStateStore";
4
+
5
+ export const useScreenResolvers = () => {
6
+ const screenStateStore = useScreenStateStore();
7
+
8
+ return useMemo(() => {
9
+ return { screen: new ScreenStateResolver(screenStateStore) };
10
+ }, [screenStateStore]);
11
+ };
@@ -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,6 +1,5 @@
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
4
  export interface IResolver {
6
5
  resolve: (string) => Promise<string | number | object>;
@@ -26,10 +25,10 @@ export class EntryResolver implements IResolver {
26
25
  // TODO: Move to proper place
27
26
 
28
27
  export class ScreenStateResolver implements IResolver {
29
- constructor(private route: string) {}
28
+ constructor(private screenStateStore: ScreenStateStore) {}
30
29
 
31
30
  async resolve(key: string) {
32
- const screenState = screenStates[this.route];
31
+ const screenState = this.screenStateStore.getState().data;
33
32
 
34
33
  return screenState?.[key];
35
34
  }
@@ -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
+ });