@openedx/frontend-base 1.0.0-alpha.0 → 1.0.0-alpha.10
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/config/eslint/base.eslint.config.js +1 -1
- package/config/jest/jest.config.js +1 -0
- package/config/types.js +0 -2
- package/config/webpack/common-config/all/getStylesheetRule.js +1 -1
- package/config/webpack/plugins/html-webpack-new-relic-plugin/test/HtmlWebpackNewRelicPlugin.test.js +66 -0
- package/config/webpack/webpack.config.build.js +1 -11
- package/config/webpack/webpack.config.dev.js +5 -11
- package/config/webpack/webpack.config.dev.shell.js +5 -11
- package/package.json +11 -3
- package/runtime/__mocks__/file.js +1 -0
- package/runtime/__mocks__/svg.js +1 -0
- package/runtime/__mocks__/universal-cookie.js +6 -0
- package/runtime/analytics/interface.test.js +242 -0
- package/runtime/auth/AxiosJwtAuthService.test.jsx +1076 -0
- package/runtime/auth/interceptors/createRetryInterceptor.test.js +23 -0
- package/runtime/config/getExternalLinkUrl.test.js +76 -0
- package/runtime/config/index.ts +2 -3
- package/runtime/i18n/lib.test.js +230 -0
- package/runtime/index.ts +5 -0
- package/runtime/initialize.async.function.config.test.js +43 -0
- package/runtime/initialize.const.config.test.js +41 -0
- package/runtime/initialize.function.config.test.js +41 -0
- package/runtime/initialize.test.js +356 -0
- package/runtime/jest.config.js +1 -0
- package/runtime/logging/NewRelicLoggingService.test.js +214 -0
- package/runtime/react/AuthenticatedPageRoute.test.jsx +135 -0
- package/runtime/react/ErrorBoundary.test.jsx +83 -0
- package/runtime/react/SiteProvider.test.jsx +66 -0
- package/runtime/react/SiteProvider.tsx +26 -3
- package/runtime/react/constants.ts +3 -0
- package/runtime/react/hooks/index.ts +8 -0
- package/runtime/react/hooks/theme/index.ts +2 -0
- package/runtime/react/hooks/theme/useTheme.test.ts +221 -0
- package/runtime/react/hooks/theme/useTheme.ts +179 -0
- package/runtime/react/hooks/theme/useThemeConfig.test.ts +107 -0
- package/runtime/react/hooks/theme/useThemeConfig.ts +34 -0
- package/runtime/react/hooks/theme/useThemeCore.test.ts +65 -0
- package/runtime/react/hooks/theme/useThemeCore.ts +52 -0
- package/runtime/react/hooks/theme/useThemeVariants.test.ts +97 -0
- package/runtime/react/hooks/theme/useThemeVariants.ts +116 -0
- package/runtime/react/hooks/theme/useTrackColorSchemeChoice.test.ts +54 -0
- package/runtime/react/hooks/theme/useTrackColorSchemeChoice.ts +30 -0
- package/runtime/react/hooks/theme/utils.ts +11 -0
- package/runtime/react/hooks/useActiveRoles.ts +15 -0
- package/runtime/react/hooks/useActiveRouteRoleWatcher.ts +31 -0
- package/runtime/react/hooks/useAppConfig.ts +9 -0
- package/runtime/react/hooks/useAuthenticatedUser.test.tsx +41 -0
- package/runtime/react/hooks/useAuthenticatedUser.ts +9 -0
- package/runtime/react/hooks/useSiteConfig.test.tsx +13 -0
- package/runtime/react/hooks/useSiteConfig.ts +9 -0
- package/runtime/react/hooks/useSiteEvent.ts +24 -0
- package/runtime/react/reducers.ts +40 -0
- package/runtime/routing/utils.test.ts +7 -0
- package/runtime/scripts/GoogleAnalyticsLoader.test.ts +77 -0
- package/runtime/setupTest.js +0 -35
- package/runtime/site.config.test.tsx +33 -0
- package/runtime/slots/Slot.test.tsx +40 -0
- package/runtime/slots/layout/DefaultSlotLayout.test.tsx +31 -0
- package/runtime/slots/layout/hooks.test.tsx +178 -0
- package/runtime/slots/layout/utils.test.ts +67 -0
- package/runtime/slots/types.ts +1 -0
- package/runtime/slots/utils.test.ts +64 -0
- package/runtime/slots/utils.ts +28 -9
- package/runtime/slots/widget/iframe/hooks.ts +1 -1
- package/runtime/testing/initializeMockApp.test.ts +66 -0
- package/runtime/testing/initializeMockApp.ts +5 -0
- package/runtime/utils.test.js +116 -0
- package/shell/Logo.test.tsx +32 -0
- package/shell/__mocks__/file.js +1 -0
- package/shell/__mocks__/svg.js +1 -0
- package/shell/__mocks__/universal-cookie.js +6 -0
- package/shell/app.scss +2 -1
- package/shell/app.ts +14 -0
- package/shell/dev/devHome/app.ts +2 -2
- package/shell/dev/slotShowcase/app.tsx +9 -9
- package/shell/header/app.tsx +3 -3
- package/shell/jest.config.js +1 -0
- package/shell/router/createRouter.test.tsx +50 -0
- package/shell/router/getAppRoutes.test.tsx +59 -0
- package/shell/setupTest.js +0 -35
- package/shell/site.config.dev.tsx +3 -3
- package/shell/site.config.test.tsx +16 -0
- package/shell/site.tsx +1 -1
- package/tools/dist/cli/intl-imports.test.js +146 -0
- package/tools/dist/cli/openedx.js +1 -15
- package/tools/dist/cli/utils/printUsage.js +0 -9
- package/tools/dist/eslint/base.eslint.config.js +1 -1
- package/tools/dist/jest/jest.config.js +1 -0
- package/tools/dist/types.js +0 -2
- package/tools/dist/webpack/common-config/all/getStylesheetRule.js +1 -1
- package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/test/HtmlWebpackNewRelicPlugin.test.js +66 -0
- package/tools/dist/webpack/webpack.config.build.js +1 -11
- package/tools/dist/webpack/webpack.config.dev.js +5 -11
- package/tools/dist/webpack/webpack.config.dev.shell.js +5 -11
- package/types.ts +21 -1
- package/config/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.js +0 -108
- package/config/webpack/plugins/paragon-webpack-plugin/index.js +0 -7
- package/config/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.js +0 -64
- package/config/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.js +0 -53
- package/config/webpack/plugins/paragon-webpack-plugin/utils/index.js +0 -9
- package/config/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.js +0 -114
- package/config/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.js +0 -146
- package/config/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.js +0 -126
- package/config/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.js +0 -57
- package/config/webpack/types.js +0 -2
- package/config/webpack/utils/paragonUtils.js +0 -138
- package/eslint.config.js +0 -18
- package/frontend-base.d.ts +0 -8
- package/jest.config.js +0 -7
- package/openedx-frontend-base.tgz +0 -0
- package/runtime/react/hooks.ts +0 -106
- package/test-site/app.d.ts +0 -15
- package/test-site/dist/176.436443549ebb858db483.js +0 -2
- package/test-site/dist/176.436443549ebb858db483.js.map +0 -1
- package/test-site/dist/362.536eff787d2380fe246c.js +0 -2
- package/test-site/dist/362.536eff787d2380fe246c.js.map +0 -1
- package/test-site/dist/653.486966b108d224551296.js +0 -2
- package/test-site/dist/653.486966b108d224551296.js.map +0 -1
- package/test-site/dist/74e025d3fe9a7b7f8503054e2563b353.jpg +0 -0
- package/test-site/dist/806.323cf6496ad0a7fe73a7.js +0 -3
- package/test-site/dist/806.323cf6496ad0a7fe73a7.js.LICENSE.txt +0 -106
- package/test-site/dist/806.323cf6496ad0a7fe73a7.js.map +0 -1
- package/test-site/dist/95ec738c0b7faac5b5c9126794446bbd.svg +0 -4
- package/test-site/dist/app.612058b36c74787759ac.css +0 -61
- package/test-site/dist/app.612058b36c74787759ac.css.map +0 -1
- package/test-site/dist/app.612058b36c74787759ac.js +0 -2
- package/test-site/dist/app.612058b36c74787759ac.js.map +0 -1
- package/test-site/dist/cb28cdb1468c915e27e5cec9af64f22f.svg +0 -1
- package/test-site/dist/index.html +0 -1
- package/test-site/dist/report.html +0 -39
- package/test-site/dist/runtime.c7aeaf7b967496cb076f.js +0 -2
- package/test-site/dist/runtime.c7aeaf7b967496cb076f.js.map +0 -1
- package/test-site/eslint.config.js +0 -12
- package/test-site/package-lock.json +0 -19226
- package/test-site/package.json +0 -29
- package/test-site/public/index.html +0 -10
- package/test-site/site.config.build.tsx +0 -27
- package/test-site/site.config.dev.tsx +0 -27
- package/test-site/src/authenticated-page/AuthenticatedPage.tsx +0 -18
- package/test-site/src/authenticated-page/i18n/index.ts +0 -27
- package/test-site/src/authenticated-page/index.tsx +0 -28
- package/test-site/src/example-page/ExamplePage.tsx +0 -79
- package/test-site/src/example-page/Image.tsx +0 -11
- package/test-site/src/example-page/ParagonPreview.jsx +0 -66
- package/test-site/src/example-page/apple.jpg +0 -0
- package/test-site/src/example-page/apple.svg +0 -1
- package/test-site/src/example-page/index.ts +0 -16
- package/test-site/src/i18n/README.md +0 -3
- package/test-site/src/i18n/messages/frontend-app-sample/ar.json +0 -4
- package/test-site/src/i18n/messages/frontend-app-sample/eo.json +0 -1
- package/test-site/src/i18n/messages/frontend-app-sample/es_419.json +0 -4
- package/test-site/src/i18n/messages/frontend-component-emptylangs/ar.json +0 -1
- package/test-site/src/i18n/messages/frontend-component-singlelang/ar.json +0 -3
- package/test-site/src/iframe-widget/IframeWidget.tsx +0 -14
- package/test-site/src/iframe-widget/index.ts +0 -16
- package/test-site/src/index.tsx +0 -3
- package/test-site/src/messages.js +0 -11
- package/test-site/src/site.scss +0 -11
- package/test-site/tsconfig.json +0 -14
- package/tools/babel/babel.config.js +0 -27
- package/tools/babel.config.js +0 -3
- package/tools/cli/README.md +0 -29
- package/tools/cli/commands/pack.ts +0 -9
- package/tools/cli/commands/release.ts +0 -27
- package/tools/cli/commands/serve.ts +0 -43
- package/tools/cli/intl-imports.ts +0 -274
- package/tools/cli/openedx.ts +0 -101
- package/tools/cli/transifex-utils.ts +0 -75
- package/tools/cli/utils/ensureConfigFilenameOption.ts +0 -40
- package/tools/cli/utils/formatter.ts +0 -10
- package/tools/cli/utils/getResolvedConfigPath.ts +0 -23
- package/tools/cli/utils/prettyPrintTitle.ts +0 -15
- package/tools/cli/utils/printUsage.ts +0 -53
- package/tools/config-helpers/createConfig.ts +0 -8
- package/tools/config-helpers/createLintConfig.ts +0 -14
- package/tools/config-helpers/getBaseConfig.ts +0 -11
- package/tools/defaultConfigPaths.ts +0 -30
- package/tools/dist/cli/commands/pack.js +0 -14
- package/tools/dist/cli/commands/release.js +0 -28
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.js +0 -108
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/index.js +0 -7
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.js +0 -64
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.js +0 -53
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/index.js +0 -9
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.js +0 -114
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.js +0 -146
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.js +0 -126
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.js +0 -57
- package/tools/dist/webpack/types.js +0 -2
- package/tools/dist/webpack/utils/paragonUtils.js +0 -138
- package/tools/eslint/base.eslint.config.js +0 -124
- package/tools/eslint/modules.d.ts +0 -5
- package/tools/eslint.config.js +0 -15
- package/tools/index.ts +0 -3
- package/tools/jest/jest.config.js +0 -30
- package/tools/jest.config.js +0 -19
- package/tools/tsconfig.json +0 -24
- package/tools/types.ts +0 -21
- package/tools/typescript/tsconfig.json +0 -32
- package/tools/webpack/common-config/README.md +0 -15
- package/tools/webpack/common-config/all/getCodeRules.ts +0 -51
- package/tools/webpack/common-config/all/getFileLoaderRules.ts +0 -23
- package/tools/webpack/common-config/all/getIgnoreWarnings.ts +0 -13
- package/tools/webpack/common-config/all/getImageMinimizer.ts +0 -26
- package/tools/webpack/common-config/all/getStylesheetRule.ts +0 -111
- package/tools/webpack/common-config/dev/getDevServer.ts +0 -35
- package/tools/webpack/common-config/index.ts +0 -6
- package/tools/webpack/common-config/site/getHtmlWebpackPlugin.ts +0 -11
- package/tools/webpack/modules.d.ts +0 -6
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.ts +0 -102
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/LICENSE +0 -21
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/README.md +0 -7
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/index.js +0 -3
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +0 -1
- package/tools/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.ts +0 -134
- package/tools/webpack/plugins/paragon-webpack-plugin/index.ts +0 -3
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.ts +0 -71
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.ts +0 -72
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/index.ts +0 -6
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.ts +0 -131
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.ts +0 -144
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.ts +0 -106
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.ts +0 -54
- package/tools/webpack/types.ts +0 -69
- package/tools/webpack/utils/getLocalAliases.ts +0 -65
- package/tools/webpack/utils/getPublicPath.ts +0 -3
- package/tools/webpack/utils/getResolvedSiteConfigPath.ts +0 -28
- package/tools/webpack/utils/paragonUtils.ts +0 -152
- package/tools/webpack/webpack.config.build.ts +0 -93
- package/tools/webpack/webpack.config.dev.shell.ts +0 -122
- package/tools/webpack/webpack.config.dev.ts +0 -90
- package/tsconfig.json +0 -23
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react';
|
|
2
|
+
import { SlotOperation } from '../types';
|
|
3
|
+
import * as slotHooks from '../hooks';
|
|
4
|
+
import * as slotUtils from '../utils';
|
|
5
|
+
import { WidgetAppendOperation, WidgetOperationTypes } from '../widget';
|
|
6
|
+
import { useLayoutForSlotId, useLayoutOptions, useLayoutOptionsForId } from './hooks';
|
|
7
|
+
import { LayoutOperationTypes, LayoutReplaceOperation } from './types';
|
|
8
|
+
|
|
9
|
+
jest.mock('../hooks'); // mocks the useSlotOperations hook
|
|
10
|
+
|
|
11
|
+
function MockLayout() {
|
|
12
|
+
return (
|
|
13
|
+
<div>Mock Component</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('useLayoutForSlotId', () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
jest.restoreAllMocks();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should return null when no replace layout operations are provided', () => {
|
|
23
|
+
(slotHooks.useSlotOperations as jest.Mock).mockReturnValue([]);
|
|
24
|
+
const { result } = renderHook(() => useLayoutForSlotId('test-slot.ui'));
|
|
25
|
+
expect(result.current).toBeNull();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should return a layout component when replace layout operation is present', () => {
|
|
29
|
+
const operation: LayoutReplaceOperation = {
|
|
30
|
+
slotId: 'test-slot.ui',
|
|
31
|
+
op: LayoutOperationTypes.REPLACE,
|
|
32
|
+
component: MockLayout
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
(slotHooks.useSlotOperations as jest.Mock).mockReturnValue([operation]);
|
|
36
|
+
const { result } = renderHook(() => useLayoutForSlotId('test-slot.ui'));
|
|
37
|
+
expect(result.current).toEqual(MockLayout);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should return null if the operation condition is not satisfied', () => {
|
|
41
|
+
const operation: LayoutReplaceOperation = {
|
|
42
|
+
slotId: 'test-slot.ui',
|
|
43
|
+
op: LayoutOperationTypes.REPLACE,
|
|
44
|
+
component: MockLayout
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
jest.spyOn(slotUtils, 'isSlotOperationConditionSatisfied').mockReturnValue(false);
|
|
48
|
+
(slotHooks.useSlotOperations as jest.Mock).mockReturnValue([operation]);
|
|
49
|
+
const { result } = renderHook(() => useLayoutForSlotId('test-slot.ui'));
|
|
50
|
+
expect(result.current).toBeNull();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should return a layout element when replace layout operation is present', () => {
|
|
54
|
+
const operation: LayoutReplaceOperation = {
|
|
55
|
+
slotId: 'test-slot.ui',
|
|
56
|
+
op: LayoutOperationTypes.REPLACE,
|
|
57
|
+
element: <div>Mock layout</div>
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// @ts-expect-error This is an intentionally malformed operation to test what happens when component/element are not present.
|
|
61
|
+
const malformedOperation: SlotOperation = {
|
|
62
|
+
slotId: 'test-slot.ui',
|
|
63
|
+
op: LayoutOperationTypes.REPLACE,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const unrelatedOperation: WidgetAppendOperation = {
|
|
67
|
+
slotId: 'test-slot.ui',
|
|
68
|
+
id: 'test-slot.widget1',
|
|
69
|
+
op: WidgetOperationTypes.APPEND,
|
|
70
|
+
element: <div>Widget</div>
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
(slotHooks.useSlotOperations as jest.Mock).mockReturnValue([operation, malformedOperation, unrelatedOperation]);
|
|
74
|
+
const { result } = renderHook(() => useLayoutForSlotId('test-slot.ui'));
|
|
75
|
+
expect(result.current).toEqual(<div>Mock layout</div>);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should return the last layout component in the operations list', () => {
|
|
79
|
+
const SecondMockLayout = () => <div>First Component</div>;
|
|
80
|
+
|
|
81
|
+
const operation1: LayoutReplaceOperation = {
|
|
82
|
+
slotId: 'test-slot.ui',
|
|
83
|
+
op: LayoutOperationTypes.REPLACE,
|
|
84
|
+
component: MockLayout
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const operation2: LayoutReplaceOperation = {
|
|
88
|
+
slotId: 'test-slot.ui',
|
|
89
|
+
op: LayoutOperationTypes.REPLACE,
|
|
90
|
+
component: SecondMockLayout
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
(slotHooks.useSlotOperations as jest.Mock).mockReturnValue([operation1, operation2]);
|
|
94
|
+
const { result } = renderHook(() => useLayoutForSlotId('test-slot.ui'));
|
|
95
|
+
expect(result.current).toEqual(SecondMockLayout);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('useLayoutOptionsForId', () => {
|
|
100
|
+
beforeEach(() => {
|
|
101
|
+
jest.restoreAllMocks();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should return an empty object whent here are no layout options operations', () => {
|
|
105
|
+
(slotHooks.useSlotOperations as jest.Mock).mockReturnValue([]);
|
|
106
|
+
const { result } = renderHook(() => useLayoutOptionsForId('test-slot.ui'));
|
|
107
|
+
expect(result.current).toEqual({});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should return an object containing options from a single layout options operation', () => {
|
|
111
|
+
const mockOptions = { option1: 'value1' };
|
|
112
|
+
const operation = {
|
|
113
|
+
slotId: 'test-slot.ui',
|
|
114
|
+
op: LayoutOperationTypes.OPTIONS,
|
|
115
|
+
options: mockOptions
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const otherOperation: LayoutReplaceOperation = {
|
|
119
|
+
slotId: 'test-slot.ui',
|
|
120
|
+
op: LayoutOperationTypes.REPLACE,
|
|
121
|
+
element: <div>Layout</div>
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
(slotHooks.useSlotOperations as jest.Mock).mockReturnValue([operation, otherOperation]);
|
|
125
|
+
const { result } = renderHook(() => useLayoutOptionsForId('test-slot.ui'));
|
|
126
|
+
expect(result.current).toEqual(mockOptions);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('when various failure conditions are true', () => {
|
|
130
|
+
const mockOptions = { option1: 'value1' };
|
|
131
|
+
const operation = {
|
|
132
|
+
slotId: 'test-slot.ui',
|
|
133
|
+
op: LayoutOperationTypes.OPTIONS,
|
|
134
|
+
options: mockOptions
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
it('should return an empty object if the operation condition is not satisfied.', () => {
|
|
138
|
+
jest.spyOn(slotUtils, 'isSlotOperationConditionSatisfied').mockReturnValue(false);
|
|
139
|
+
(slotHooks.useSlotOperations as jest.Mock).mockReturnValue([operation]);
|
|
140
|
+
const { result } = renderHook(() => useLayoutOptionsForId('test-slot.ui'));
|
|
141
|
+
expect(result.current).toEqual({});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should return merged layout options from multiple operations', () => {
|
|
146
|
+
const mockOptions1 = { option1: 'value1', optionOverride: 'foo', };
|
|
147
|
+
const mockOptions2 = { option2: 'value2', optionOverride: 'bar' };
|
|
148
|
+
const operation1 = {
|
|
149
|
+
slotId: 'test-slot.ui',
|
|
150
|
+
op: LayoutOperationTypes.OPTIONS,
|
|
151
|
+
options: mockOptions1
|
|
152
|
+
};
|
|
153
|
+
const operation2 = {
|
|
154
|
+
slotId: 'test-slot.ui',
|
|
155
|
+
op: LayoutOperationTypes.OPTIONS,
|
|
156
|
+
options: mockOptions2
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
(slotHooks.useSlotOperations as jest.Mock).mockReturnValue([operation1, operation2]);
|
|
160
|
+
const { result } = renderHook(() => useLayoutOptionsForId('test-slot.ui'));
|
|
161
|
+
expect(result.current).toEqual({ option1: 'value1', option2: 'value2', optionOverride: 'bar' });
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('useLayoutOptions', () => {
|
|
166
|
+
it('should call useLayoutOptionsForId with the correct id ', () => {
|
|
167
|
+
const mockOptions1 = { option1: 'value1' };
|
|
168
|
+
const operation = {
|
|
169
|
+
slotId: 'test-slot.ui',
|
|
170
|
+
op: LayoutOperationTypes.OPTIONS,
|
|
171
|
+
options: mockOptions1
|
|
172
|
+
};
|
|
173
|
+
(slotHooks.useSlotOperations as jest.Mock).mockReturnValue([operation]);
|
|
174
|
+
jest.spyOn(slotHooks, 'useSlotContext').mockReturnValue({ id: 'test-slot.ui' });
|
|
175
|
+
const { result } = renderHook(() => useLayoutOptions());
|
|
176
|
+
expect(result.current).toEqual({ option1: 'value1' });
|
|
177
|
+
});
|
|
178
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { SlotOperation } from '../types';
|
|
2
|
+
import { WidgetOperationTypes } from '../widget';
|
|
3
|
+
import { LayoutOperationTypes } from './types';
|
|
4
|
+
import { isLayoutOperation, isLayoutOptionsOperation, isLayoutReplaceOperation } from './utils';
|
|
5
|
+
|
|
6
|
+
describe('UI Layout Operation utilities', () => {
|
|
7
|
+
describe('isLayoutOperation', () => {
|
|
8
|
+
it('should return true for valid LayoutOperation', () => {
|
|
9
|
+
const operation: SlotOperation = {
|
|
10
|
+
slotId: 'mock.slot.ui',
|
|
11
|
+
op: LayoutOperationTypes.OPTIONS,
|
|
12
|
+
options: {},
|
|
13
|
+
};
|
|
14
|
+
expect(isLayoutOperation(operation)).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should return false for invalid LayoutOperation', () => {
|
|
18
|
+
const operation: SlotOperation = {
|
|
19
|
+
slotId: 'mock.slot.ui',
|
|
20
|
+
op: WidgetOperationTypes.APPEND,
|
|
21
|
+
id: 'mock.slot.widget',
|
|
22
|
+
element: '',
|
|
23
|
+
};
|
|
24
|
+
expect(isLayoutOperation(operation)).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('isLayoutOptionsOperation', () => {
|
|
29
|
+
it('should return true for valid LayoutOptionsOperation', () => {
|
|
30
|
+
const operation: SlotOperation = {
|
|
31
|
+
slotId: 'mock.slot.ui',
|
|
32
|
+
op: LayoutOperationTypes.OPTIONS,
|
|
33
|
+
options: {},
|
|
34
|
+
};
|
|
35
|
+
expect(isLayoutOptionsOperation(operation)).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should return false for invalid LayoutOptionsOperation', () => {
|
|
39
|
+
const operation: SlotOperation = {
|
|
40
|
+
slotId: 'mock.slot.ui',
|
|
41
|
+
op: LayoutOperationTypes.REPLACE,
|
|
42
|
+
element: '',
|
|
43
|
+
};
|
|
44
|
+
expect(isLayoutOptionsOperation(operation)).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('isLayoutReplaceOperation', () => {
|
|
49
|
+
it('should return true for valid LayoutReplaceOperation', () => {
|
|
50
|
+
const operation: SlotOperation = {
|
|
51
|
+
slotId: 'mock.slot.ui',
|
|
52
|
+
op: LayoutOperationTypes.REPLACE,
|
|
53
|
+
element: '',
|
|
54
|
+
};
|
|
55
|
+
expect(isLayoutReplaceOperation(operation)).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should return false for invalid LayoutReplaceOperation', () => {
|
|
59
|
+
const operation: SlotOperation = {
|
|
60
|
+
slotId: 'mock.slot.ui',
|
|
61
|
+
op: LayoutOperationTypes.OPTIONS,
|
|
62
|
+
options: {},
|
|
63
|
+
};
|
|
64
|
+
expect(isLayoutReplaceOperation(operation)).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
package/runtime/slots/types.ts
CHANGED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { getSiteConfig } from '../config';
|
|
2
|
+
import { SlotOperation } from './types';
|
|
3
|
+
import { getSlotOperations } from './utils';
|
|
4
|
+
import { WidgetOperationTypes } from '.';
|
|
5
|
+
|
|
6
|
+
jest.mock('../config');
|
|
7
|
+
|
|
8
|
+
describe('getSlotOperations', () => {
|
|
9
|
+
it('should return an empty array if no apps are configured', () => {
|
|
10
|
+
(getSiteConfig as jest.Mock).mockReturnValue({ apps: [] });
|
|
11
|
+
const result = getSlotOperations('test-slot.ui');
|
|
12
|
+
expect(result).toEqual([]);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should return an empty array if no slots are present in apps', () => {
|
|
16
|
+
(getSiteConfig as jest.Mock).mockReturnValue({ apps: [{ slots: [] }] });
|
|
17
|
+
const result = getSlotOperations('test-slot.ui');
|
|
18
|
+
expect(result).toEqual([]);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should return an empty array if no matching slotId is found', () => {
|
|
22
|
+
const mockSlots: SlotOperation[] = [{ slotId: 'other-slot.ui', op: WidgetOperationTypes.APPEND, id: 'widget1', element: '' }];
|
|
23
|
+
(getSiteConfig as jest.Mock).mockReturnValue({ apps: [{ slots: mockSlots }] });
|
|
24
|
+
const result = getSlotOperations('test-slot.ui');
|
|
25
|
+
expect(result).toEqual([]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should return the correct slot operations for a given slotId', () => {
|
|
29
|
+
const mockSlots: SlotOperation[] = [
|
|
30
|
+
{ slotId: 'test-slot.ui', op: WidgetOperationTypes.APPEND, id: 'widget1', element: '' },
|
|
31
|
+
{ slotId: 'test-slot.ui', op: WidgetOperationTypes.APPEND, id: 'widget2', element: '' },
|
|
32
|
+
{ slotId: 'other-slot.ui', op: WidgetOperationTypes.APPEND, id: 'widget3', element: '' },
|
|
33
|
+
];
|
|
34
|
+
(getSiteConfig as jest.Mock).mockReturnValue({ apps: [{ slots: mockSlots }] });
|
|
35
|
+
const result = getSlotOperations('test-slot.ui');
|
|
36
|
+
expect(result).toEqual([
|
|
37
|
+
{ slotId: 'test-slot.ui', op: WidgetOperationTypes.APPEND, id: 'widget1', element: '' },
|
|
38
|
+
{ slotId: 'test-slot.ui', op: WidgetOperationTypes.APPEND, id: 'widget2', element: '' },
|
|
39
|
+
]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should handle multiple apps with slots correctly', () => {
|
|
43
|
+
(getSiteConfig as jest.Mock).mockReturnValue({
|
|
44
|
+
apps: [
|
|
45
|
+
{
|
|
46
|
+
slots: [
|
|
47
|
+
{ slotId: 'test-slot.ui', op: WidgetOperationTypes.APPEND, id: 'widget1', element: '' },
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
slots: [
|
|
52
|
+
{ slotId: 'test-slot.ui', op: WidgetOperationTypes.APPEND, id: 'widget2', element: '' },
|
|
53
|
+
{ slotId: 'other-slot.ui', op: WidgetOperationTypes.APPEND, id: 'widget3', element: '' },
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
});
|
|
58
|
+
const result = getSlotOperations('test-slot.ui');
|
|
59
|
+
expect(result).toEqual([
|
|
60
|
+
{ slotId: 'test-slot.ui', op: WidgetOperationTypes.APPEND, id: 'widget1', element: '' },
|
|
61
|
+
{ slotId: 'test-slot.ui', op: WidgetOperationTypes.APPEND, id: 'widget2', element: '' },
|
|
62
|
+
]);
|
|
63
|
+
});
|
|
64
|
+
});
|
package/runtime/slots/utils.ts
CHANGED
|
@@ -35,18 +35,37 @@ export function isSlotOperationConditionSatisfied(operation: SlotOperation) {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
if (condition?.active !== undefined) {
|
|
39
|
-
let activeConditionRoleFound = false;
|
|
38
|
+
if (condition?.active !== undefined || condition?.inactive !== undefined) {
|
|
40
39
|
const activeRoles: string[] = getActiveRoles();
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
|
|
41
|
+
if (condition?.active !== undefined) {
|
|
42
|
+
let activeConditionRoleFound = false;
|
|
43
|
+
for (const conditionRole of condition.active) {
|
|
44
|
+
if (activeRoles.includes(conditionRole)) {
|
|
45
|
+
activeConditionRoleFound = true;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// If we couldn't find an active role in our list, then we've failed this condition.
|
|
51
|
+
if (!activeConditionRoleFound) {
|
|
52
|
+
return false;
|
|
45
53
|
}
|
|
46
54
|
}
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
55
|
+
|
|
56
|
+
if (condition?.inactive !== undefined) {
|
|
57
|
+
let inactiveConditionRoleFound = false;
|
|
58
|
+
for (const conditionRole of condition.inactive) {
|
|
59
|
+
if (activeRoles.includes(conditionRole)) {
|
|
60
|
+
inactiveConditionRoleFound = true;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// If we find an active role from our inactive list, then we've failed this condition.
|
|
66
|
+
if (inactiveConditionRoleFound) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
50
69
|
}
|
|
51
70
|
}
|
|
52
71
|
|
|
@@ -127,7 +127,7 @@ export function dispatchUnmountedEvent() {
|
|
|
127
127
|
*/
|
|
128
128
|
export function useElementSize() {
|
|
129
129
|
// Holds a reference to the ResizeObserver
|
|
130
|
-
const observerRef = useRef<ResizeObserver>();
|
|
130
|
+
const observerRef = useRef<ResizeObserver | null>(null);
|
|
131
131
|
|
|
132
132
|
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
|
133
133
|
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAnalyticsService,
|
|
3
|
+
MockAnalyticsService,
|
|
4
|
+
sendTrackEvent,
|
|
5
|
+
} from '../analytics';
|
|
6
|
+
import {
|
|
7
|
+
ensureAuthenticatedUser,
|
|
8
|
+
getAuthService,
|
|
9
|
+
getLoginRedirectUrl,
|
|
10
|
+
MockAuthService,
|
|
11
|
+
setAuthenticatedUser,
|
|
12
|
+
} from '../auth';
|
|
13
|
+
import {
|
|
14
|
+
getLoggingService,
|
|
15
|
+
logInfo,
|
|
16
|
+
MockLoggingService,
|
|
17
|
+
} from '../logging';
|
|
18
|
+
import initializeMockApp from './initializeMockApp';
|
|
19
|
+
|
|
20
|
+
describe('initializeMockApp', () => {
|
|
21
|
+
it('should create mock analytics, auth, and logging services, and a real i18n service', () => {
|
|
22
|
+
const {
|
|
23
|
+
analyticsService,
|
|
24
|
+
authService,
|
|
25
|
+
loggingService,
|
|
26
|
+
} = initializeMockApp();
|
|
27
|
+
|
|
28
|
+
expect(getAnalyticsService()).toBeInstanceOf(MockAnalyticsService);
|
|
29
|
+
expect(getAuthService()).toBeInstanceOf(MockAuthService);
|
|
30
|
+
expect(getLoggingService()).toBeInstanceOf(MockLoggingService);
|
|
31
|
+
|
|
32
|
+
const customAttributes = { custom: 'attribute' };
|
|
33
|
+
|
|
34
|
+
// Next, test a representative sample of functionality to prove mocking is working the way we hope.
|
|
35
|
+
|
|
36
|
+
// Analytics
|
|
37
|
+
sendTrackEvent('testEvent', customAttributes);
|
|
38
|
+
expect(analyticsService.sendTrackEvent).toHaveBeenCalledWith('testEvent', customAttributes);
|
|
39
|
+
// The mock analytics service calls checkIdentifyCalled when sendTrackEvent is called. Prove
|
|
40
|
+
// the jest.fn() works.
|
|
41
|
+
expect(analyticsService.checkIdentifyCalled).toHaveBeenCalledTimes(1);
|
|
42
|
+
|
|
43
|
+
// Auth
|
|
44
|
+
getLoginRedirectUrl('http://localhost/redirect/url');
|
|
45
|
+
setAuthenticatedUser({
|
|
46
|
+
userId: 'abc123',
|
|
47
|
+
username: 'Mock User',
|
|
48
|
+
roles: [],
|
|
49
|
+
administrator: false,
|
|
50
|
+
name: 'mock tester',
|
|
51
|
+
});
|
|
52
|
+
ensureAuthenticatedUser();
|
|
53
|
+
|
|
54
|
+
expect(authService.getLoginRedirectUrl).toHaveBeenCalledWith('http://localhost/redirect/url');
|
|
55
|
+
expect(authService.ensureAuthenticatedUser).toHaveBeenCalled();
|
|
56
|
+
// ensureAuthenticatedUser calls a few other things under the covers. Prove our mocking is
|
|
57
|
+
// working as expected across multiple levels.
|
|
58
|
+
expect(authService.fetchAuthenticatedUser).toHaveBeenCalled();
|
|
59
|
+
expect(authService.getAuthenticatedUser).toHaveBeenCalled();
|
|
60
|
+
expect(authService.redirectToLogin).not.toHaveBeenCalled();
|
|
61
|
+
|
|
62
|
+
// Logging
|
|
63
|
+
logInfo('logging info', customAttributes);
|
|
64
|
+
expect(loggingService.logInfo).toHaveBeenCalledWith('logging info', customAttributes);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import siteConfig from 'site.config';
|
|
2
2
|
|
|
3
|
+
import { LocalizedMessages, User } from '../../types';
|
|
4
|
+
|
|
3
5
|
import { configureAnalytics, MockAnalyticsService } from '../analytics';
|
|
4
6
|
import { configureAuth, MockAuthService, setAuthenticatedUser } from '../auth';
|
|
5
7
|
import { getSiteConfig, mergeSiteConfig } from '../config';
|
|
@@ -47,6 +49,9 @@ import mockMessages from './mockMessages';
|
|
|
47
49
|
export default function initializeMockApp({
|
|
48
50
|
messages = mockMessages,
|
|
49
51
|
authenticatedUser = null,
|
|
52
|
+
}: {
|
|
53
|
+
messages?: LocalizedMessages,
|
|
54
|
+
authenticatedUser?: User | null,
|
|
50
55
|
} = {}) {
|
|
51
56
|
const config = siteConfig;
|
|
52
57
|
mergeSiteConfig(config);
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { waitFor } from '@testing-library/react';
|
|
2
|
+
import {
|
|
3
|
+
camelCaseObject,
|
|
4
|
+
convertKeyNames,
|
|
5
|
+
getQueryParameters,
|
|
6
|
+
modifyObjectKeys,
|
|
7
|
+
snakeCaseObject,
|
|
8
|
+
} from '.';
|
|
9
|
+
|
|
10
|
+
describe('modifyObjectKeys', () => {
|
|
11
|
+
it('should use the provided modify function to change all keys in and object and its children', () => {
|
|
12
|
+
function meowKeys(key) {
|
|
13
|
+
return `${key}Meow`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const result = modifyObjectKeys(
|
|
17
|
+
{
|
|
18
|
+
one: undefined,
|
|
19
|
+
two: null,
|
|
20
|
+
three: '',
|
|
21
|
+
four: 0,
|
|
22
|
+
five: NaN,
|
|
23
|
+
six: [1, 2, { seven: 'woof' }],
|
|
24
|
+
eight: { nine: { ten: 'bark' }, eleven: true },
|
|
25
|
+
},
|
|
26
|
+
meowKeys,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
expect(result).toEqual({
|
|
30
|
+
oneMeow: undefined,
|
|
31
|
+
twoMeow: null,
|
|
32
|
+
threeMeow: '',
|
|
33
|
+
fourMeow: 0,
|
|
34
|
+
fiveMeow: NaN,
|
|
35
|
+
sixMeow: [1, 2, { sevenMeow: 'woof' }],
|
|
36
|
+
eightMeow: { nineMeow: { tenMeow: 'bark' }, elevenMeow: true },
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('camelCaseObject', () => {
|
|
42
|
+
it('should make everything camelCase', () => {
|
|
43
|
+
const result = camelCaseObject({
|
|
44
|
+
what_now: 'brown cow',
|
|
45
|
+
but_who: { says_you_people: 'okay then', but_how: { will_we_even_know: 'the song is over' } },
|
|
46
|
+
'dot.dot.dot': 123,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(result).toEqual({
|
|
50
|
+
whatNow: 'brown cow',
|
|
51
|
+
butWho: { saysYouPeople: 'okay then', butHow: { willWeEvenKnow: 'the song is over' } },
|
|
52
|
+
dotDotDot: 123,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('snakeCaseObject', () => {
|
|
58
|
+
it('should make everything snake_case', () => {
|
|
59
|
+
const result = snakeCaseObject({
|
|
60
|
+
whatNow: 'brown cow',
|
|
61
|
+
butWho: { saysYouPeople: 'okay then', butHow: { willWeEvenKnow: 'the song is over' } },
|
|
62
|
+
'dot.dot.dot': 123,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(result).toEqual({
|
|
66
|
+
what_now: 'brown cow',
|
|
67
|
+
but_who: { says_you_people: 'okay then', but_how: { will_we_even_know: 'the song is over' } },
|
|
68
|
+
dot_dot_dot: 123,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('convertKeyNames', () => {
|
|
74
|
+
it('should replace the specified keynames', () => {
|
|
75
|
+
const result = convertKeyNames(
|
|
76
|
+
{
|
|
77
|
+
one: { two: { three: 'four' } },
|
|
78
|
+
five: 'six',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
two: 'blue',
|
|
82
|
+
five: 'alive',
|
|
83
|
+
seven: 'heaven',
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
expect(result).toEqual({
|
|
88
|
+
one: { blue: { three: 'four' } },
|
|
89
|
+
alive: 'six',
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('getQueryParameters', () => {
|
|
95
|
+
it('should use window location by default', () => {
|
|
96
|
+
expect(global.location.search).toEqual('');
|
|
97
|
+
expect(getQueryParameters()).toEqual({});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should make an empty object with no query string', () => {
|
|
101
|
+
expect(getQueryParameters('')).toEqual({});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should make an object with one key value pair', () => {
|
|
105
|
+
expect(getQueryParameters('?foo=bar')).toEqual({
|
|
106
|
+
foo: 'bar',
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should make an object with one key value pair', () => {
|
|
111
|
+
expect(getQueryParameters('?foo=bar&baz=1')).toEqual({
|
|
112
|
+
foo: 'bar',
|
|
113
|
+
baz: '1',
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import Logo from './Logo';
|
|
4
|
+
|
|
5
|
+
describe('Logo component', () => {
|
|
6
|
+
it('renders the image with default URL when no props are provided', async () => {
|
|
7
|
+
const { getByRole, queryByRole } = render(<Logo />);
|
|
8
|
+
const image = getByRole('img');
|
|
9
|
+
expect(image).toHaveAttribute('src', 'https://edx-cdn.org/v3/default/logo.svg');
|
|
10
|
+
const link = queryByRole('link');
|
|
11
|
+
expect(link).toBeNull();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('renders the image with provided imageUrl', () => {
|
|
15
|
+
const testUrl = 'https://example.com/test-logo.svg';
|
|
16
|
+
const { getByRole, queryByRole } = render(<Logo imageUrl={testUrl} />);
|
|
17
|
+
const image = getByRole('img');
|
|
18
|
+
expect(image).toHaveAttribute('src', testUrl);
|
|
19
|
+
const link = queryByRole('link');
|
|
20
|
+
expect(link).toBeNull();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('renders the image wrapped in a Hyperlink when destinationUrl is provided', () => {
|
|
24
|
+
const testDestinationUrl = 'https://example.com';
|
|
25
|
+
const { getByRole } = render(<Logo destinationUrl={testDestinationUrl} />);
|
|
26
|
+
const link = getByRole('link');
|
|
27
|
+
expect(link).toHaveAttribute('href', testDestinationUrl);
|
|
28
|
+
const image = getByRole('img');
|
|
29
|
+
expect(image).toBeInTheDocument();
|
|
30
|
+
expect(image).toHaveAttribute('src', 'https://edx-cdn.org/v3/default/logo.svg');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default 'FileMock';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default 'SvgURL';
|
package/shell/app.scss
CHANGED
package/shell/app.ts
CHANGED
|
@@ -3,6 +3,14 @@ import { App } from '../types';
|
|
|
3
3
|
import { Footer } from './footer';
|
|
4
4
|
import { Header } from './header';
|
|
5
5
|
|
|
6
|
+
const inactive = [
|
|
7
|
+
'org.openedx.frontend.role.login',
|
|
8
|
+
'org.openedx.frontend.role.register',
|
|
9
|
+
'org.openedx.frontend.role.resetPassword',
|
|
10
|
+
'org.openedx.frontend.role.confirmPassword',
|
|
11
|
+
'org.openedx.frontend.role.welcome'
|
|
12
|
+
];
|
|
13
|
+
|
|
6
14
|
const app: App = {
|
|
7
15
|
appId: 'org.openedx.frontend.app.shell',
|
|
8
16
|
slots: [
|
|
@@ -11,12 +19,18 @@ const app: App = {
|
|
|
11
19
|
id: 'org.openedx.frontend.widget.header.main.v1',
|
|
12
20
|
op: WidgetOperationTypes.APPEND,
|
|
13
21
|
component: Header,
|
|
22
|
+
condition: {
|
|
23
|
+
inactive,
|
|
24
|
+
}
|
|
14
25
|
},
|
|
15
26
|
{
|
|
16
27
|
slotId: 'org.openedx.frontend.slot.footer.main.v1',
|
|
17
28
|
id: 'org.openedx.frontend.widget.footer.main.v1',
|
|
18
29
|
op: WidgetOperationTypes.APPEND,
|
|
19
30
|
component: Footer,
|
|
31
|
+
condition: {
|
|
32
|
+
inactive,
|
|
33
|
+
}
|
|
20
34
|
},
|
|
21
35
|
]
|
|
22
36
|
};
|
package/shell/dev/devHome/app.ts
CHANGED
|
@@ -6,10 +6,10 @@ const app: App = {
|
|
|
6
6
|
appId: 'org.openedx.frontend.app.dev.home',
|
|
7
7
|
routes: [{
|
|
8
8
|
path: '/',
|
|
9
|
-
id: 'dev.home',
|
|
9
|
+
id: 'org.openedx.frontend.route.dev.home',
|
|
10
10
|
Component: HomePage,
|
|
11
11
|
handle: {
|
|
12
|
-
role: '
|
|
12
|
+
role: 'org.openedx.frontend.role.devHome'
|
|
13
13
|
}
|
|
14
14
|
}],
|
|
15
15
|
messages,
|