From 10bb6f0b47f701f2b2159ebc9554e41e76c9d10a Mon Sep 17 00:00:00 2001
From: Kira Miller <31229189+kiram15@users.noreply.github.com>
Date: Tue, 13 Jun 2023 10:36:04 -0600
Subject: [PATCH] fix: adding query size check (#997)
* fix: adding query size check
* fix: more test converage
---
src/components/forms/data/reducer.ts | 1 -
.../ErrorReporting/tests/SyncHistory.test.jsx | 158 ++++++++++++++++++
.../settings/SettingsLMSTab/index.jsx | 29 ++--
3 files changed, 175 insertions(+), 13 deletions(-)
create mode 100644 src/components/settings/SettingsLMSTab/ErrorReporting/tests/SyncHistory.test.jsx
diff --git a/src/components/forms/data/reducer.ts b/src/components/forms/data/reducer.ts
index 8f9d8bb343..1aa424be21 100644
--- a/src/components/forms/data/reducer.ts
+++ b/src/components/forms/data/reducer.ts
@@ -131,7 +131,6 @@ export const FormReducer: FormReducerType = (
...oldStateMap,
[setStateArgs.name]: setStateArgs.state,
};
- console.log('setting state to ', newStateMap);
return { ...state, stateMap: newStateMap };
} default:
return state;
diff --git a/src/components/settings/SettingsLMSTab/ErrorReporting/tests/SyncHistory.test.jsx b/src/components/settings/SettingsLMSTab/ErrorReporting/tests/SyncHistory.test.jsx
new file mode 100644
index 0000000000..ffcede173a
--- /dev/null
+++ b/src/components/settings/SettingsLMSTab/ErrorReporting/tests/SyncHistory.test.jsx
@@ -0,0 +1,158 @@
+import React from 'react';
+import {
+ cleanup, screen, waitFor, waitForElementToBeRemoved,
+} from '@testing-library/react';
+import '@testing-library/jest-dom';
+import configureMockStore from 'redux-mock-store';
+
+import thunk from 'redux-thunk';
+import { Provider } from 'react-redux';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
+import { features } from '../../../../../config';
+
+import { renderWithRouter } from '../../../../test/testUtils';
+import SettingsLMSTab from '../..';
+import LmsApiService from '../../../../../data/services/LmsApiService';
+import { getChannelMap } from '../../../../../utils';
+
+const mockFetch = jest.fn();
+mockFetch.mockResolvedValue({ data: { refresh_token: 'foobar' } });
+
+const channelMapReturn = {
+ BLACKBOARD: {
+ fetch: mockFetch,
+ },
+};
+
+jest.mock('../../../../../utils', () => ({
+ ...jest.requireActual('../../../../../utils'),
+ getChannelMap: jest.fn(),
+}));
+
+getChannelMap.mockReturnValue(channelMapReturn);
+
+const enterpriseId = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee';
+const enterpriseSlug = 'test-slug';
+const enableSamlConfigurationScreen = false;
+const identityProvider = '';
+
+const initialState = {
+ portalConfiguration: {
+ enterpriseId, enterpriseSlug, enableSamlConfigurationScreen, identityProvider,
+ },
+};
+
+const existingConfigData = {
+ data: [{
+ channelCode: 'BLACKBOARD',
+ id: 1,
+ isValid: [{ missing: [] }, { incorrect: [] }],
+ active: true,
+ displayName: 'squish',
+ enterpriseCustomer: enterpriseId,
+ lastSyncAttemptedAt: '2022-11-22T20:59:56Z',
+ lastContentSyncAttemptedAt: '2022-11-22T20:59:56Z',
+ lastLearnerSyncAttemptedAt: null,
+ lastSyncErroredAt: null,
+ lastContentSyncErroredAt: null,
+ lastLearnerSyncErroredAt: null,
+ lastModifiedAt: '2023-05-05T14:51:53.473144Z',
+ }],
+};
+
+const configData = {
+ data: {
+ channelCode: 'BLACKBOARD',
+ id: 1,
+ isValid: [{ missing: [] }, { incorrect: [] }],
+ active: true,
+ displayName: 'foobar',
+ enterpriseCustomer: enterpriseId,
+ lastSyncAttemptedAt: '2022-11-22T20:59:56Z',
+ lastContentSyncAttemptedAt: '2022-11-22T20:59:56Z',
+ lastLearnerSyncAttemptedAt: null,
+ lastSyncErroredAt: null,
+ lastContentSyncErroredAt: null,
+ lastLearnerSyncErroredAt: null,
+ lastModifiedAt: '2023-05-05T14:51:53.473144Z',
+ },
+};
+
+const mockStore = configureMockStore([thunk]);
+window.open = jest.fn();
+
+describe('Test sync history page full flow', () => {
+ afterEach(() => {
+ cleanup();
+ jest.clearAllMocks();
+ });
+
+ test('Test opening sync history page', async () => {
+ getAuthenticatedUser.mockReturnValue({
+ administrator: true,
+ });
+ features.FEATURE_INTEGRATION_REPORTING = true;
+
+ const mockFetchSingleConfig = jest.spyOn(LmsApiService, 'fetchSingleBlackboardConfig');
+ const mockFetchExistingConfigs = jest.spyOn(LmsApiService, 'fetchEnterpriseCustomerIntegrationConfigs');
+ mockFetchSingleConfig.mockResolvedValue(configData);
+ mockFetchExistingConfigs.mockResolvedValue(existingConfigData);
+
+ const SettingsLMSWrapper = () => (
+
+
+
+
+
+ );
+
+ renderWithRouter();
+ const skeleton = screen.getAllByTestId('skeleton');
+ await waitForElementToBeRemoved(skeleton);
+ await (expect(screen.getByText('squish')));
+
+ const syncHistoryButton = screen.getByText('View sync history');
+ expect(syncHistoryButton.getAttribute('href')).toBe(`/${configData.data.channelCode}/${configData.data.id}`);
+ });
+ test('Test configure action from sync history', async () => {
+ const mockFetchSingleConfig = jest.spyOn(LmsApiService, 'fetchSingleBlackboardConfig');
+ mockFetchSingleConfig.mockResolvedValue(configData);
+
+ const location = {
+ ...window.location,
+ search: `?lms=${configData.data.channelCode}&id=${configData.data.id}`,
+ };
+ Object.defineProperty(window, 'location', {
+ writable: true,
+ value: location,
+ });
+
+ const SettingsLMSWrapper = () => (
+
+
+
+
+
+ );
+
+ renderWithRouter();
+ expect(window.location.search).toEqual(`?lms=${configData.data.channelCode}&id=${configData.data.id}`);
+ await waitFor(() => expect(mockFetch).toHaveBeenCalledWith('1'));
+ // opens stepper
+ await waitFor(() => expect(screen.getByText('New learning platform integration')));
+ screen.debug(undefined, 100000);
+ });
+});
diff --git a/src/components/settings/SettingsLMSTab/index.jsx b/src/components/settings/SettingsLMSTab/index.jsx
index 7103e24e69..54c3c3fa1d 100644
--- a/src/components/settings/SettingsLMSTab/index.jsx
+++ b/src/components/settings/SettingsLMSTab/index.jsx
@@ -1,5 +1,7 @@
import _ from 'lodash';
-import React, { useCallback, useEffect, useState } from 'react';
+import React, {
+ useCallback, useEffect, useMemo, useState,
+} from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
@@ -10,7 +12,7 @@ import {
import { Add, Info } from '@edx/paragon/icons';
import { logError } from '@edx/frontend-platform/logging';
-import { camelCaseDictArray, channelMapping } from '../../../utils';
+import { camelCaseDictArray, getChannelMap } from '../../../utils';
import LMSConfigPage from './LMSConfigPage';
import ExistingLMSCardDeck from './ExistingLMSCardDeck';
import NoConfigCard from './NoConfigCard';
@@ -44,6 +46,7 @@ const SettingsLMSTab = ({
const [isLmsStepperOpen, openLmsStepper, closeLmsStepper] = useToggle(false);
const toastMessages = [ACTIVATE_TOAST_MESSAGE, DELETE_TOAST_MESSAGE, INACTIVATE_TOAST_MESSAGE, SUBMIT_TOAST_MESSAGE];
const { dispatch } = useFormContext();
+ const channelMap = useMemo(() => getChannelMap(), []);
// onClick function for existing config cards' edit action
const editExistingConfig = useCallback((configData, configType) => {
@@ -62,18 +65,20 @@ const SettingsLMSTab = ({
openLmsStepper();
}, [dispatch, openLmsStepper]);
- // we pass in params (configId and lmsType) from SyncHistory when user wants to edit that config
useEffect(() => {
const query = new URLSearchParams(window.location.search);
- const fetchData = async () => channelMapping[query.get('lms')].fetch(query.get('id'));
- fetchData()
- .then((response) => {
- editExistingConfig(camelCaseObject(response.data), query.get('id'));
- })
- .catch((err) => {
- logError(err);
- });
- }, [editExistingConfig]);
+ // if we have passed in params (lmsType and configId) from SyncHistory, user wants to edit that config
+ if (query.has('lms') && query.has('id')) {
+ const fetchData = async () => channelMap[query.get('lms')].fetch(query.get('id'));
+ fetchData()
+ .then((response) => {
+ editExistingConfig(camelCaseObject(response.data), query.get('id'));
+ })
+ .catch((err) => {
+ logError(err);
+ });
+ }
+ }, [channelMap, editExistingConfig]);
const fetchExistingConfigs = useCallback(() => {
const options = { enterprise_customer: enterpriseId };