@applicaster/zapp-react-native-utils 14.0.0-alpha.1661204539 → 14.0.0-alpha.2247415563
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/actionsExecutor/ActionExecutorContext.tsx +82 -53
- package/analyticsUtils/AnalyticsEvents/sendHeaderClickEvent.ts +1 -1
- package/analyticsUtils/AnalyticsEvents/sendMenuClickEvent.ts +2 -1
- package/analyticsUtils/index.tsx +3 -4
- package/analyticsUtils/manager.ts +1 -1
- package/appUtils/accessibilityManager/index.ts +4 -1
- package/appUtils/contextKeysManager/contextResolver.ts +1 -15
- package/arrayUtils/__tests__/isEmptyArray.test.ts +63 -0
- package/arrayUtils/__tests__/isFilledArray.test.ts +1 -1
- package/arrayUtils/index.ts +7 -2
- package/audioPlayerUtils/__tests__/getArtworkImage.test.ts +144 -0
- package/audioPlayerUtils/__tests__/getBackgroundImage.test.ts +72 -0
- package/audioPlayerUtils/__tests__/getImageFromEntry.test.ts +110 -0
- package/audioPlayerUtils/assets/index.ts +2 -0
- package/audioPlayerUtils/index.ts +242 -0
- package/conf/player/__tests__/selectors.test.ts +34 -0
- package/conf/player/selectors.ts +10 -0
- package/configurationUtils/__tests__/configurationUtils.test.js +0 -31
- package/configurationUtils/__tests__/getMediaItems.test.ts +65 -0
- package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +34 -0
- package/configurationUtils/index.ts +63 -34
- package/manifestUtils/_internals/getDefaultConfiguration.js +28 -0
- package/manifestUtils/{_internals.js → _internals/index.js} +2 -25
- package/manifestUtils/createConfig.js +4 -1
- package/manifestUtils/defaultManifestConfigurations/player.js +1231 -200
- package/manifestUtils/progressBar/__tests__/mobileProgressBar.test.js +0 -30
- package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +1 -2
- package/package.json +2 -2
- package/playerUtils/__tests__/configurationUtils.test.ts +1 -65
- package/playerUtils/__tests__/getPlayerActionButtons.test.ts +54 -0
- package/playerUtils/_internals/__tests__/utils.test.ts +71 -0
- package/playerUtils/_internals/index.ts +1 -0
- package/playerUtils/_internals/utils.ts +31 -0
- package/playerUtils/configurationUtils.ts +0 -44
- package/playerUtils/getPlayerActionButtons.ts +17 -0
- package/playerUtils/index.ts +8 -0
- package/playerUtils/useValidatePlayerConfig.tsx +22 -19
- package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +12 -13
- package/reactHooks/cell-click/index.ts +1 -5
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +39 -88
- package/reactHooks/feed/useBatchLoading.ts +2 -2
- package/reactHooks/feed/useFeedLoader.tsx +6 -13
- package/reactHooks/navigation/index.ts +2 -2
- package/testUtils/index.tsx +7 -8
- package/utils/index.ts +12 -1
- package/actionsExecutor/ScreenActions.ts +0 -90
- package/actionsExecutor/StorageActions.ts +0 -110
- package/actionsExecutor/screenResolver.ts +0 -8
- package/playerUtils/configurationGenerator.ts +0 -2572
- package/storage/ScreenSingleValueProvider.ts +0 -92
- package/storage/ScreenStateMultiSelectProvider.ts +0 -119
- package/storage/StorageMultiSelectProvider.ts +0 -192
- package/storage/StorageSingleSelectProvider.ts +0 -108
|
@@ -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 {
|
|
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-utils/storage/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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
224
|
+
if (!context) {
|
|
225
|
+
log_error(
|
|
226
|
+
"handleAction: localStorageToggleFlag action missing context"
|
|
227
|
+
);
|
|
234
228
|
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
246
|
-
}
|
|
247
|
-
);
|
|
232
|
+
const entry = context?.entry as ZappEntry;
|
|
248
233
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
8
|
+
type AnalyticsDefaultHelperProperties = {
|
|
8
9
|
analyticsScreenData: AnalyticsScreenProperties;
|
|
9
10
|
extraProps: any;
|
|
10
11
|
props;
|
package/analyticsUtils/index.tsx
CHANGED
|
@@ -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.
|
|
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(
|
|
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
|
-
{
|
|
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 "
|
|
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(
|
|
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-utils/storage/ScreenSingleValueProvider";
|
|
4
3
|
|
|
5
|
-
|
|
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
|
+
});
|
package/arrayUtils/index.ts
CHANGED
|
@@ -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
|
|
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(
|
|
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
|
+
});
|