[Enterprise Search] Update WS Overview logic to use new config data (#77122)

* Update AppLogic to use new data structure

* Update components to use AppLogic

* Remove unused props from logic file

* Fix failing tests

* Add tests to get 100% converage

The test added for app_logic will never happen but hey, 100 is 100.

Also added test to recent_activity for 100% coverage

* Use non-null assertion operator in logic file

TIL this is a thing

* Remove test for undefined
This commit is contained in:
Scotty Bollinger 2020-09-10 13:55:40 -05:00 committed by GitHub
parent 50ec790f71
commit 65abdfffee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 116 additions and 90 deletions

View file

@ -16,7 +16,28 @@ describe('AppLogic', () => {
});
const DEFAULT_VALUES = {
account: {},
hasInitialized: false,
isFederatedAuth: true,
organization: {},
};
const expectedLogicValues = {
account: {
canCreateInvitations: true,
canCreatePersonalSources: true,
groups: ['Default', 'Cats'],
id: 'some-id-string',
isAdmin: true,
isCurated: false,
viewedOnboardingPage: true,
},
hasInitialized: true,
isFederatedAuth: false,
organization: {
defaultOrgName: 'My Organization',
name: 'ACME Donuts',
},
};
it('has expected default values', () => {
@ -27,9 +48,7 @@ describe('AppLogic', () => {
it('sets values based on passed props', () => {
AppLogic.actions.initializeAppData(DEFAULT_INITIAL_APP_DATA);
expect(AppLogic.values).toEqual({
hasInitialized: true,
});
expect(AppLogic.values).toEqual(expectedLogicValues);
});
});
});

View file

@ -7,18 +7,26 @@
import { kea, MakeLogicType } from 'kea';
import { IInitialAppData } from '../../../common/types';
import { IWorkplaceSearchInitialData } from '../../../common/types/workplace_search';
import {
IOrganization,
IWorkplaceSearchInitialData,
IAccount,
} from '../../../common/types/workplace_search';
export interface IAppValues extends IWorkplaceSearchInitialData {
hasInitialized: boolean;
isFederatedAuth: boolean;
}
export interface IAppActions {
initializeAppData(props: IInitialAppData): void;
initializeAppData(props: IInitialAppData): IInitialAppData;
}
export const AppLogic = kea<MakeLogicType<IAppValues, IAppActions>>({
actions: {
initializeAppData: ({ workplaceSearch }) => workplaceSearch,
initializeAppData: ({ workplaceSearch, isFederatedAuth }) => ({
workplaceSearch,
isFederatedAuth,
}),
},
reducers: {
hasInitialized: [
@ -27,5 +35,23 @@ export const AppLogic = kea<MakeLogicType<IAppValues, IAppActions>>({
initializeAppData: () => true,
},
],
isFederatedAuth: [
true,
{
initializeAppData: (_, { isFederatedAuth }) => !!isFederatedAuth,
},
],
organization: [
{} as IOrganization,
{
initializeAppData: (_, { workplaceSearch }) => workplaceSearch!.organization,
},
],
account: [
{} as IAccount,
{
initializeAppData: (_, { workplaceSearch }) => workplaceSearch!.account,
},
],
},
});

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { setMockValues, mockValues, mockActions } from './overview_logic.mock';
export { setMockValues, mockOverviewValues, mockActions } from './overview_logic.mock';

View file

@ -5,19 +5,18 @@
*/
import { IOverviewValues } from '../overview_logic';
import { IAccount, IOrganization } from '../../../types';
export const mockValues = {
import { DEFAULT_INITIAL_APP_DATA } from '../../../../../../common/__mocks__';
const { workplaceSearch: mockAppValues } = DEFAULT_INITIAL_APP_DATA;
export const mockOverviewValues = {
accountsCount: 0,
activityFeed: [],
canCreateContentSources: false,
canCreateInvitations: false,
fpAccount: {} as IAccount,
hasOrgSources: false,
hasUsers: false,
isFederatedAuth: true,
isOldAccount: false,
organization: {} as IOrganization,
pendingInvitationsCount: 0,
personalSourcesCount: 0,
sourcesCount: 0,
@ -28,6 +27,8 @@ export const mockActions = {
initializeOverview: jest.fn(() => ({})),
};
const mockValues = { ...mockOverviewValues, ...mockAppValues, isFederatedAuth: true };
jest.mock('kea', () => ({
...(jest.requireActual('kea') as object),
useActions: jest.fn(() => ({ ...mockActions })),
@ -37,8 +38,5 @@ jest.mock('kea', () => ({
import { useValues } from 'kea';
export const setMockValues = (values: object) => {
(useValues as jest.Mock).mockImplementationOnce(() => ({
...mockValues,
...values,
}));
(useValues as jest.Mock).mockImplementation(() => ({ ...mockValues, ...values }));
};

View file

@ -25,6 +25,7 @@ const account = {
canCreatePersonalSources: true,
groups: [],
isCurated: false,
canCreateInvitations: true,
};
describe('OnboardingSteps', () => {
@ -60,9 +61,8 @@ describe('OnboardingSteps', () => {
describe('Users & Invitations', () => {
it('renders 0 users when not on federated auth', () => {
setMockValues({
canCreateInvitations: true,
isFederatedAuth: false,
fpAccount: account,
account,
accountsCount: 0,
hasUsers: false,
});
@ -78,7 +78,7 @@ describe('OnboardingSteps', () => {
it('renders completed users state', () => {
setMockValues({
isFederatedAuth: false,
fpAccount: account,
account,
accountsCount: 1,
hasUsers: true,
});
@ -90,7 +90,13 @@ describe('OnboardingSteps', () => {
});
it('disables link when the user cannot create invitations', () => {
setMockValues({ isFederatedAuth: false, canCreateInvitations: false });
setMockValues({
isFederatedAuth: false,
account: {
...account,
canCreateInvitations: false,
},
});
const wrapper = shallow(<OnboardingSteps />);
expect(wrapper.find(OnboardingCard).last().prop('actionPath')).toBe(undefined);
});
@ -98,6 +104,12 @@ describe('OnboardingSteps', () => {
describe('Org Name', () => {
it('renders button to change name', () => {
setMockValues({
organization: {
name: 'foo',
defaultOrgName: 'foo',
},
});
const wrapper = shallow(<OnboardingSteps />);
const button = wrapper

View file

@ -28,6 +28,7 @@ import { ORG_SOURCES_PATH, USERS_PATH, ORG_SETTINGS_PATH } from '../../routes';
import { ContentSection } from '../../components/shared/content_section';
import { AppLogic } from '../../app_logic';
import { OverviewLogic } from './overview_logic';
import { OnboardingCard } from './onboarding_card';
@ -58,16 +59,18 @@ const ONBOARDING_USERS_CARD_DESCRIPTION = i18n.translate(
);
export const OnboardingSteps: React.FC = () => {
const {
isFederatedAuth,
organization: { name, defaultOrgName },
account: { isCurated, canCreateInvitations },
} = useValues(AppLogic);
const {
hasUsers,
hasOrgSources,
canCreateContentSources,
canCreateInvitations,
accountsCount,
sourcesCount,
fpAccount: { isCurated },
organization: { name, defaultOrgName },
isFederatedAuth,
} = useValues(OverviewLogic);
const accountsPath =

View file

@ -14,18 +14,17 @@ import { i18n } from '@kbn/i18n';
import { ContentSection } from '../../components/shared/content_section';
import { ORG_SOURCES_PATH, USERS_PATH } from '../../routes';
import { AppLogic } from '../../app_logic';
import { OverviewLogic } from './overview_logic';
import { StatisticCard } from './statistic_card';
export const OrganizationStats: React.FC = () => {
const {
sourcesCount,
pendingInvitationsCount,
accountsCount,
personalSourcesCount,
isFederatedAuth,
} = useValues(OverviewLogic);
const { isFederatedAuth } = useValues(AppLogic);
const { sourcesCount, pendingInvitationsCount, accountsCount, personalSourcesCount } = useValues(
OverviewLogic
);
return (
<ContentSection

View file

@ -14,6 +14,7 @@ import { useActions, useValues } from 'kea';
import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
import { SendWorkplaceSearchTelemetry as SendTelemetry } from '../../../shared/telemetry';
import { AppLogic } from '../../app_logic';
import { OverviewLogic } from './overview_logic';
import { Loading } from '../../components/shared/loading';
@ -44,15 +45,12 @@ const HEADER_DESCRIPTION = i18n.translate(
);
export const Overview: React.FC = () => {
const { initializeOverview } = useActions(OverviewLogic);
const {
dataLoading,
hasUsers,
hasOrgSources,
isOldAccount,
organization: { name: orgName, defaultOrgName },
} = useValues(OverviewLogic);
} = useValues(AppLogic);
const { initializeOverview } = useActions(OverviewLogic);
const { dataLoading, hasUsers, hasOrgSources, isOldAccount } = useValues(OverviewLogic);
useEffect(() => {
initializeOverview();

View file

@ -9,7 +9,7 @@ import { resetContext } from 'kea';
jest.mock('../../../shared/http', () => ({ HttpLogic: { values: { http: { get: jest.fn() } } } }));
import { HttpLogic } from '../../../shared/http';
import { mockValues } from './__mocks__';
import { mockOverviewValues } from './__mocks__';
import { OverviewLogic } from './overview_logic';
describe('OverviewLogic', () => {
@ -20,32 +20,19 @@ describe('OverviewLogic', () => {
});
it('has expected default values', () => {
expect(OverviewLogic.values).toEqual(mockValues);
expect(OverviewLogic.values).toEqual(mockOverviewValues);
});
describe('setServerData', () => {
const feed = [{ foo: 'bar' }] as any;
const account = {
id: '1243',
groups: ['Default'],
isAdmin: true,
isCurated: false,
canCreatePersonalSources: true,
viewedOnboardingPage: false,
};
const org = { name: 'ACME', defaultOrgName: 'Org' };
const data = {
accountsCount: 1,
activityFeed: feed,
canCreateContentSources: true,
canCreateInvitations: true,
fpAccount: account,
hasOrgSources: true,
hasUsers: true,
isFederatedAuth: false,
isOldAccount: true,
organization: org,
pendingInvitationsCount: 1,
personalSourcesCount: 1,
sourcesCount: 1,
@ -60,10 +47,6 @@ describe('OverviewLogic', () => {
});
it('will set server values', () => {
expect(OverviewLogic.values.organization).toEqual(org);
expect(OverviewLogic.values.isFederatedAuth).toEqual(false);
expect(OverviewLogic.values.fpAccount).toEqual(account);
expect(OverviewLogic.values.canCreateInvitations).toEqual(true);
expect(OverviewLogic.values.hasUsers).toEqual(true);
expect(OverviewLogic.values.hasOrgSources).toEqual(true);
expect(OverviewLogic.values.canCreateContentSources).toEqual(true);

View file

@ -7,24 +7,18 @@
import { kea, MakeLogicType } from 'kea';
import { HttpLogic } from '../../../shared/http';
import { IAccount, IOrganization } from '../../types';
import { IFeedActivity } from './recent_activity';
export interface IOverviewServerData {
hasUsers: boolean;
hasOrgSources: boolean;
canCreateContentSources: boolean;
canCreateInvitations: boolean;
isOldAccount: boolean;
sourcesCount: number;
pendingInvitationsCount: number;
accountsCount: number;
personalSourcesCount: number;
activityFeed: IFeedActivity[];
organization: IOrganization;
isFederatedAuth: boolean;
fpAccount: IAccount;
}
export interface IOverviewActions {
@ -42,30 +36,6 @@ export const OverviewLogic = kea<MakeLogicType<IOverviewValues, IOverviewActions
initializeOverview: () => null,
},
reducers: {
organization: [
{} as IOrganization,
{
setServerData: (_, { organization }) => organization,
},
],
isFederatedAuth: [
true,
{
setServerData: (_, { isFederatedAuth }) => isFederatedAuth,
},
],
fpAccount: [
{} as IAccount,
{
setServerData: (_, { fpAccount }) => fpAccount,
},
],
canCreateInvitations: [
false,
{
setServerData: (_, { canCreateInvitations }) => canCreateInvitations,
},
],
hasUsers: [
false,
{

View file

@ -12,6 +12,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import { EuiEmptyPrompt, EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { RecentActivity, RecentActivityItem } from './recent_activity';
@ -61,4 +62,19 @@ describe('RecentActivity', () => {
expect(wrapper.find('.activity--error__label')).toHaveLength(1);
expect(wrapper.find(EuiLink).prop('color')).toEqual('danger');
});
it('renders recent activity message for default org name', () => {
setMockValues({
organization: {
name: 'foo',
defaultOrgName: 'foo',
},
});
const wrapper = shallow(<RecentActivity />);
const emptyPrompt = wrapper.find(EuiEmptyPrompt).dive();
expect(emptyPrompt.find(FormattedMessage).prop('defaultMessage')).toEqual(
'Your organization has no recent activity'
);
});
});

View file

@ -17,6 +17,7 @@ import { sendTelemetry } from '../../../shared/telemetry';
import { KibanaContext, IKibanaContext } from '../../../index';
import { SOURCE_DETAILS_PATH, getContentSourcePath } from '../../routes';
import { AppLogic } from '../../app_logic';
import { OverviewLogic } from './overview_logic';
import './recent_activity.scss';
@ -32,8 +33,9 @@ export interface IFeedActivity {
export const RecentActivity: React.FC = () => {
const {
organization: { name, defaultOrgName },
activityFeed,
} = useValues(OverviewLogic);
} = useValues(AppLogic);
const { activityFeed } = useValues(OverviewLogic);
return (
<ContentSection