Skip to content

Commit

Permalink
test(Templates): Adding Tests for Templates Designer / Card / State (#…
Browse files Browse the repository at this point in the history
…4782)

* modified templates state according to tests & fixed params,conns type

* commiting rest of the files

* set up initial tests

* divided tests into templateDesigner and templateCard

* removed empty white space
  • Loading branch information
Elaina-Lee committed May 6, 2024
1 parent 8eaf2e1 commit 453acae
Show file tree
Hide file tree
Showing 11 changed files with 283 additions and 33 deletions.
34 changes: 34 additions & 0 deletions libs/designer/src/lib/__test__/template-test-utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { PropsWithChildren } from 'react';
import { render } from '@testing-library/react';
import type { RenderOptions } from '@testing-library/react';
import { Provider as ReduxProvider } from 'react-redux';
import type { AppStore, RootState } from '../core/state/templates/store';
import { setupStore } from '../core/state/templates/store';
import { TemplatesWrappedContext } from '../core/templates/TemplatesDesignerContext';
import { TemplatesDataProvider } from '../core/templates/TemplatesDataProvider';

// As a basic setup, import your same slice reducers

// This type interface extends the default options for render from RTL, as well
// as allows the user to specify other things such as initialState, templateStore.
interface ExtendedRenderOptions extends Omit<RenderOptions, 'queries'> {
preloadedState?: Partial<RootState>;
store?: AppStore;
}

export function renderWithProviders(ui: React.ReactElement, extendedRenderOptions: ExtendedRenderOptions = {}) {
const {
preloadedState = {},
// Automatically create a templateStore instance if no templateStore was passed in
store = setupStore(preloadedState),
...renderOptions
} = extendedRenderOptions;

const Wrapper = ({ children }: PropsWithChildren) => <ReduxProvider store={store}>{children}</ReduxProvider>;

// Return an object with the templateStore and all of RTL's query functions
return {
store,
...render(ui, { wrapper: Wrapper, ...renderOptions }),
};
}
25 changes: 16 additions & 9 deletions libs/designer/src/lib/core/state/templates/store.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import { configureStore } from '@reduxjs/toolkit';
import { combineReducers, configureStore } from '@reduxjs/toolkit';
import type {} from 'redux-thunk';
import { templateSlice } from './templateSlice';
import { manifestSlice } from './manifestSlice';
import { panelSlice } from './panelSlice';

export const templateStore = configureStore({
reducer: {
template: templateSlice.reducer,
manifest: manifestSlice.reducer,
panel: panelSlice.reducer,
},
const rootReducer = combineReducers({
template: templateSlice.reducer,
manifest: manifestSlice.reducer,
panel: panelSlice.reducer,
});

export const setupStore = (preloadedState?: Partial<RootState>) => {
return configureStore({
reducer: rootReducer,
preloadedState,
});
};

export const templateStore = setupStore();
export const templatesPathFromState = '../../templates/samples';
export type RootState = ReturnType<typeof templateStore.getState>;
export type AppDispatch = typeof templateStore.dispatch;
export type RootState = ReturnType<typeof rootReducer>;
export type AppStore = ReturnType<typeof setupStore>;
export type AppDispatch = AppStore['dispatch'];
31 changes: 19 additions & 12 deletions libs/designer/src/lib/core/state/templates/templateSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ import { templatesPathFromState, type RootState } from './store';

export interface TemplateState {
templateName?: string;
workflowDefinition?: LogicAppsV2.WorkflowDefinition;
manifest?: Template.Manifest;
connections?: Record<string, Template.Connection>;
parameters?: Record<string, any>;
workflowDefinition: LogicAppsV2.WorkflowDefinition | undefined;
manifest: Template.Manifest | undefined;
parameters: Template.Parameter[];
connections: Template.Connection[];
}

const initialState: TemplateState = {};
const initialState: TemplateState = {
workflowDefinition: undefined,
manifest: undefined,
parameters: [],
connections: [],
};

export const loadTemplate = createAsyncThunk(
'template/loadTemplate',
Expand All @@ -37,18 +42,20 @@ export const templateSlice = createSlice({
},
extraReducers: (builder) => {
builder.addCase(loadTemplate.fulfilled, (state, action) => {
state.workflowDefinition = action.payload?.workflowDefinition;
state.manifest = action.payload?.manifest;
state.connections = action.payload?.connections;
state.parameters = action.payload?.parameters;
if (action.payload) {
state.workflowDefinition = action.payload.workflowDefinition;
state.manifest = action.payload.manifest;
state.parameters = action.payload.parameters;
state.connections = action.payload.connections;
}
});

builder.addCase(loadTemplate.rejected, (state) => {
// TODO change to null for error handling case
state.workflowDefinition = undefined;
state.manifest = undefined;
state.connections = undefined;
state.parameters = undefined;
state.parameters = [];
state.connections = [];
});
},
});
Expand All @@ -70,8 +77,8 @@ const loadTemplateFromGithub = async (
return {
workflowDefinition: (templateWorkflowDefinition as any)?.default ?? templateWorkflowDefinition,
manifest: templateManifest,
connections: templateManifest.connections,
parameters: templateManifest.parameters,
connections: templateManifest.connections,
};
} catch (ex) {
console.error(ex);
Expand Down
2 changes: 1 addition & 1 deletion libs/designer/src/lib/ui/templates/TemplatesDesigner.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { RootState } from '../../core/state/templates/store';
import { useSelector } from 'react-redux';
import { TemplateCard } from './templateCard';
import { TemplateCard } from './cards/templateCard';
import { TemplatePanel } from '../panel/templatePanel/templatePanel';

export const TemplatesDesigner = () => {
Expand Down
110 changes: 110 additions & 0 deletions libs/designer/src/lib/ui/templates/__tests__/templateCard.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { describe, beforeAll, expect, it } from 'vitest';
import type { AppStore } from '../../../core/state/templates/store';
import { setupStore, type RootState } from '../../../core/state/templates/store';
import type { Template } from '@microsoft/logic-apps-shared';
import { renderWithProviders } from '../../../__test__/template-test-utils';
import { screen } from '@testing-library/react';
import { TemplateCard } from '../cards/templateCard';

describe('ui/templates/templatesDesigner', () => {
let store: AppStore;
let minimalStoreData: Partial<RootState>;
let template1Manifest: Template.Manifest;
let template2Manifest: Template.Manifest;

beforeAll(() => {
template1Manifest = {
title: 'Template 1',
description: 'Template 1 Description',
skus: ['standard'],
kinds: ['stateful'],
artifacts: [
{
type: 'workflow',
file: 'workflow.json',
},
{
type: 'description',
file: 'description.md',
},
],
connections: [
{
id: 'connection example 1',
},
{
id: 'connection example 2',
},
],
parameters: [],
};
template2Manifest = {
title: 'Template 2',
description: 'Template 2 Description',
skus: ['standard', 'consumption'],
kinds: ['stateful', 'stateless'],
artifacts: [
{
type: 'workflow',
file: 'workflow.json',
},
{
type: 'description',
file: 'description.md',
},
],
connections: [
{
id: 'connection example 1',
},
],
parameters: [],
};
});

it('Renders TemplateCard and loads template state correctly on buttons click', async () => {
minimalStoreData = {
manifest: {
availableTemplateNames: ['template1', 'template2'],
availableTemplates: {
template1: template1Manifest,
template2: template2Manifest,
},
},
};
store = setupStore(minimalStoreData);

renderWithProviders(<TemplateCard templateName="template1" />, { store });

expect(screen.getByText('Create Workflow')).toBeDefined();
expect(screen.getByText('Quick View')).toBeDefined();
screen.getByText('Create Workflow').click();
expect(store.getState().template.templateName).toBe('template1');
});

it('Renders TemplateCard and Opens the right panel', async () => {
minimalStoreData = {
manifest: {
availableTemplateNames: ['template1', 'template2'],
availableTemplates: {
template1: template1Manifest,
template2: template2Manifest,
},
},
};
store = setupStore(minimalStoreData);

renderWithProviders(<TemplateCard templateName="template2" />, { store });

expect(screen.getByText('Create Workflow')).toBeDefined();
expect(screen.getByText('Quick View')).toBeDefined();
screen.getByText('Create Workflow').click();
expect(store.getState().panel.isOpen).toBe(true);
expect(store.getState().panel.currentPanelView).toBe('createWorkflow');
store.dispatch({ type: 'panel/closePanel' });
expect(store.getState().panel.isOpen).toBe(false);
screen.getByText('Quick View').click();
expect(store.getState().panel.isOpen).toBe(true);
expect(store.getState().panel.currentPanelView).toBe('quickView');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { describe, beforeAll, expect, it } from 'vitest';
import type { AppStore } from '../../../core/state/templates/store';
import { setupStore, type RootState } from '../../../core/state/templates/store';
import type { Template } from '@microsoft/logic-apps-shared';
import { renderWithProviders } from '../../../__test__/template-test-utils';
import { TemplatesDesigner } from '../TemplatesDesigner';
import { screen } from '@testing-library/react';

describe('ui/templates/templatesDesigner', () => {
let store: AppStore;
let minimalStoreData: Partial<RootState>;
let template1Manifest: Template.Manifest;
let template2Manifest: Template.Manifest;

beforeAll(() => {
template1Manifest = {
title: 'Template 1',
description: 'Template 1 Description',
skus: ['standard'],
kinds: ['stateful'],
artifacts: [
{
type: 'workflow',
file: 'workflow.json',
},
{
type: 'description',
file: 'description.md',
},
],
connections: [
{
id: 'connection example 1',
},
{
id: 'connection example 2',
},
],
parameters: [],
};
template2Manifest = {
title: 'Template 2',
description: 'Template 2 Description',
skus: ['standard', 'consumption'],
kinds: ['stateful', 'stateless'],
artifacts: [
{
type: 'workflow',
file: 'workflow.json',
},
{
type: 'description',
file: 'description.md',
},
],
connections: [
{
id: 'connection example 1',
},
],
parameters: [],
};
});

it('Fetches templates and display the title and description', async () => {
minimalStoreData = {
manifest: {
availableTemplateNames: ['template1', 'template2'],
availableTemplates: {
template1: template1Manifest,
template2: template2Manifest,
},
},
};
store = setupStore(minimalStoreData);

renderWithProviders(<TemplatesDesigner />, { store });

expect(screen.getByText(/Template 1/i)).toBeDefined();
expect(screen.getByText(/Template 1 Description/i)).toBeDefined();
expect(screen.getByText(/Template 2/i)).toBeDefined();
expect(screen.getByText(/Template 2 Description/i)).toBeDefined();
});
});
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { AppDispatch, RootState } from '../../core/state/templates/store';
import { changeCurrentTemplateName, loadTemplate } from '../../core/state/templates/templateSlice';
import type { AppDispatch, RootState } from '../../../core/state/templates/store';
import { changeCurrentTemplateName, loadTemplate } from '../../../core/state/templates/templateSlice';
import { useDispatch, useSelector } from 'react-redux';
import { Button } from '@fluentui/react-components';
import { openCreateWorkflowPanelView, openQuickViewPanelView } from '../../core/state/templates/panelSlice';
import { openCreateWorkflowPanelView, openQuickViewPanelView } from '../../../core/state/templates/panelSlice';
import { useIntl } from 'react-intl';

interface TemplateCardProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import { Button } from '@fluentui/react-components';
import type { Template } from '@microsoft/logic-apps-shared';

export interface DisplayConnectionsProps {
connections: Record<string, Template.Connection>;
connections: Template.Connection[];
}

export const DisplayConnections = ({ connections }: DisplayConnectionsProps) => {
return (
<>
<div>Template Connections</div>
{Object.keys(connections).map((connection, index) => {
{connections.map((connection, index) => {
return (
<div key={index}>
<b>
{index + 1}: {connection}{' '}
{index + 1}: {connection?.id}
<Button
appearance="outline"
onClick={() => {
Expand All @@ -23,7 +23,7 @@ export const DisplayConnections = ({ connections }: DisplayConnectionsProps) =>
CHOOSE
</Button>
</b>
<div>- ID: {connections[connection]?.id}</div>
<div>- ID: {connection?.id}</div>
</div>
);
})}
Expand Down
2 changes: 1 addition & 1 deletion libs/designer/src/lib/ui/templates/styles.less
Original file line number Diff line number Diff line change
@@ -1 +1 @@
@import './templateCard.less';
@import './cards/templateCard.less';
Loading

0 comments on commit 453acae

Please sign in to comment.