Merge pull request #1515 from arbrandes/frontend-base-fixes
feat: Prepare for publication to NPM
This commit is contained in:
15
.gitignore
vendored
15
.gitignore
vendored
@@ -1,20 +1,15 @@
|
||||
.DS_Store
|
||||
.eslintcache
|
||||
.idea
|
||||
node_modules
|
||||
npm-debug.log
|
||||
coverage
|
||||
module.config.js
|
||||
|
||||
dist/
|
||||
/*.tgz
|
||||
|
||||
### i18n ###
|
||||
src/i18n/transifex_input.json
|
||||
temp/babel-plugin-react-intl
|
||||
|
||||
### pyenv ###
|
||||
.python-version
|
||||
|
||||
### Emacs ###
|
||||
### Editors ###
|
||||
.DS_Store
|
||||
*~
|
||||
/temp
|
||||
/.vscode
|
||||
src/i18n/messages
|
||||
|
||||
15
.npmignore
15
.npmignore
@@ -1,11 +1,6 @@
|
||||
.eslintignore
|
||||
.eslintrc.json
|
||||
.gitignore
|
||||
docker-compose.yml
|
||||
Dockerfile
|
||||
Makefile
|
||||
npm-debug.log
|
||||
|
||||
coverage
|
||||
__mocks__
|
||||
node_modules
|
||||
public
|
||||
*.test.js
|
||||
*.test.jsx
|
||||
*.test.ts
|
||||
*.test.tsx
|
||||
|
||||
20
README.rst
20
README.rst
@@ -34,26 +34,6 @@ Installation
|
||||
.. _Tutor: https://github.com/overhangio/tutor
|
||||
.. _relevant tutor-mfe documentation: https://github.com/overhangio/tutor-mfe?tab=readme-ov-file#mfe-development
|
||||
|
||||
Devstack (Deprecated) instructions
|
||||
==================================
|
||||
|
||||
1. Install Devstack using the `Getting Started <https://github.com/openedx/devstack#getting-started>`_ instructions.
|
||||
|
||||
2. Start up LMS, if it's not already started.
|
||||
|
||||
4. Within this project (frontend-app-authn), install requirements and start the development server:
|
||||
|
||||
.. code-block::
|
||||
|
||||
npm install
|
||||
npm start # The server will run on port 1999
|
||||
|
||||
5. Once the dev server is up, visit http://localhost:1999 to access the MFE
|
||||
|
||||
.. image:: ./docs/images/frontend-app-authn-localhost-preview.png
|
||||
|
||||
**Note:** Follow `Enable social auth locally <docs/how_tos/enable_social_auth.rst>`_ for enabling Social Sign-on Buttons (SSO) locally
|
||||
|
||||
Environment Variables/Setup Notes
|
||||
=================================
|
||||
|
||||
|
||||
24
package-lock.json
generated
24
package-lock.json
generated
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"name": "@edx/frontend-app-authn",
|
||||
"name": "@openedx/frontend-app-authn",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@edx/frontend-app-authn",
|
||||
"name": "@openedx/frontend-app-authn",
|
||||
"version": "1.0.0",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.3",
|
||||
"@edx/openedx-atlas": "^0.6.0",
|
||||
"@edx/openedx-atlas": "^0.7.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||
@@ -41,14 +41,14 @@
|
||||
"react-test-renderer": "^18.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@openedx/frontend-base": "^1.0.0-alpha.0",
|
||||
"@openedx/frontend-base": "^1.0.0-alpha.1",
|
||||
"@openedx/paragon": "^22",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react-redux": "^8.1.3",
|
||||
"react-redux": "^8",
|
||||
"react-router": "^6",
|
||||
"react-router-dom": "^6",
|
||||
"redux": "^4.2.1"
|
||||
"redux": "^4"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
@@ -2048,9 +2048,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/openedx-atlas": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@edx/openedx-atlas/-/openedx-atlas-0.6.2.tgz",
|
||||
"integrity": "sha512-28Q8vzJDMS4wUxdkbIUBQpzWJ3HTdMaGlaEhFjrVGfuZkh++1AG6Tn/7FMD88cegalYAkphu530VQCHEkMZQhw==",
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/openedx-atlas/-/openedx-atlas-0.7.0.tgz",
|
||||
"integrity": "sha512-jqv0IV1pHsSn9+RO8Rdsr8jm3SOd84CCzzmo2QC9yvh1MK1+p4YDURQLpmmgKJ0JzE5Cb6ImhnNL/ogpJ2wetQ==",
|
||||
"license": "AGPL-3.0",
|
||||
"bin": {
|
||||
"atlas": "atlas"
|
||||
@@ -3666,9 +3666,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-base": {
|
||||
"version": "1.0.0-alpha.0",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/frontend-base/-/frontend-base-1.0.0-alpha.0.tgz",
|
||||
"integrity": "sha512-7IgkQcQZ7AeLVPpc26KpTS4VI01s3+vXfDrYFRJNcmCN8kjx7OhCMauIAMDmpA36cjm37NNndxIuihscsUo/Bg==",
|
||||
"version": "1.0.0-alpha.1",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/frontend-base/-/frontend-base-1.0.0-alpha.1.tgz",
|
||||
"integrity": "sha512-8NSHhC32FMlvFhXBggCyNE1uZ0bghtq3a0iY8W0/7S+9XZUUOQrvb0w+YUIkpf0CrzxS8jV0eRh4W5EF4IBVyQ==",
|
||||
"license": "AGPL-3.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
|
||||
11
package.json
11
package.json
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@edx/frontend-app-authn",
|
||||
"name": "@openedx/frontend-app-authn",
|
||||
"version": "1.0.0",
|
||||
"description": "Frontend authentication",
|
||||
"repository": {
|
||||
@@ -7,6 +7,9 @@
|
||||
"url": "git+https://github.com/openedx/frontend-app-authn.git"
|
||||
},
|
||||
"main": "src/index.ts",
|
||||
"files": [
|
||||
"/src"
|
||||
],
|
||||
"browserslist": [
|
||||
"extends @edx/browserslist-config"
|
||||
],
|
||||
@@ -22,7 +25,7 @@
|
||||
"snapshot": "openedx test --updateSnapshot",
|
||||
"test": "openedx test --coverage --passWithNoTests"
|
||||
},
|
||||
"author": "edX",
|
||||
"author": "Open edX",
|
||||
"license": "AGPL-3.0",
|
||||
"homepage": "https://github.com/openedx/frontend-app-authn#readme",
|
||||
"publishConfig": {
|
||||
@@ -33,7 +36,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.3",
|
||||
"@edx/openedx-atlas": "^0.6.0",
|
||||
"@edx/openedx-atlas": "^0.7.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||
@@ -64,7 +67,7 @@
|
||||
"react-test-renderer": "^18.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@openedx/frontend-base": "^1.0.0-alpha.0",
|
||||
"@openedx/frontend-base": "^1.0.0-alpha.1",
|
||||
"@openedx/paragon": "^22",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<title><%= (htmlWebpackPlugin.options.SITE_NAME && htmlWebpackPlugin.options.SITE_NAME != 'null') ? 'Authentication | ' + htmlWebpackPlugin.options.SITE_NAME : 'Authentication' %></title>
|
||||
<title>Authentication Development Site></title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="<%=htmlWebpackPlugin.options.FAVICON_URL%>" type="image/x-icon"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { EnvironmentTypes, SiteConfig } from '@openedx/frontend-base';
|
||||
|
||||
import { appId } from './src/constants';
|
||||
|
||||
const siteConfig: SiteConfig = {
|
||||
siteId: 'test-site',
|
||||
siteName: 'Test Site',
|
||||
@@ -10,7 +12,7 @@ const siteConfig: SiteConfig = {
|
||||
|
||||
environment: EnvironmentTypes.TEST,
|
||||
apps: [{
|
||||
appId: 'test-app',
|
||||
appId,
|
||||
config: {
|
||||
ACTIVATION_EMAIL_SUPPORT_LINK: null,
|
||||
ALLOW_PUBLIC_ACCOUNT_CREATION: false,
|
||||
@@ -37,7 +39,6 @@ const siteConfig: SiteConfig = {
|
||||
PRIVACY_POLICY: null,
|
||||
SEARCH_CATALOG_URL: null,
|
||||
SESSION_COOKIE_DOMAIN: 'local.openedx.io',
|
||||
SHOW_CONFIGURABLE_EDX_FIELDS: false,
|
||||
SHOW_REGISTRATION_LINKS: false,
|
||||
TOS_AND_HONOR_CODE: null,
|
||||
TOS_LINK: null,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Provider as ReduxProvider } from 'react-redux';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { CurrentAppProvider } from '@openedx/frontend-base';
|
||||
|
||||
import { appId } from './app';
|
||||
import { appId } from './constants';
|
||||
import {
|
||||
registerIcons,
|
||||
} from './common-components';
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { App } from '@openedx/frontend-base';
|
||||
import { appId } from './constants';
|
||||
import routes from './routes';
|
||||
import messages from './i18n';
|
||||
|
||||
export const appId = 'org.openedx.frontend.app.authn';
|
||||
|
||||
const app: App = {
|
||||
appId,
|
||||
routes,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { IntlProvider, mergeSiteConfig } from '@openedx/frontend-base';
|
||||
import { IntlProvider, mergeAppConfig } from '@openedx/frontend-base';
|
||||
import { render } from '@testing-library/react';
|
||||
import { Context as ResponsiveContext } from 'react-responsive';
|
||||
|
||||
import BaseContainer from '../index';
|
||||
import { appId } from '../../constants';
|
||||
|
||||
const LargeScreen = {
|
||||
wrappingComponent: ResponsiveContext.Provider,
|
||||
@@ -25,10 +26,8 @@ describe('Base component tests', () => {
|
||||
});
|
||||
|
||||
it('renders Image layout when ENABLE_IMAGE_LAYOUT configuration is enabled', () => {
|
||||
mergeSiteConfig({
|
||||
custom: {
|
||||
ENABLE_IMAGE_LAYOUT: true,
|
||||
}
|
||||
mergeAppConfig(appId, {
|
||||
ENABLE_IMAGE_LAYOUT: true,
|
||||
});
|
||||
|
||||
const { container } = render(
|
||||
|
||||
1
src/constants.ts
Normal file
1
src/constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const appId = 'org.openedx.frontend.app.authn';
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import {
|
||||
configureI18n, injectIntl, IntlProvider, mergeSiteConfig
|
||||
configureI18n, injectIntl, IntlProvider, mergeAppConfig
|
||||
} from '@openedx/frontend-base';
|
||||
import {
|
||||
fireEvent, render, screen,
|
||||
@@ -11,6 +11,7 @@ import configureStore from 'redux-mock-store';
|
||||
|
||||
import { INTERNAL_SERVER_ERROR, LOGIN_PAGE } from '../../data/constants';
|
||||
import { PASSWORD_RESET } from '../../reset-password/data/constants';
|
||||
import { appId } from '../../constants';
|
||||
import { setForgotPasswordFormData } from '../data/actions';
|
||||
import ForgotPasswordPage from '../ForgotPasswordPage';
|
||||
|
||||
@@ -41,11 +42,9 @@ const initialState = {
|
||||
};
|
||||
|
||||
describe('ForgotPasswordPage', () => {
|
||||
mergeSiteConfig({
|
||||
custom: {
|
||||
LOGIN_ISSUE_SUPPORT_LINK: '',
|
||||
INFO_EMAIL: '',
|
||||
}
|
||||
mergeAppConfig(appId, {
|
||||
LOGIN_ISSUE_SUPPORT_LINK: '',
|
||||
INFO_EMAIL: '',
|
||||
});
|
||||
|
||||
let props = {};
|
||||
@@ -81,10 +80,8 @@ describe('ForgotPasswordPage', () => {
|
||||
});
|
||||
|
||||
it('should display need other help signing in button', () => {
|
||||
mergeSiteConfig({
|
||||
custom: {
|
||||
LOGIN_ISSUE_SUPPORT_LINK: '/support',
|
||||
}
|
||||
mergeAppConfig(appId, {
|
||||
LOGIN_ISSUE_SUPPORT_LINK: '/support',
|
||||
});
|
||||
render(reduxWrapper(<IntlForgotPasswordPage {...props} />));
|
||||
const forgotPasswordButton = screen.findByText('Need help signing in?');
|
||||
|
||||
@@ -4,14 +4,14 @@ import {
|
||||
} from '@testing-library/react';
|
||||
|
||||
|
||||
import { testAppId } from '../../setupTest';
|
||||
import { appId } from '../../constants';
|
||||
import { ACCOUNT_ACTIVATION_MESSAGE } from '../data/constants';
|
||||
import AccountActivationMessage from '../AccountActivationMessage';
|
||||
|
||||
const IntlAccountActivationMessage = injectIntl(AccountActivationMessage);
|
||||
const providerWrapper = children => (
|
||||
<IntlProvider locale="en">
|
||||
<CurrentAppProvider appId={testAppId}>
|
||||
<CurrentAppProvider appId={appId}>
|
||||
{children}
|
||||
</CurrentAppProvider>
|
||||
</IntlProvider>
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
} from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import { testAppId, initializeMockServices } from '../../setupTest';
|
||||
import { appId } from '../../constants';
|
||||
import {
|
||||
ACCOUNT_LOCKED_OUT,
|
||||
ALLOWED_DOMAIN_LOGIN_ERROR,
|
||||
@@ -29,7 +29,7 @@ jest.mock('@openedx/frontend-base', () => ({
|
||||
const IntlLoginFailureMessage = injectIntl(LoginFailureMessage);
|
||||
const providerWrapper = children => (
|
||||
<IntlProvider locale="en">
|
||||
<CurrentAppProvider appId={testAppId}>
|
||||
<CurrentAppProvider appId={appId}>
|
||||
{children}
|
||||
</CurrentAppProvider>
|
||||
</IntlProvider>
|
||||
|
||||
@@ -10,7 +10,8 @@ import { act } from 'react-dom/test-utils';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import configureStore from 'redux-mock-store';
|
||||
|
||||
import { initializeMockServices, testAppId } from '../../setupTest';
|
||||
import { appId } from '../../constants';
|
||||
import { initializeMockServices } from '../../setupTest';
|
||||
import { COMPLETE_STATE, LOGIN_PAGE, PENDING_STATE } from '../../data/constants';
|
||||
import { backupLoginFormBegin, dismissPasswordResetBanner, loginRequest } from '../data/actions';
|
||||
import { INTERNAL_SERVER_ERROR } from '../data/constants';
|
||||
@@ -30,7 +31,7 @@ describe('LoginPage', () => {
|
||||
const reduxWrapper = children => (
|
||||
<IntlProvider locale="en">
|
||||
<MemoryRouter>
|
||||
<CurrentAppProvider appId={testAppId}>
|
||||
<CurrentAppProvider appId={appId}>
|
||||
<Provider store={store}>{children}</Provider>
|
||||
</CurrentAppProvider>
|
||||
</MemoryRouter>
|
||||
@@ -85,7 +86,7 @@ describe('LoginPage', () => {
|
||||
it('should submit form for valid input', () => {
|
||||
store.dispatch = jest.fn(store.dispatch);
|
||||
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: '',
|
||||
});
|
||||
|
||||
@@ -272,7 +273,7 @@ describe('LoginPage', () => {
|
||||
{ selector: `#${ssoProvider.id}` },
|
||||
)).toBeDefined();
|
||||
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: '',
|
||||
});
|
||||
});
|
||||
@@ -337,7 +338,7 @@ describe('LoginPage', () => {
|
||||
// ******** test enterprise login disabled scenarios ********
|
||||
|
||||
it('should show sign-in header for institution login if enterprise login is disabled', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: true,
|
||||
});
|
||||
|
||||
@@ -358,13 +359,13 @@ describe('LoginPage', () => {
|
||||
expect(queryByText('Company or school credentials')).toBeNull();
|
||||
expect(queryByText('Institution/campus credentials')).toBeDefined();
|
||||
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('should show sign-in header with secondary Providers and without Providers', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: true,
|
||||
});
|
||||
|
||||
@@ -385,7 +386,7 @@ describe('LoginPage', () => {
|
||||
expect(queryByText('Or sign in with:')).toBeDefined();
|
||||
expect(queryByText('Institution/campus credentials')).toBeDefined();
|
||||
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: '',
|
||||
});
|
||||
});
|
||||
@@ -424,7 +425,7 @@ describe('LoginPage', () => {
|
||||
expect(queryByText('Company or school credentials')).toBeNull();
|
||||
expect(queryByText('Institution/campus credentials')).toBeDefined();
|
||||
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: '',
|
||||
});
|
||||
});
|
||||
@@ -694,7 +695,7 @@ describe('LoginPage', () => {
|
||||
const { container } = render(reduxWrapper(<IntlLoginPage {...props} />));
|
||||
expect(container.querySelector(`#${ssoProvider.id}`).querySelector('#provider-name').textContent).toEqual(`${ssoProvider.name}`);
|
||||
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: '',
|
||||
});
|
||||
});
|
||||
@@ -712,7 +713,7 @@ describe('LoginPage', () => {
|
||||
},
|
||||
});
|
||||
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ALLOW_PUBLIC_ACCOUNT_CREATION: true,
|
||||
SHOW_REGISTRATION_LINKS: true,
|
||||
});
|
||||
@@ -727,7 +728,7 @@ describe('LoginPage', () => {
|
||||
});
|
||||
|
||||
it('should render other ways to sign in button when public account creation is disabled', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ALLOW_PUBLIC_ACCOUNT_CREATION: false,
|
||||
});
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import configureStore from 'redux-mock-store';
|
||||
|
||||
import { testAppId } from '../setupTest';
|
||||
import { appId } from '../constants';
|
||||
import { clearThirdPartyAuthContextErrorMessage } from '../common-components/data/actions';
|
||||
import {
|
||||
COMPLETE_STATE, LOGIN_PAGE, REGISTER_PAGE,
|
||||
@@ -43,7 +43,7 @@ describe('Logistration', () => {
|
||||
const reduxWrapper = children => (
|
||||
<IntlProvider locale="en">
|
||||
<MemoryRouter>
|
||||
<CurrentAppProvider appId={testAppId}>
|
||||
<CurrentAppProvider appId={appId}>
|
||||
<Provider store={store}>{children}</Provider>
|
||||
</CurrentAppProvider>
|
||||
</MemoryRouter>
|
||||
@@ -91,7 +91,7 @@ describe('Logistration', () => {
|
||||
});
|
||||
|
||||
it('should render registration page', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ALLOW_PUBLIC_ACCOUNT_CREATION: true,
|
||||
});
|
||||
|
||||
@@ -108,7 +108,7 @@ describe('Logistration', () => {
|
||||
});
|
||||
|
||||
it('should render login/register headings when show registration links is disabled', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ALLOW_PUBLIC_ACCOUNT_CREATION: true,
|
||||
SHOW_REGISTRATION_LINKS: false,
|
||||
});
|
||||
@@ -129,7 +129,7 @@ describe('Logistration', () => {
|
||||
});
|
||||
|
||||
it('should render only login page when public account creation is disabled', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ALLOW_PUBLIC_ACCOUNT_CREATION: false,
|
||||
DISABLE_ENTERPRISE_LOGIN: 'true',
|
||||
SHOW_REGISTRATION_LINKS: 'true',
|
||||
@@ -160,7 +160,7 @@ describe('Logistration', () => {
|
||||
});
|
||||
|
||||
it('should display institution login option when secondary providers are present', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: 'true',
|
||||
ALLOW_PUBLIC_ACCOUNT_CREATION: 'true',
|
||||
});
|
||||
@@ -186,13 +186,13 @@ describe('Logistration', () => {
|
||||
fireEvent.click(screen.getByText('Institution/campus credentials'));
|
||||
expect(screen.getByText('Test University')).toBeDefined();
|
||||
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('send tracking and page events when institutional login button is clicked', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: 'true',
|
||||
});
|
||||
|
||||
@@ -216,13 +216,13 @@ describe('Logistration', () => {
|
||||
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.institution_login_form.toggled', { category: 'user-engagement' });
|
||||
expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'institution_login');
|
||||
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not display institution register button', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: 'true',
|
||||
});
|
||||
|
||||
@@ -246,7 +246,7 @@ describe('Logistration', () => {
|
||||
fireEvent.click(screen.getByText('Institution/campus credentials'));
|
||||
expect(screen.getByText('Test University')).toBeDefined();
|
||||
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
DISABLE_ENTERPRISE_LOGIN: '',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
import { MemoryRouter, mockNavigate, useLocation } from 'react-router-dom';
|
||||
import configureStore from 'redux-mock-store';
|
||||
|
||||
import { testAppId } from '../../setupTest';
|
||||
import { appId } from '../../constants';
|
||||
import {
|
||||
AUTHN_PROGRESSIVE_PROFILING,
|
||||
COMPLETE_STATE, DEFAULT_REDIRECT_URL,
|
||||
@@ -87,7 +87,7 @@ describe('ProgressiveProfilingTests', () => {
|
||||
const reduxWrapper = children => (
|
||||
<IntlProvider locale="en">
|
||||
<MemoryRouter>
|
||||
<CurrentAppProvider appId={testAppId}>
|
||||
<CurrentAppProvider appId={appId}>
|
||||
<Provider store={store}>{children}</Provider>
|
||||
</CurrentAppProvider>
|
||||
</MemoryRouter>
|
||||
@@ -111,7 +111,7 @@ describe('ProgressiveProfilingTests', () => {
|
||||
// ******** test form links and modal ********
|
||||
|
||||
it('should not display button "Learn more about how we use this information."', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
AUTHN_PROGRESSIVE_PROFILING_SUPPORT_LINK: '',
|
||||
});
|
||||
const { queryByRole } = render(reduxWrapper(<IntlProgressiveProfilingPage />));
|
||||
@@ -121,7 +121,7 @@ describe('ProgressiveProfilingTests', () => {
|
||||
});
|
||||
|
||||
it('should display button "Learn more about how we use this information."', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
AUTHN_PROGRESSIVE_PROFILING_SUPPORT_LINK: 'http://localhost:1999/support',
|
||||
});
|
||||
|
||||
@@ -157,7 +157,7 @@ describe('ProgressiveProfilingTests', () => {
|
||||
});
|
||||
|
||||
it('should send analytic event for support link click', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
AUTHN_PROGRESSIVE_PROFILING_SUPPORT_LINK: 'http://localhost:1999/support',
|
||||
});
|
||||
render(reduxWrapper(<IntlProgressiveProfilingPage />));
|
||||
@@ -200,7 +200,7 @@ describe('ProgressiveProfilingTests', () => {
|
||||
});
|
||||
|
||||
describe('Embedded Form Workflow Test', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
SEARCH_CATALOG_URL: 'http://localhost/search',
|
||||
});
|
||||
const host = 'http://example.com';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { injectIntl, IntlProvider, mergeSiteConfig } from '@openedx/frontend-base';
|
||||
import { injectIntl, IntlProvider } from '@openedx/frontend-base';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import configureStore from 'redux-mock-store';
|
||||
@@ -71,12 +71,6 @@ describe('CountryField', () => {
|
||||
});
|
||||
|
||||
describe('Test Country Field', () => {
|
||||
mergeSiteConfig({
|
||||
custom: {
|
||||
SHOW_CONFIGURABLE_EDX_FIELDS: true,
|
||||
}
|
||||
});
|
||||
|
||||
const emptyFieldValidation = {
|
||||
country: 'Select your country or region of residence',
|
||||
};
|
||||
|
||||
@@ -3,21 +3,21 @@ import {
|
||||
} from '@openedx/frontend-base';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { testAppId } from '../../../setupTest';
|
||||
import { appId } from '../../../constants';
|
||||
import { HonorCode } from '../index';
|
||||
|
||||
const IntlHonorCode = injectIntl(HonorCode);
|
||||
|
||||
const providerWrapper = children => (
|
||||
<IntlProvider locale="en">
|
||||
<CurrentAppProvider appId={testAppId}>
|
||||
<CurrentAppProvider appId={appId}>
|
||||
{children}
|
||||
</CurrentAppProvider>
|
||||
</IntlProvider>
|
||||
);
|
||||
|
||||
describe('HonorCodeTest', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
PRIVACY_POLICY: 'http://privacy-policy.com',
|
||||
TOS_AND_HONOR_CODE: 'http://tos-and-honot-code.com',
|
||||
});
|
||||
|
||||
@@ -11,7 +11,8 @@ import configureStore from 'redux-mock-store';
|
||||
import {
|
||||
AUTHN_PROGRESSIVE_PROFILING, COMPLETE_STATE, PENDING_STATE, REGISTER_PAGE,
|
||||
} from '../data/constants';
|
||||
import { initializeMockServices, testAppId } from '../setupTest';
|
||||
import { appId } from '../constants';
|
||||
import { initializeMockServices } from '../setupTest';
|
||||
import {
|
||||
backupRegistrationFormBegin,
|
||||
clearRegistrationBackendError,
|
||||
@@ -51,7 +52,7 @@ jest.mock('react-router-dom', () => {
|
||||
jest.mock('universal-cookie');
|
||||
|
||||
describe('RegistrationPage', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
PRIVACY_POLICY: 'https://privacy-policy.com',
|
||||
TOS_AND_HONOR_CODE: 'https://tos-and-honot-code.com',
|
||||
USER_RETENTION_COOKIE_NAME: 'authn-returning-user',
|
||||
@@ -76,7 +77,7 @@ describe('RegistrationPage', () => {
|
||||
|
||||
const reduxWrapper = children => (
|
||||
<IntlProvider locale="en">
|
||||
<CurrentAppProvider appId={testAppId}>
|
||||
<CurrentAppProvider appId={appId}>
|
||||
<Provider store={store}>{children}</Provider>
|
||||
</CurrentAppProvider>
|
||||
</IntlProvider>
|
||||
@@ -256,7 +257,7 @@ describe('RegistrationPage', () => {
|
||||
});
|
||||
|
||||
it('should submit form with marketing email opt in value', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
MARKETING_EMAILS_OPT_IN: 'true',
|
||||
});
|
||||
|
||||
@@ -278,13 +279,13 @@ describe('RegistrationPage', () => {
|
||||
fireEvent.click(button);
|
||||
expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...payload }));
|
||||
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
MARKETING_EMAILS_OPT_IN: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('should submit form without UsernameField when autoGeneratedUsernameEnabled is true', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ENABLE_AUTO_GENERATED_USERNAME: true,
|
||||
});
|
||||
jest.spyOn(global.Date, 'now').mockImplementation(() => 0);
|
||||
@@ -301,20 +302,20 @@ describe('RegistrationPage', () => {
|
||||
const button = container.querySelector('button.btn-brand');
|
||||
fireEvent.click(button);
|
||||
expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...payload }));
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ENABLE_AUTO_GENERATED_USERNAME: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not display UsernameField when ENABLE_AUTO_GENERATED_USERNAME is true', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ENABLE_AUTO_GENERATED_USERNAME: true,
|
||||
});
|
||||
|
||||
const { queryByLabelText } = render(routerWrapper(reduxWrapper(<IntlRegistrationPage {...props} />)));
|
||||
expect(queryByLabelText('Username')).toBeNull();
|
||||
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ENABLE_AUTO_GENERATED_USERNAME: false,
|
||||
});
|
||||
});
|
||||
@@ -431,7 +432,7 @@ describe('RegistrationPage', () => {
|
||||
});
|
||||
|
||||
it('should display opt-in/opt-out checkbox', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
MARKETING_EMAILS_OPT_IN: 'true',
|
||||
});
|
||||
|
||||
@@ -439,7 +440,7 @@ describe('RegistrationPage', () => {
|
||||
const checkboxDivs = container.querySelectorAll('div.form-field--checkbox');
|
||||
expect(checkboxDivs.length).toEqual(1);
|
||||
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
MARKETING_EMAILS_OPT_IN: '',
|
||||
});
|
||||
});
|
||||
@@ -468,7 +469,7 @@ describe('RegistrationPage', () => {
|
||||
});
|
||||
|
||||
render(routerWrapper(reduxWrapper(<IntlRegistrationPage {...props} />)));
|
||||
expect(Cookies.prototype.set).toHaveBeenCalledWith(getAppConfig(testAppId).USER_RETENTION_COOKIE_NAME, true, { domain: 'local.openedx.io', path: '/' });
|
||||
expect(Cookies.prototype.set).toHaveBeenCalledWith(getAppConfig(appId).USER_RETENTION_COOKIE_NAME, true, { domain: 'local.openedx.io', path: '/' });
|
||||
});
|
||||
|
||||
it('should redirect to url returned in registration result after successful account creation', () => {
|
||||
@@ -490,7 +491,7 @@ describe('RegistrationPage', () => {
|
||||
});
|
||||
|
||||
it('should redirect to dashboard if features flags are configured but no optional fields are configured', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ENABLE_PROGRESSIVE_PROFILING_ON_AUTHN: true,
|
||||
});
|
||||
const dashboardUrl = 'https://test.com/testing-dashboard/';
|
||||
@@ -518,7 +519,7 @@ describe('RegistrationPage', () => {
|
||||
|
||||
it('should redirect to progressive profiling page if optional fields are configured', () => {
|
||||
getLocale.mockImplementation(() => ('en-us'));
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ENABLE_PROGRESSIVE_PROFILING_ON_AUTHN: true,
|
||||
});
|
||||
|
||||
@@ -679,7 +680,7 @@ describe('RegistrationPage', () => {
|
||||
|
||||
it('should call the postMessage API when embedded variant is rendered', () => {
|
||||
getLocale.mockImplementation(() => ('en-us'));
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ENABLE_PROGRESSIVE_PROFILING_ON_AUTHN: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { fireEvent, render } from '@testing-library/react';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import configureStore from 'redux-mock-store';
|
||||
|
||||
import { testAppId } from '../../../setupTest';
|
||||
import { appId } from '../../../constants';
|
||||
import { registerNewUser } from '../../data/actions';
|
||||
import { FIELDS } from '../../data/constants';
|
||||
import RegistrationPage from '../../RegistrationPage';
|
||||
@@ -30,7 +30,7 @@ const IntlRegistrationPage = injectIntl(RegistrationPage);
|
||||
const mockStore = configureStore();
|
||||
|
||||
describe('ConfigurableRegistrationForm', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
PRIVACY_POLICY: 'https://privacy-policy.com',
|
||||
TOS_AND_HONOR_CODE: 'https://tos-and-honot-code.com',
|
||||
});
|
||||
@@ -54,7 +54,7 @@ describe('ConfigurableRegistrationForm', () => {
|
||||
|
||||
const reduxWrapper = children => (
|
||||
<IntlProvider locale="en">
|
||||
<CurrentAppProvider appId={testAppId}>
|
||||
<CurrentAppProvider appId={appId}>
|
||||
<Provider store={store}>{children}</Provider>
|
||||
</CurrentAppProvider>
|
||||
</IntlProvider>
|
||||
@@ -126,7 +126,7 @@ describe('ConfigurableRegistrationForm', () => {
|
||||
};
|
||||
|
||||
describe('Test Configurable Fields', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ENABLE_DYNAMIC_REGISTRATION_FIELDS: true,
|
||||
});
|
||||
|
||||
@@ -201,7 +201,7 @@ describe('ConfigurableRegistrationForm', () => {
|
||||
});
|
||||
|
||||
it('should submit form with fields returned by backend in payload', () => {
|
||||
mergeAppConfig(testAppId, {
|
||||
mergeAppConfig(appId, {
|
||||
ENABLE_DYNAMIC_REGISTRATION_FIELDS: true,
|
||||
});
|
||||
getLocale.mockImplementation(() => ('en-us'));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import {
|
||||
configureI18n, getLocale, injectIntl, IntlProvider, mergeSiteConfig
|
||||
configureI18n, getLocale, injectIntl, IntlProvider, mergeAppConfig
|
||||
} from '@openedx/frontend-base';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
@@ -10,6 +10,7 @@ import configureStore from 'redux-mock-store';
|
||||
import {
|
||||
FORBIDDEN_REQUEST, INTERNAL_SERVER_ERROR, TPA_AUTHENTICATION_FAILURE, TPA_SESSION_EXPIRED,
|
||||
} from '../../data/constants';
|
||||
import { appId } from '../../../constants';
|
||||
import RegistrationPage from '../../RegistrationPage';
|
||||
import RegistrationFailureMessage from '../RegistrationFailure';
|
||||
|
||||
@@ -40,12 +41,10 @@ jest.mock('react-router-dom', () => {
|
||||
});
|
||||
|
||||
describe('RegistrationFailure', () => {
|
||||
mergeSiteConfig({
|
||||
custom: {
|
||||
PRIVACY_POLICY: 'https://privacy-policy.com',
|
||||
TOS_AND_HONOR_CODE: 'https://tos-and-honot-code.com',
|
||||
USER_RETENTION_COOKIE_NAME: 'authn-returning-user',
|
||||
}
|
||||
mergeAppConfig(appId, {
|
||||
PRIVACY_POLICY: 'https://privacy-policy.com',
|
||||
TOS_AND_HONOR_CODE: 'https://tos-and-honot-code.com',
|
||||
USER_RETENTION_COOKIE_NAME: 'authn-returning-user',
|
||||
});
|
||||
|
||||
let props = {};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import {
|
||||
configureI18n, getSiteConfig, getLocale, injectIntl, IntlProvider, mergeSiteConfig
|
||||
configureI18n, getSiteConfig, getLocale, injectIntl, IntlProvider, mergeAppConfig
|
||||
} from '@openedx/frontend-base';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
@@ -10,6 +10,7 @@ import configureStore from 'redux-mock-store';
|
||||
import {
|
||||
COMPLETE_STATE, LOGIN_PAGE, PENDING_STATE, REGISTER_PAGE,
|
||||
} from '../../../data/constants';
|
||||
import { appId } from '../../../constants';
|
||||
import RegistrationPage from '../../RegistrationPage';
|
||||
|
||||
jest.mock('@openedx/frontend-base', () => ({
|
||||
@@ -39,12 +40,10 @@ jest.mock('react-router-dom', () => {
|
||||
});
|
||||
|
||||
describe('ThirdPartyAuth', () => {
|
||||
mergeSiteConfig({
|
||||
custom: {
|
||||
PRIVACY_POLICY: 'https://privacy-policy.com',
|
||||
TOS_AND_HONOR_CODE: 'https://tos-and-honot-code.com',
|
||||
USER_RETENTION_COOKIE_NAME: 'authn-returning-user',
|
||||
}
|
||||
mergeAppConfig(appId, {
|
||||
PRIVACY_POLICY: 'https://privacy-policy.com',
|
||||
TOS_AND_HONOR_CODE: 'https://tos-and-honot-code.com',
|
||||
USER_RETENTION_COOKIE_NAME: 'authn-returning-user',
|
||||
});
|
||||
|
||||
let props = {};
|
||||
@@ -128,11 +127,6 @@ describe('ThirdPartyAuth', () => {
|
||||
};
|
||||
|
||||
describe('Test Third Party Auth', () => {
|
||||
mergeSiteConfig({
|
||||
custom: {
|
||||
SHOW_CONFIGURABLE_EDX_FIELDS: true,
|
||||
}
|
||||
});
|
||||
getLocale.mockImplementation(() => ('en-us'));
|
||||
|
||||
const secondaryProviders = {
|
||||
|
||||
@@ -8,7 +8,8 @@ import { act } from 'react-dom/test-utils';
|
||||
import { MemoryRouter, useParams } from 'react-router-dom';
|
||||
import configureStore from 'redux-mock-store';
|
||||
|
||||
import { testAppId, initializeMockServices } from '../../setupTest';
|
||||
import { appId } from '../../constants';
|
||||
import { initializeMockServices } from '../../setupTest';
|
||||
import { LOGIN_PAGE, RESET_PAGE } from '../../data/constants';
|
||||
import { resetPassword, validateToken } from '../data/actions';
|
||||
import {
|
||||
@@ -36,7 +37,7 @@ describe('ResetPasswordPage', () => {
|
||||
const reduxWrapper = children => (
|
||||
<IntlProvider locale="en">
|
||||
<MemoryRouter>
|
||||
<CurrentAppProvider appId={testAppId}>
|
||||
<CurrentAppProvider appId={appId}>
|
||||
<Provider store={store}>{children}</Provider>
|
||||
</CurrentAppProvider>
|
||||
</MemoryRouter>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Navigate } from 'react-router';
|
||||
|
||||
import { EmbeddedRegistrationRoute, NotFoundPage, UnAuthOnlyRoute } from './common-components';
|
||||
import { LOGIN_PAGE, PAGE_NOT_FOUND } from './data/constants';
|
||||
import { LOGIN_PAGE } from './data/constants';
|
||||
import { ForgotPasswordPage } from './forgot-password';
|
||||
import Logistration from './logistration/Logistration';
|
||||
import { ProgressiveProfiling } from './progressive-profiling';
|
||||
@@ -19,34 +19,49 @@ const routes = [
|
||||
path: 'register-embedded',
|
||||
element: (
|
||||
<EmbeddedRegistrationRoute><RegistrationPage /></EmbeddedRegistrationRoute>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
handle: {
|
||||
role: 'org.openedx.frontend.role.login',
|
||||
},
|
||||
element: (
|
||||
<UnAuthOnlyRoute><Logistration selectedPage={LOGIN_PAGE} /></UnAuthOnlyRoute>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'register',
|
||||
handle: {
|
||||
role: 'org.openedx.frontend.role.register',
|
||||
},
|
||||
element: (
|
||||
<UnAuthOnlyRoute><Logistration /></UnAuthOnlyRoute>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'reset',
|
||||
handle: {
|
||||
role: 'org.openedx.frontend.role.resetPassword',
|
||||
},
|
||||
element: (
|
||||
<UnAuthOnlyRoute><ForgotPasswordPage /></UnAuthOnlyRoute>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'password_reset_confirm/:token',
|
||||
handle: {
|
||||
role: 'org.openedx.frontend.role.confirmPassword',
|
||||
},
|
||||
element: (
|
||||
<ResetPasswordPage />
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'welcome',
|
||||
handle: {
|
||||
role: 'org.openedx.frontend.role.welcome',
|
||||
},
|
||||
element: (
|
||||
<ProgressiveProfiling />
|
||||
),
|
||||
@@ -57,12 +72,6 @@ const routes = [
|
||||
<NotFoundPage />
|
||||
),
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
element: (
|
||||
<Navigate replace to={PAGE_NOT_FOUND} />
|
||||
),
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@@ -5,8 +5,6 @@ import { addAppConfigs, configureAnalytics, configureAuth, configureLogging, get
|
||||
mergeSiteConfig(siteConfig);
|
||||
addAppConfigs();
|
||||
|
||||
export const testAppId = getSiteConfig().apps[0].appId;
|
||||
|
||||
export function initializeMockServices() {
|
||||
const loggingService = configureLogging(MockLoggingService, {
|
||||
config: getSiteConfig(),
|
||||
|
||||
Reference in New Issue
Block a user