Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Template flows #2975

Merged
merged 28 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0478c41
added template flows in drop down
Jul 4, 2024
a31fea3
added support for viewing template flow
akanshaaa19 Jul 8, 2024
b516ed5
create options
akanshaaa19 Jul 11, 2024
bc09942
fixed mocks
akanshaaa19 Jul 11, 2024
7a3b7e6
Merge branch 'master' of github.com:glific/glific-frontend into featu…
akanshaaa19 Jul 11, 2024
19aabbd
fixed formik submit func
akanshaaa19 Jul 11, 2024
30824f6
fixed test cases
akanshaaa19 Jul 11, 2024
f7e2506
added test cases
akanshaaa19 Jul 12, 2024
8ef9ac8
minor refactoring
akanshaaa19 Jul 12, 2024
6088331
minor refactoring
akanshaaa19 Jul 12, 2024
643390d
updated mocks
akanshaaa19 Jul 12, 2024
c87ac51
disabled buttons
akanshaaa19 Jul 12, 2024
5ffa54d
updated mocks
akanshaaa19 Jul 12, 2024
c5ed917
updated branch for cypress test cases
akanshaaa19 Jul 15, 2024
a949b7e
Merge branch 'master' into feature/template-flows
akanshaaa19 Jul 15, 2024
affb611
testing feedbacks
akanshaaa19 Jul 16, 2024
f316d0e
Merge branch 'master' of github.com:glific/glific-frontend into featu…
akanshaaa19 Jul 16, 2024
3882cea
Merge branch 'feature/template-flows' of github.com:glific/glific-fro…
akanshaaa19 Jul 16, 2024
3086d49
resolved comments
akanshaaa19 Jul 16, 2024
2e7411e
minor refactoring
akanshaaa19 Jul 17, 2024
b7e80a4
minor refactoring
akanshaaa19 Jul 17, 2024
bca2866
updated test cases
akanshaaa19 Jul 17, 2024
ebae958
fixed a small bug
akanshaaa19 Jul 18, 2024
908f3b5
Merge branch 'master' of github.com:glific/glific-frontend into featu…
akanshaaa19 Jul 18, 2024
f7d725b
round 2 feedbacks
akanshaaa19 Jul 19, 2024
7241d5e
Merge branch 'master' of github.com:glific/glific-frontend into featu…
akanshaaa19 Jul 22, 2024
f174b91
fixed failing test case
akanshaaa19 Jul 23, 2024
2a6e084
Merge branch 'master' of github.com:glific/glific-frontend into featu…
akanshaaa19 Jul 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cypress-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
git clone https://github.com/glific/cypress-testing.git
echo done. go to dir.
cd cypress-testing
git checkout main
git checkout feature/template-flows
cd ..
cp -r cypress-testing/cypress cypress
yarn add cypress@13.6.2
Expand Down
15 changes: 15 additions & 0 deletions src/assets/images/icons/ViewLight.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/components/floweditor/FlowEditor.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import '@nyaruka/temba-components/dist/temba-components.js';

const glificBase = FLOW_EDITOR_API;

export const setConfig = (uuid: any) => {
export const setConfig = (uuid: any, isTemplate: boolean) => {
const services = JSON.parse(localStorage.getItem('organizationServices') || '{}');

const config = {
flow: uuid,
flowType: 'messaging',
localStorage: true,
mutable: true,
mutable: !isTemplate,
showNodeLabel: false,
attachmentsEnabled: false,
filters: ['whatsapp', 'classifier', 'profile', 'optins', 'ticketer'],
Expand Down
11 changes: 8 additions & 3 deletions src/components/floweditor/FlowEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { useMutation, useLazyQuery, useQuery } from '@apollo/client';
import { Navigate, useNavigate, useParams } from 'react-router-dom';
import { Navigate, useLocation, useNavigate, useParams } from 'react-router-dom';
import { Menu, MenuItem, Typography } from '@mui/material';
import BackIconFlow from 'assets/images/icons/BackIconFlow.svg?react';
import WarningIcon from 'assets/images/icons/Warning.svg?react';
Expand Down Expand Up @@ -31,11 +31,13 @@ export const FlowEditor = () => {
const params = useParams();
const { uuid } = params;
const navigate = useNavigate();
const location = useLocation();
const [publishDialog, setPublishDialog] = useState(false);
const [loading, setLoading] = useState(true);
const [flowEditorLoaded, setFlowEditorLoaded] = useState(false);
const [flowId, setFlowId] = useState();
const config = setConfig(uuid);
const isTemplate = location?.state === 'template';
const config = setConfig(uuid, isTemplate);
const [published, setPublished] = useState(false);
const [showSimulator, setShowSimulator] = useState(false);
const [stayOnPublish, setStayOnPublish] = useState(false);
Expand Down Expand Up @@ -350,7 +352,7 @@ export const FlowEditor = () => {
<div className={styles.Header}>
<div className={styles.Title}>
<BackIconFlow
onClick={() => navigate('/flow')}
onClick={() => (isTemplate ? navigate('/flow?isTemplate=true') : navigate('/flow'))}
className={styles.BackIcon}
data-testid="back-button"
/>
Expand Down Expand Up @@ -402,6 +404,7 @@ export const FlowEditor = () => {
handleClose();
}}
disableRipple
disabled={isTemplate}
>
Reset flow count
</MenuItem>
Expand All @@ -411,6 +414,7 @@ export const FlowEditor = () => {
variant="outlined"
color="primary"
data-testid="previewButton"
disabled={isTemplate}
onClick={() => {
setShowTranslateFlowModal(true);
handleClose();
Expand All @@ -433,6 +437,7 @@ export const FlowEditor = () => {
variant="contained"
color="primary"
data-testid="button"
disabled={isTemplate}
onClick={() => setPublishDialog(true)}
>
<PublishIcon className={styles.Icon} />
Expand Down
94 changes: 92 additions & 2 deletions src/containers/Flow/Flow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,34 @@ import {
createFlowQuery,
createTagQuery,
updateFlowQueryWithError,
getFlowCountQuery,
releaseFlow,
} from 'mocks/Flow';
import { Flow } from './Flow';
import { setOrganizationServices } from 'services/AuthService';
import { getFilterTagQuery } from 'mocks/Tag';
import { getRoleNameQuery } from 'mocks/Role';
import userEvent from '@testing-library/user-event';
import { setErrorMessage, setNotification } from 'common/notification';
import FlowList from './FlowList/FlowList';

setOrganizationServices('{"__typename":"OrganizationServicesResult","rolesAndPermission":true}');

const mocks = [
...getOrganizationQuery,
getFlowQuery({ id: 1 }),
getFlowQuery({ id: '1' }),
filterFlowQuery,
filterFlowQuery({ isActive: true, isTemplate: false }),
getFilterTagQuery,
getRoleNameQuery,
getOrganizationLanguagesQuery,
copyFlowQuery,
copyFlowQuery(),
copyFlowQuery(true),
createFlowQuery,
createTagQuery,
getFlowCountQuery({ isActive: true, isTemplate: false }),
releaseFlow,
getFilterTagQuery,
];

const mockUseLocationValue: any = {
Expand All @@ -41,11 +48,14 @@ const mockUseLocationValue: any = {
hash: '',
state: null,
};
const mockedUsedNavigate = vi.fn();

vi.mock('react-router-dom', async () => ({
...((await vi.importActual<any>('react-router-dom')) as {}),
useLocation: () => {
return mockUseLocationValue;
},
useNavigate: () => mockedUsedNavigate,
}));

vi.mock('common/notification', async (importOriginal) => {
Expand Down Expand Up @@ -155,6 +165,7 @@ it('should edit the flow', async () => {
<MockedProvider mocks={[...mocks, updateFlowQuery]} addTypename={false}>
<MemoryRouter initialEntries={[`/flow/1/edit`]}>
<Routes>
<Route path="flow" element={<FlowList />} />
<Route path="flow/:id/edit" element={<Flow />} />
</Routes>
</MemoryRouter>
Expand All @@ -180,6 +191,32 @@ it('should edit the flow', async () => {
});
});

it('should configure the flow', async () => {
const editFlow = () => (
<MockedProvider mocks={[...mocks, updateFlowQuery]} addTypename={false}>
<MemoryRouter initialEntries={[`/flow/1/edit`]}>
<Routes>
<Route path="flow" element={<FlowList />} />
<Route path="flow/:id/edit" element={<Flow />} />
</Routes>
</MemoryRouter>
</MockedProvider>
);
const { getByText } = render(editFlow());

expect(getByText('Loading...')).toBeInTheDocument();

await waitFor(() => {
expect(getByText('Edit flow')).toBeInTheDocument();
});

fireEvent.click(screen.getByText('Configure'));

await waitFor(() => {
expect(setNotification).toHaveBeenCalled();
});
});

it('should edit the flow and show error if exists', async () => {
const editFlow = () => (
<MockedProvider mocks={[...mocks, updateFlowQueryWithError]} addTypename={false}>
Expand Down Expand Up @@ -236,3 +273,56 @@ it('should create copy of flow', async () => {
fireEvent.click(button);
await waitFor(() => {});
});

it('buttons should be disabled in template state', async () => {
mockUseLocationValue.state = 'template';

render(
<MockedProvider mocks={mocks} addTypename={false}>
<MemoryRouter initialEntries={[`/flow/1/edit`]}>
<Routes>
<Route path="flow/:id/edit" element={<Flow />} />
</Routes>
</MemoryRouter>
</MockedProvider>
);

await waitFor(() => {
expect(screen.getByText('Template Flow')).toBeInTheDocument();
});

await waitFor(() => {
expect(screen.getByTestId('submitActionButton')).toBeDisabled();
expect(screen.getByTestId('remove-icon')).toBeDisabled();
});

fireEvent.click(screen.getByText('View'));

await waitFor(() => {
expect(mockedUsedNavigate).toHaveBeenCalled();
});
});

it('should create copy of a template flow', async () => {
mockUseLocationValue.state = 'copyTemplate';

const copyFlow = () => (
<MockedProvider mocks={mocks} addTypename={false}>
<MemoryRouter initialEntries={[`/flow/1/edit`]}>
<Routes>
<Route path="flow/:id/edit" element={<Flow />} />
</Routes>
</MemoryRouter>
</MockedProvider>
);

const { container, getByTestId } = render(copyFlow());
await waitFor(() => {
const inputElement = container.querySelector('input[name="name"]') as HTMLInputElement;
expect(inputElement?.value).toBe('Copy of Help');
});

const button = getByTestId('submitActionButton');
fireEvent.click(button);
await waitFor(() => {});
});
Loading
Loading