@applicaster/zapp-react-native-utils 15.1.0-rc.1 → 16.0.0-rc.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/README.md +0 -6
- package/actionUtils/index.ts +7 -0
- package/actionsExecutor/ActionExecutorContext.tsx +43 -12
- package/actionsExecutor/feedDecorator.ts +6 -6
- package/adsUtils/__tests__/createVMAP.test.ts +419 -0
- package/adsUtils/index.ts +2 -2
- package/analyticsUtils/README.md +1 -1
- package/appDataUtils/__tests__/urlScheme.test.ts +678 -0
- package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +0 -188
- package/appUtils/HooksManager/__tests__/hooksManager.test.js +16 -2
- package/appUtils/HooksManager/index.ts +10 -10
- package/appUtils/RiverFocusManager/{index.js → index.ts} +25 -18
- package/appUtils/accessibilityManager/const.ts +4 -0
- package/appUtils/accessibilityManager/utils.ts +0 -1
- package/appUtils/contextKeysManager/__tests__/getKeys/failure.test.ts +7 -2
- package/appUtils/contextKeysManager/__tests__/getKeys/success.test.ts +48 -0
- package/appUtils/contextKeysManager/contextResolver.ts +51 -22
- package/appUtils/contextKeysManager/index.ts +65 -10
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +3 -0
- package/appUtils/focusManager/index.ios.ts +43 -4
- package/appUtils/focusManager/treeDataStructure/Tree/__tests__/Tree.test.js +46 -0
- package/appUtils/focusManager/treeDataStructure/Tree/index.js +18 -18
- package/appUtils/focusManagerAux/utils/index.ios.ts +122 -0
- package/appUtils/focusManagerAux/utils/index.ts +11 -3
- package/appUtils/focusManagerAux/utils/utils.ios.ts +202 -3
- package/appUtils/keyCodes/keys/keys.web.ts +1 -4
- package/appUtils/orientationHelper.ts +2 -4
- package/appUtils/platform/platformUtils.ts +1 -1
- package/appUtils/playerManager/player.ts +4 -0
- package/appUtils/playerManager/playerNative.ts +30 -21
- package/appUtils/playerManager/usePlayerState.tsx +14 -2
- package/cloudEventsUtils/__tests__/index.test.ts +529 -0
- package/cloudEventsUtils/index.ts +65 -1
- package/componentsUtils/index.ts +8 -0
- package/configurationUtils/__tests__/manifestKeyParser.test.ts +26 -26
- package/enumUtils/__tests__/getEnumKeyByEnumValue.test.ts +207 -0
- package/errorUtils/__tests__/GeneralError.test.ts +97 -0
- package/errorUtils/__tests__/HttpStatusCode.test.ts +344 -0
- package/errorUtils/__tests__/MissingPluginError.test.ts +113 -0
- package/errorUtils/__tests__/NetworkError.test.ts +202 -0
- package/errorUtils/__tests__/getParsedResponse.test.ts +188 -0
- package/errorUtils/__tests__/invariant.test.ts +112 -0
- package/focusManager/aux/index.ts +1 -1
- package/headersUtils/__tests__/headersUtils.test.js +11 -1
- package/headersUtils/index.ts +2 -1
- package/manifestUtils/_internals/__tests__/index.test.js +41 -0
- package/manifestUtils/_internals/index.js +33 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +59 -1
- package/manifestUtils/fieldUtils/__tests__/fieldUtils.test.js +49 -0
- package/manifestUtils/fieldUtils/index.js +54 -0
- package/manifestUtils/index.js +2 -0
- package/manifestUtils/keys.js +228 -0
- package/manifestUtils/mobileAction/button/__tests__/mobileActionButton.test.js +168 -0
- package/manifestUtils/mobileAction/button/index.js +140 -0
- package/manifestUtils/mobileAction/container/__tests__/mobileActionButtonsContainer.test.js +102 -0
- package/manifestUtils/mobileAction/container/index.js +73 -0
- package/manifestUtils/mobileAction/groups/__tests__/buildMobileActionButtonGroups.test.js +127 -0
- package/manifestUtils/mobileAction/groups/defaults.js +76 -0
- package/manifestUtils/mobileAction/groups/index.js +80 -0
- package/manifestUtils/tvAction/container/index.js +1 -1
- package/numberUtils/__tests__/toNumber.test.ts +27 -0
- package/numberUtils/__tests__/toPositiveNumber.test.ts +193 -0
- package/numberUtils/index.ts +23 -1
- package/package.json +4 -4
- package/pluginUtils/index.ts +4 -0
- package/reactHooks/advertising/index.ts +2 -2
- package/reactHooks/analytics/__tests__/useSendAnalyticsOnPress.test.ts +537 -0
- package/reactHooks/app/__tests__/useAppState.test.ts +1 -1
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
- package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -2
- package/reactHooks/cell-click/__tests__/index.test.js +1 -3
- package/reactHooks/cell-click/index.ts +2 -1
- package/reactHooks/configuration/__tests__/index.test.tsx +1 -1
- package/reactHooks/connection/__tests__/index.test.js +1 -1
- package/reactHooks/debugging/__tests__/index.test.js +4 -4
- package/reactHooks/dev/__tests__/useReRenderLog.test.ts +188 -0
- package/reactHooks/device/useIsTablet.tsx +14 -19
- package/reactHooks/device/useMemoizedIsTablet.ts +3 -3
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +32 -23
- package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +19 -19
- package/reactHooks/feed/__tests__/useEntryScreenId.test.tsx +4 -1
- package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +42 -30
- package/reactHooks/feed/__tests__/useInflatedUrl.test.tsx +1 -1
- package/reactHooks/feed/index.ts +0 -2
- package/reactHooks/feed/useBatchLoading.ts +7 -1
- package/reactHooks/feed/useEntryScreenId.ts +2 -2
- package/reactHooks/feed/usePipesCacheReset.ts +3 -1
- package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +13 -16
- package/reactHooks/layout/__tests__/index.test.tsx +1 -1
- package/reactHooks/layout/__tests__/useLayoutVersion.test.tsx +1 -1
- package/reactHooks/layout/useDimensions/__tests__/{useDimensions.test.ts → useDimensions.test.tsx} +105 -25
- package/reactHooks/layout/useDimensions/useDimensions.ts +2 -2
- package/reactHooks/navigation/__tests__/index.test.tsx +2 -4
- package/reactHooks/navigation/index.ts +7 -6
- package/reactHooks/navigation/useRoute.ts +8 -6
- package/reactHooks/player/TVSeekControlller/TVSeekController.ts +27 -10
- package/reactHooks/player/__tests__/useAutoSeek._test.tsx +1 -1
- package/reactHooks/player/__tests__/useTapSeek._test.ts +1 -1
- package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +1 -1
- package/reactHooks/resolvers/__tests__/useComponentResolver.test.tsx +1 -1
- package/reactHooks/resolvers/useCellResolver.ts +6 -2
- package/reactHooks/resolvers/useComponentResolver.ts +8 -2
- package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +2 -2
- package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +1 -1
- package/reactHooks/screen/__tests__/useScreenData.test.tsx +1 -1
- package/reactHooks/screen/__tests__/useTargetScreenData.test.tsx +12 -4
- package/reactHooks/screen/index.ts +0 -2
- package/reactHooks/screen/useTargetScreenData.ts +4 -2
- package/reactHooks/state/useRivers.ts +1 -1
- package/reactHooks/ui/__tests__/useFadeOutWhenBlurred.test.ts +580 -0
- package/reactHooks/usePluginConfiguration.ts +2 -2
- package/reactHooks/utils/__tests__/index.test.js +1 -1
- package/rectUtils/__tests__/index.test.ts +549 -0
- package/rectUtils/index.ts +2 -2
- package/refreshUtils/RefreshCoordinator/__tests__/refreshCoordinator.test.ts +206 -0
- package/refreshUtils/RefreshCoordinator/index.ts +245 -0
- package/refreshUtils/RefreshCoordinator/utils/__tests__/getDataRefreshConfig.test.ts +104 -0
- package/refreshUtils/RefreshCoordinator/utils/index.ts +29 -0
- package/screenPickerUtils/__tests__/index.test.ts +333 -0
- package/screenState/__tests__/index.test.ts +1 -1
- package/screenUtils/index.ts +3 -0
- package/searchUtils/const.ts +7 -0
- package/searchUtils/index.ts +3 -0
- package/stringUtils/index.ts +1 -1
- package/testUtils/index.tsx +30 -21
- package/time/__tests__/BackgroundTimer.test.ts +156 -0
- package/time/__tests__/Timer.test.ts +236 -0
- package/typeGuards/__tests__/isString.test.ts +21 -0
- package/typeGuards/index.ts +4 -0
- package/utils/__tests__/mergeRight.test.ts +48 -0
- package/utils/__tests__/path.test.ts +7 -0
- package/utils/__tests__/selectors.test.ts +124 -0
- package/utils/index.ts +13 -0
- package/utils/mergeRight.ts +5 -0
- package/utils/path.ts +6 -3
- package/utils/pathOr.ts +5 -1
- package/utils/selectors.ts +46 -0
- package/zappFrameworkUtils/HookCallback/callbackNavigationAction.ts +1 -1
- package/zappFrameworkUtils/HookCallback/hookCallbackManifestExtensions.config.js +1 -1
- package/zappFrameworkUtils/loginPluginUtils.ts +1 -1
- package/reactHooks/componentsMap/index.ts +0 -55
- package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +0 -75
- package/reactHooks/feed/useFeedRefresh.tsx +0 -65
- package/reactHooks/screen/useIsStandaloneFullscreen.ts +0 -12
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { getParsedResponse } from "../index";
|
|
2
|
+
import { RESPONSES } from "../errorCodes";
|
|
3
|
+
|
|
4
|
+
describe("getParsedResponse", () => {
|
|
5
|
+
it("should parse error with response status code from response.data", () => {
|
|
6
|
+
const err = {
|
|
7
|
+
response: {
|
|
8
|
+
data: {
|
|
9
|
+
statusCode: 404,
|
|
10
|
+
message: "Resource not found",
|
|
11
|
+
},
|
|
12
|
+
config: {
|
|
13
|
+
url: "https://api.example.com/data",
|
|
14
|
+
params: { id: 123 },
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
name: "AxiosError",
|
|
18
|
+
context: { userId: "user123" },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const result = getParsedResponse(err);
|
|
22
|
+
|
|
23
|
+
expect(result.message).toBe("Error: Resource not found");
|
|
24
|
+
expect(result.data.statusCode).toBe("404");
|
|
25
|
+
expect(result.data.url).toBe("https://api.example.com/data");
|
|
26
|
+
expect(result.data.params).toEqual({ id: 123 });
|
|
27
|
+
expect(result.data.context).toEqual({ userId: "user123" });
|
|
28
|
+
expect(result.data.name).toBe("AxiosError");
|
|
29
|
+
expect(result.jsOnly).toBe(false);
|
|
30
|
+
expect(result.exception).toBe(result.message);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should parse error with response status code from response.status", () => {
|
|
34
|
+
const err = {
|
|
35
|
+
response: {
|
|
36
|
+
status: 500,
|
|
37
|
+
config: {
|
|
38
|
+
url: "https://api.example.com/data",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const result = getParsedResponse(err);
|
|
44
|
+
|
|
45
|
+
expect(result.message).toBe(`Error: ${RESPONSES[500]}`);
|
|
46
|
+
expect(result.data.statusCode).toBe("500");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should handle error with request but no response", () => {
|
|
50
|
+
const err = {
|
|
51
|
+
request: {},
|
|
52
|
+
message: "Network timeout",
|
|
53
|
+
config: {
|
|
54
|
+
url: "https://api.example.com/data",
|
|
55
|
+
params: { test: "value" },
|
|
56
|
+
},
|
|
57
|
+
name: "NetworkError",
|
|
58
|
+
context: { app: "test" },
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const result = getParsedResponse(err);
|
|
62
|
+
|
|
63
|
+
expect(result.message).toBe("Error: Network timeout");
|
|
64
|
+
expect(result.data.url).toBe("https://api.example.com/data");
|
|
65
|
+
expect(result.data.params).toEqual({ test: "value" });
|
|
66
|
+
expect(result.data.name).toBe("NetworkError");
|
|
67
|
+
expect(result.data.context).toEqual({ app: "test" });
|
|
68
|
+
expect(result.jsOnly).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should handle error with request but no response and no message", () => {
|
|
72
|
+
const err = {
|
|
73
|
+
request: {},
|
|
74
|
+
config: {
|
|
75
|
+
url: "https://api.example.com/data",
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const result = getParsedResponse(err);
|
|
80
|
+
|
|
81
|
+
expect(result.message).toBe(`Error: ${RESPONSES.noResponse}`);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("should handle error with neither response nor request", () => {
|
|
85
|
+
const err = {
|
|
86
|
+
message: "Failed to set up request",
|
|
87
|
+
config: {
|
|
88
|
+
url: "https://api.example.com/data",
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const result = getParsedResponse(err);
|
|
93
|
+
|
|
94
|
+
expect(result.message).toBe("Error: Failed to set up request");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should handle error with neither response nor request and no message", () => {
|
|
98
|
+
const err = {
|
|
99
|
+
config: {
|
|
100
|
+
url: "https://api.example.com/data",
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const result = getParsedResponse(err);
|
|
105
|
+
|
|
106
|
+
expect(result.message).toBe(`Error: ${RESPONSES.unknown}`);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should handle null error", () => {
|
|
110
|
+
const err = null;
|
|
111
|
+
|
|
112
|
+
const result = getParsedResponse(err);
|
|
113
|
+
|
|
114
|
+
expect(result.message).toBe(`Error: ${RESPONSES.unknown}`);
|
|
115
|
+
|
|
116
|
+
expect(result.data).toEqual({
|
|
117
|
+
context: undefined,
|
|
118
|
+
name: undefined,
|
|
119
|
+
params: undefined,
|
|
120
|
+
url: undefined,
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("should handle undefined error", () => {
|
|
125
|
+
const err = undefined;
|
|
126
|
+
|
|
127
|
+
const result = getParsedResponse(err);
|
|
128
|
+
|
|
129
|
+
expect(result.message).toBe(`Error: ${RESPONSES.unknown}`);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should extract params from err.config when response is not present", () => {
|
|
133
|
+
const err = {
|
|
134
|
+
request: {},
|
|
135
|
+
message: "Request failed",
|
|
136
|
+
config: {
|
|
137
|
+
url: "https://api.example.com/data",
|
|
138
|
+
params: { key: "value" },
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const result = getParsedResponse(err);
|
|
143
|
+
|
|
144
|
+
expect(result.data.params).toEqual({ key: "value" });
|
|
145
|
+
expect(result.data.url).toBe("https://api.example.com/data");
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("should prioritize response config over error config", () => {
|
|
149
|
+
const err = {
|
|
150
|
+
response: {
|
|
151
|
+
status: 404,
|
|
152
|
+
config: {
|
|
153
|
+
url: "https://api.example.com/response-url",
|
|
154
|
+
params: { from: "response" },
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
config: {
|
|
158
|
+
url: "https://api.example.com/error-url",
|
|
159
|
+
params: { from: "error" },
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const result = getParsedResponse(err);
|
|
164
|
+
|
|
165
|
+
expect(result.data.url).toBe("https://api.example.com/response-url");
|
|
166
|
+
expect(result.data.params).toEqual({ from: "response" });
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("should handle response with custom status codes", () => {
|
|
170
|
+
const err = {
|
|
171
|
+
response: {
|
|
172
|
+
data: {
|
|
173
|
+
statusCode: 418,
|
|
174
|
+
message: "I'm a teapot",
|
|
175
|
+
},
|
|
176
|
+
status: 418,
|
|
177
|
+
config: {
|
|
178
|
+
url: "https://api.example.com/coffee",
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const result = getParsedResponse(err);
|
|
184
|
+
|
|
185
|
+
expect(result.message).toBe("Error: I'm a teapot");
|
|
186
|
+
expect(result.data.statusCode).toBe("418");
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { invariant } from "../index";
|
|
2
|
+
|
|
3
|
+
// Mock __DEV__ global
|
|
4
|
+
declare global {
|
|
5
|
+
var __DEV__: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
describe("invariant", () => {
|
|
9
|
+
const originalDev = global.__DEV__;
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
global.__DEV__ = originalDev;
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe("in development mode", () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
global.__DEV__ = true;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should not throw when condition is true", () => {
|
|
21
|
+
expect(() => {
|
|
22
|
+
invariant(true, "This should not throw");
|
|
23
|
+
}).not.toThrow();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should throw when condition is false", () => {
|
|
27
|
+
expect(() => {
|
|
28
|
+
invariant(false, "This should throw");
|
|
29
|
+
}).toThrow("This should throw");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should throw with correct error message", () => {
|
|
33
|
+
const errorMessage = "Custom error message";
|
|
34
|
+
|
|
35
|
+
expect(() => {
|
|
36
|
+
invariant(false, errorMessage);
|
|
37
|
+
}).toThrow(errorMessage);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should not throw when condition is truthy value", () => {
|
|
41
|
+
expect(() => {
|
|
42
|
+
invariant(1 as any, "Should not throw");
|
|
43
|
+
}).not.toThrow();
|
|
44
|
+
|
|
45
|
+
expect(() => {
|
|
46
|
+
invariant("string" as any, "Should not throw");
|
|
47
|
+
}).not.toThrow();
|
|
48
|
+
|
|
49
|
+
expect(() => {
|
|
50
|
+
invariant({} as any, "Should not throw");
|
|
51
|
+
}).not.toThrow();
|
|
52
|
+
|
|
53
|
+
expect(() => {
|
|
54
|
+
invariant([] as any, "Should not throw");
|
|
55
|
+
}).not.toThrow();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should throw when condition is falsy value", () => {
|
|
59
|
+
expect(() => {
|
|
60
|
+
invariant(0 as any, "Should throw for 0");
|
|
61
|
+
}).toThrow("Should throw for 0");
|
|
62
|
+
|
|
63
|
+
expect(() => {
|
|
64
|
+
invariant("" as any, "Should throw for empty string");
|
|
65
|
+
}).toThrow("Should throw for empty string");
|
|
66
|
+
|
|
67
|
+
expect(() => {
|
|
68
|
+
invariant(null as any, "Should throw for null");
|
|
69
|
+
}).toThrow("Should throw for null");
|
|
70
|
+
|
|
71
|
+
expect(() => {
|
|
72
|
+
invariant(undefined as any, "Should throw for undefined");
|
|
73
|
+
}).toThrow("Should throw for undefined");
|
|
74
|
+
|
|
75
|
+
expect(() => {
|
|
76
|
+
invariant(NaN as any, "Should throw for NaN");
|
|
77
|
+
}).toThrow("Should throw for NaN");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("in production mode", () => {
|
|
82
|
+
beforeEach(() => {
|
|
83
|
+
global.__DEV__ = false;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should not throw when condition is true", () => {
|
|
87
|
+
expect(() => {
|
|
88
|
+
invariant(true, "This should not throw");
|
|
89
|
+
}).not.toThrow();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should not throw when condition is false", () => {
|
|
93
|
+
expect(() => {
|
|
94
|
+
invariant(false, "This should not throw in production");
|
|
95
|
+
}).not.toThrow();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should not throw for any condition", () => {
|
|
99
|
+
expect(() => {
|
|
100
|
+
invariant(0 as any, "Should not throw in production");
|
|
101
|
+
}).not.toThrow();
|
|
102
|
+
|
|
103
|
+
expect(() => {
|
|
104
|
+
invariant(null as any, "Should not throw in production");
|
|
105
|
+
}).not.toThrow();
|
|
106
|
+
|
|
107
|
+
expect(() => {
|
|
108
|
+
invariant(undefined as any, "Should not throw in production");
|
|
109
|
+
}).not.toThrow();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
2
2
|
import { getAppDataHeaders } from "../index";
|
|
3
3
|
|
|
4
|
+
const mockUILanguage = "en-US";
|
|
5
|
+
|
|
6
|
+
jest.mock(
|
|
7
|
+
"@applicaster/zapp-react-native-utils/appUtils/localizationsHelper",
|
|
8
|
+
() => ({
|
|
9
|
+
getUILanguage: jest.fn(() => mockUILanguage),
|
|
10
|
+
})
|
|
11
|
+
);
|
|
12
|
+
|
|
4
13
|
describe("getAppDataHeaders", () => {
|
|
5
14
|
const mockAppData = {
|
|
6
15
|
riversConfigurationId: "test-layout",
|
|
@@ -25,7 +34,7 @@ describe("getAppDataHeaders", () => {
|
|
|
25
34
|
const expectedHeaders = {
|
|
26
35
|
"x-applicaster-layout-id": mockAppData.riversConfigurationId,
|
|
27
36
|
"x-applicaster-screen-id": screenId,
|
|
28
|
-
"x-applicaster-language-code":
|
|
37
|
+
"x-applicaster-language-code": mockUILanguage,
|
|
29
38
|
"x-applicaster-version-number": mockAppData.version_name,
|
|
30
39
|
"x-applicaster-platform": mockAppData.platform,
|
|
31
40
|
"x-applicaster-country-code": mockAppData.countryCode,
|
|
@@ -33,6 +42,7 @@ describe("getAppDataHeaders", () => {
|
|
|
33
42
|
mockAppData.signedDeviceInfoToken,
|
|
34
43
|
"x-applicaster-device-make": mockAppData.deviceMake,
|
|
35
44
|
"x-applicaster-device-model": mockAppData.deviceModel,
|
|
45
|
+
"x-applicaster-uuid": undefined,
|
|
36
46
|
};
|
|
37
47
|
|
|
38
48
|
const headers = getAppDataHeaders(screenId);
|
package/headersUtils/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
2
2
|
import { getPlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
3
3
|
import { utilsLogger } from "../logger";
|
|
4
|
+
import { getUILanguage } from "../localizationUtils";
|
|
4
5
|
|
|
5
6
|
const logger = utilsLogger.addSubsystem("headersUtils");
|
|
6
7
|
|
|
@@ -21,7 +22,7 @@ export const getAppDataHeaders = (screenId: string) => {
|
|
|
21
22
|
"x-applicaster-layout-id":
|
|
22
23
|
appData.riversConfigurationId || appData.rivers_configuration_id,
|
|
23
24
|
"x-applicaster-screen-id": screenId,
|
|
24
|
-
"x-applicaster-language-code":
|
|
25
|
+
"x-applicaster-language-code": getUILanguage(),
|
|
25
26
|
"x-applicaster-version-number": appData.version_name || appData.versionName,
|
|
26
27
|
"x-applicaster-platform": (platform || appData.platform).toLowerCase(),
|
|
27
28
|
"x-applicaster-country-code": appData.countryCode,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const {
|
|
2
|
+
isKeyHasSuffix,
|
|
3
|
+
isKeyHasAnyOfSuffixes,
|
|
4
|
+
getKeyWithPrefixGenerator,
|
|
5
|
+
} = require("..");
|
|
6
|
+
|
|
7
|
+
describe("manifestUtils/_internals helpers", () => {
|
|
8
|
+
it("checks a key suffix", () => {
|
|
9
|
+
expect(
|
|
10
|
+
isKeyHasSuffix("button_enabled", "mobile_button_1_button_enabled")
|
|
11
|
+
).toBe(true);
|
|
12
|
+
|
|
13
|
+
expect(
|
|
14
|
+
isKeyHasSuffix("button_enabled", "mobile_button_1_assign_action")
|
|
15
|
+
).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("checks key against multiple suffixes", () => {
|
|
19
|
+
const suffixes = ["font_color", "focused_font_color", "text_transform"];
|
|
20
|
+
|
|
21
|
+
expect(
|
|
22
|
+
isKeyHasAnyOfSuffixes(suffixes, "mobile_button_1_text_transform")
|
|
23
|
+
).toBe(true);
|
|
24
|
+
|
|
25
|
+
expect(
|
|
26
|
+
isKeyHasAnyOfSuffixes(suffixes, "mobile_button_1_asset_alignment")
|
|
27
|
+
).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("generates stable key names using a prefix", () => {
|
|
31
|
+
const withMobileButtonPrefix = getKeyWithPrefixGenerator("mobile_button_2");
|
|
32
|
+
|
|
33
|
+
expect(withMobileButtonPrefix("display_mode")).toBe(
|
|
34
|
+
"mobile_button_2_display_mode"
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
expect(withMobileButtonPrefix("asset_enabled")).toBe(
|
|
38
|
+
"mobile_button_2_asset_enabled"
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -174,6 +174,36 @@ function generateFieldsFromDefaultsWithoutPrefixedLabel(
|
|
|
174
174
|
)(fields);
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Checks whether a generated manifest field key ends with the given suffix.
|
|
179
|
+
*
|
|
180
|
+
* @param {string} suffix
|
|
181
|
+
* @param {string} key
|
|
182
|
+
* @returns {boolean}
|
|
183
|
+
*/
|
|
184
|
+
const isKeyHasSuffix = (suffix, key) => key.endsWith(`_${suffix}`);
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Checks whether a generated manifest field key ends with any supported suffix.
|
|
188
|
+
*
|
|
189
|
+
* @param {string[]} suffixes
|
|
190
|
+
* @param {string} key
|
|
191
|
+
* @returns {boolean}
|
|
192
|
+
*/
|
|
193
|
+
const isKeyHasAnyOfSuffixes = (suffixes, key) =>
|
|
194
|
+
suffixes.some((suffix) => key.endsWith(`_${suffix}`));
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Creates a helper that prefixes a manifest field suffix with a shared key stem.
|
|
198
|
+
*
|
|
199
|
+
* @param {string} prefix
|
|
200
|
+
* @returns {(suffix: string) => string}
|
|
201
|
+
*/
|
|
202
|
+
function getKeyWithPrefixGenerator(prefix) {
|
|
203
|
+
// expect prefix as lower snake case, e.g. "mobile_buttons_container"
|
|
204
|
+
return (suffix) => `${prefix}_${suffix}`;
|
|
205
|
+
}
|
|
206
|
+
|
|
177
207
|
module.exports = {
|
|
178
208
|
toSnakeCase,
|
|
179
209
|
toCamelCase,
|
|
@@ -185,4 +215,7 @@ module.exports = {
|
|
|
185
215
|
getDefaultConfiguration,
|
|
186
216
|
compact,
|
|
187
217
|
replaceUnderscoreToSpace,
|
|
218
|
+
isKeyHasSuffix,
|
|
219
|
+
isKeyHasAnyOfSuffixes,
|
|
220
|
+
getKeyWithPrefixGenerator,
|
|
188
221
|
};
|
|
@@ -370,6 +370,20 @@ function getPlayerConfiguration({ platform, version }) {
|
|
|
370
370
|
};
|
|
371
371
|
|
|
372
372
|
if (isTV(platform)) {
|
|
373
|
+
localizations.fields.push({
|
|
374
|
+
key: "back_to_live_label",
|
|
375
|
+
label: "Back to live label",
|
|
376
|
+
initial_value: "Back To Live",
|
|
377
|
+
type: "text_input",
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
localizations.fields.push({
|
|
381
|
+
key: "start_over_label",
|
|
382
|
+
label: "Start over label",
|
|
383
|
+
initial_value: "Start Over",
|
|
384
|
+
type: "text_input",
|
|
385
|
+
});
|
|
386
|
+
|
|
373
387
|
styles.fields.push(
|
|
374
388
|
fieldsGroup("Always Show Scrub Bar & Timestamp", "", [
|
|
375
389
|
{
|
|
@@ -482,7 +496,7 @@ function getPlayerConfiguration({ platform, version }) {
|
|
|
482
496
|
),
|
|
483
497
|
fieldsGroup(
|
|
484
498
|
"Skip Button",
|
|
485
|
-
"This section allows you to configure the skip button
|
|
499
|
+
"This section allows you to configure the skip button behaviour",
|
|
486
500
|
[
|
|
487
501
|
{
|
|
488
502
|
type: "switch",
|
|
@@ -499,6 +513,12 @@ function getPlayerConfiguration({ platform, version }) {
|
|
|
499
513
|
label: "Persistent Button Toggle",
|
|
500
514
|
initial_value: true,
|
|
501
515
|
},
|
|
516
|
+
]
|
|
517
|
+
),
|
|
518
|
+
fieldsGroup(
|
|
519
|
+
"Labeled Button Style",
|
|
520
|
+
"This section allows you to configure the labeled button styles",
|
|
521
|
+
[
|
|
502
522
|
{
|
|
503
523
|
type: "color_picker_rgba",
|
|
504
524
|
label_tooltip: "",
|
|
@@ -654,6 +674,44 @@ function getPlayerConfiguration({ platform, version }) {
|
|
|
654
674
|
);
|
|
655
675
|
}
|
|
656
676
|
|
|
677
|
+
if (isTV(platform)) {
|
|
678
|
+
general.fields.push(
|
|
679
|
+
{
|
|
680
|
+
key: "liveSeekingEnabled",
|
|
681
|
+
label: "Live Seeking Enabled",
|
|
682
|
+
initial_value: false,
|
|
683
|
+
type: "switch",
|
|
684
|
+
label_tooltip: "Enable Live Seek",
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
key: "minimumAllowedSeekableDurationInSeconds",
|
|
688
|
+
label: "Minimum allowed seekable duration in seconds",
|
|
689
|
+
initial_value: 300,
|
|
690
|
+
type: "number_input",
|
|
691
|
+
label_tooltip:
|
|
692
|
+
"If duration less than this value, player will disable 'liveSeekingEnabled' value",
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
key: "live_image",
|
|
696
|
+
label: "Live badge",
|
|
697
|
+
type: "uploader",
|
|
698
|
+
label_tooltip: "Override default live badge / icon",
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
key: "live_width",
|
|
702
|
+
label: "Live badge width",
|
|
703
|
+
type: "number_input",
|
|
704
|
+
initial_value: 85,
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
key: "live_height",
|
|
708
|
+
label: "Live badge height",
|
|
709
|
+
type: "number_input",
|
|
710
|
+
initial_value: 50,
|
|
711
|
+
}
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
|
|
657
715
|
if (isMobile(platform)) {
|
|
658
716
|
general.fields.push(
|
|
659
717
|
{
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const {
|
|
2
|
+
withConditional,
|
|
3
|
+
getConditionalKey,
|
|
4
|
+
createConditionalField,
|
|
5
|
+
} = require("..");
|
|
6
|
+
|
|
7
|
+
describe("manifestUtils/fieldUtils", () => {
|
|
8
|
+
it("appends conditions and adds all_conditions when there is more than one condition", () => {
|
|
9
|
+
const config = {
|
|
10
|
+
key: "mobile_button_1_width",
|
|
11
|
+
conditional_fields: [
|
|
12
|
+
{ key: "styles/mobile_button_1_button_enabled", condition_value: true },
|
|
13
|
+
],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const result = withConditional([
|
|
17
|
+
{ key: "styles/mobile_button_1_display_mode", condition_value: "fixed" },
|
|
18
|
+
])(config);
|
|
19
|
+
|
|
20
|
+
expect(result.conditional_fields).toEqual([
|
|
21
|
+
{ key: "styles/mobile_button_1_button_enabled", condition_value: true },
|
|
22
|
+
{ key: "styles/mobile_button_1_display_mode", condition_value: "fixed" },
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
expect(result.rules).toBe("all_conditions");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("creates category-prefixed conditional key", () => {
|
|
29
|
+
expect(
|
|
30
|
+
getConditionalKey("mobile_buttons_container_position", "styles")
|
|
31
|
+
).toBe("styles/mobile_buttons_container_position");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("creates conditional fields with default category and with raw key", () => {
|
|
35
|
+
expect(
|
|
36
|
+
createConditionalField("mobile_button_1_asset_enabled", true)
|
|
37
|
+
).toEqual({
|
|
38
|
+
key: "styles/mobile_button_1_asset_enabled",
|
|
39
|
+
condition_value: true,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(
|
|
43
|
+
createConditionalField("mobile_button_1_asset_enabled", true, null)
|
|
44
|
+
).toEqual({
|
|
45
|
+
key: "mobile_button_1_asset_enabled",
|
|
46
|
+
condition_value: true,
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Appends new conditional_fields from conditions array
|
|
3
|
+
* to config.conditional_fields
|
|
4
|
+
* if there are more than 1 condition, sets rules to "all_conditions"
|
|
5
|
+
*
|
|
6
|
+
* @param {Array<{key: string, condition_value: unknown}>} conditions
|
|
7
|
+
* @returns {(config: object) => object}
|
|
8
|
+
*/
|
|
9
|
+
const withConditional = (conditions) => (config) => {
|
|
10
|
+
const conditional_fields = [
|
|
11
|
+
...(config.conditional_fields || []),
|
|
12
|
+
...conditions,
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const next = { ...config, conditional_fields };
|
|
16
|
+
|
|
17
|
+
if (conditional_fields.length > 1) {
|
|
18
|
+
return { ...next, rules: "all_conditions" };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return next;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Builds key for conditional fields prepending category, e.g. "styles/mobile_buttons_container_position"
|
|
26
|
+
*
|
|
27
|
+
* @param {string} key
|
|
28
|
+
* @param {string} category
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
function getConditionalKey(key, category) {
|
|
32
|
+
return `${category}/${key}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Returns a conditional field object for manifest visibility rules.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} key
|
|
39
|
+
* @param {unknown} condition_value
|
|
40
|
+
* @param {string|null} [category="styles"] Pass `null` to use the key as-is.
|
|
41
|
+
* @returns {{key: string, condition_value: unknown}}
|
|
42
|
+
*/
|
|
43
|
+
function createConditionalField(key, condition_value, category = "styles") {
|
|
44
|
+
return {
|
|
45
|
+
key: category ? getConditionalKey(key, category) : key,
|
|
46
|
+
condition_value,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
withConditional,
|
|
52
|
+
getConditionalKey,
|
|
53
|
+
createConditionalField,
|
|
54
|
+
};
|
package/manifestUtils/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
|
|
9
9
|
const { tvActionButtonsContainer } = require("./tvAction/container");
|
|
10
10
|
const { tvActionButton } = require("./tvAction/button");
|
|
11
|
+
const { buildMobileActionButtonGroups } = require("./mobileAction/groups");
|
|
11
12
|
const { compact } = require("./_internals");
|
|
12
13
|
|
|
13
14
|
const { spacingKey, absolutePositionElement } = require("./containers");
|
|
@@ -43,6 +44,7 @@ module.exports = {
|
|
|
43
44
|
tvMenuLabel,
|
|
44
45
|
tvActionButtonsContainer,
|
|
45
46
|
tvActionButton,
|
|
47
|
+
buildMobileActionButtonGroups,
|
|
46
48
|
mobileCellLabel,
|
|
47
49
|
tvCellLabel,
|
|
48
50
|
tvBadges,
|