Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
6a257db
refactor: replace deprecated Apollo onCompleted/onError callbacks wit…
akanshaaa19 Mar 17, 2026
750725f
refactor: update mutation handling to use async/await for better erro…
akanshaaa19 Mar 17, 2026
784ce37
Merge branch 'master' into apollo_v4/batch_1
akanshaaa19 Mar 18, 2026
29851a6
refactor: remove deprecated tier state and update to use qualityRatin…
akanshaaa19 Mar 18, 2026
9b5cc51
refactor: enhance error handling and notifications in Flow, HSMList, …
akanshaaa19 Mar 18, 2026
8c98323
refactor: improve error handling in FormLayout; update notification m…
akanshaaa19 Mar 18, 2026
eddb8a7
refactor: update notification message handling in FormLayout; rename …
akanshaaa19 Mar 18, 2026
f55d9fd
refactor: enhance loading state handling in FormLayout; clean up HSML…
akanshaaa19 Mar 18, 2026
98d1d6f
refactor: improve loading state handling in FormLayout; remove unused…
akanshaaa19 Mar 18, 2026
1723d58
refactor: streamline FormLayout logic; remove redundant variable decl…
akanshaaa19 Mar 18, 2026
1d0c1c6
refactor: enhance FlowList tests; add error handling for import, expo…
akanshaaa19 Mar 18, 2026
b5c2003
refactor: enhance saveHandler and related functions to support isSave…
akanshaaa19 Mar 23, 2026
d958b49
Merge branch 'master' of github.com:glific/glific-frontend into apoll…
akanshaaa19 Mar 23, 2026
61b304f
chore: update cypress testing workflow to checkout apollo_migration b…
akanshaaa19 Mar 23, 2026
970defa
refactor: extract languageId logic into a separate function for impro…
akanshaaa19 Mar 23, 2026
bd72a64
Merge branch 'master' into apollo_v4/batch_1
akanshaaa19 Mar 26, 2026
4709abd
Merge branch 'master' of github.com:glific/glific-frontend into apoll…
akanshaaa19 Apr 6, 2026
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
3 changes: 2 additions & 1 deletion .github/workflows/cypress-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ jobs:
git clone https://github.com/glific/cypress-testing.git
echo done. go to dir.
cd cypress-testing
git checkout apollo_migration
cd ..
cp -r cypress-testing/cypress cypress
yarn add [email protected]
Expand Down Expand Up @@ -135,4 +136,4 @@ jobs:
name: phoenix-server-log-shard-${{ matrix.shard }}
path: ${{ env.artifacts_path }}
if-no-files-found: warn
retention-days: 7
retention-days: 7
8 changes: 3 additions & 5 deletions src/components/UI/Layout/Navigation/SideMenus/SideMenus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,17 @@ const SideMenus = ({ opened }: SideMenusProps) => {
const { t } = useTranslation();

// handle count for notifictions
const [notificationCount, setNotificationCount] = useState(0);
const [getNotificationCount] = useLazyQuery(GET_NOTIFICATIONS_COUNT, {
const [getNotificationCount, { data: notificationData }] = useLazyQuery(GET_NOTIFICATIONS_COUNT, {
variables: {
filter: {
is_read: false,
},
},
fetchPolicy: 'cache-and-network',
onCompleted: (countData) => {
setNotificationCount(countData.countNotifications);
},
});

const notificationCount = notificationData?.countNotifications ?? 0;

useEffect(() => {
getNotificationCount();
}, []);
Expand Down
130 changes: 109 additions & 21 deletions src/containers/Flow/FlowList/FlowList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import { Flow } from '../Flow';
import { getFilterTagQuery } from 'mocks/Tag';
import { getRoleNameQuery } from 'mocks/Role';
import * as Notification from 'common/notification';
import { IMPORT_FLOW } from 'graphql/mutations/Flow';
import { IMPORT_FLOW, PIN_FLOW } from 'graphql/mutations/Flow';
import { EXPORT_FLOW } from 'graphql/queries/Flow';

const isActiveFilter = { isActive: true, isTemplate: false };

Expand All @@ -42,21 +43,17 @@ const mocks = [
getFlowCountNewQuery,
getFlowQuery({ id: 1 }),
getFlowQuery({ id: '1' }),
importFlow,
releaseFlow,
exportFlow,
getFilterTagQuery,
getRoleNameQuery,
getFlowCountQuery({ isTemplate: true }),
filterTemplateFlows,
pinFlowQuery('2', true),
pinFlowQuery('1'),
...getOrganizationQuery,
];
const normalize = (s: string) => s.replace(/\s+/g, ' ').trim().toLowerCase();

const flowList = (
<MockedProvider mocks={mocks} addTypename={false}>
const flowList = (customMocks?: any[]) => (
<MockedProvider mocks={customMocks || mocks}>
<MemoryRouter>
<FlowList />
</MemoryRouter>
Expand All @@ -81,7 +78,7 @@ const notificationSpy = vi.spyOn(Notification, 'setNotification');

describe('<FlowList />', () => {
test('should render Flow', async () => {
const { getByText, getByTestId } = render(flowList);
const { getByText, getByTestId } = render(flowList());
expect(getByTestId('loading')).toBeInTheDocument();
await waitFor(() => {
expect(getByText('Flows'));
Expand All @@ -94,7 +91,7 @@ describe('<FlowList />', () => {
});

test('should search flow and check if flow keywords are present below the name', async () => {
const { getByText, getByTestId, queryByPlaceholderText } = render(flowList);
const { getByText, getByTestId, queryByPlaceholderText } = render(flowList());
await waitFor(() => {
// type "Help Workflow" in search box and enter
expect(getByTestId('searchInput')).toBeInTheDocument();
Expand All @@ -121,7 +118,7 @@ describe('<FlowList />', () => {
});

test('click on Make a copy', async () => {
const { getAllByTestId } = render(flowList);
const { getAllByTestId } = render(flowList());

await waitFor(() => {
expect(getAllByTestId('copy-icon')[0]).toBeInTheDocument();
Expand All @@ -135,7 +132,7 @@ describe('<FlowList />', () => {
});

test('should import flow using json file', async () => {
render(flowList);
render(flowList([...mocks, importFlow]));

await waitFor(() => {
expect(screen.getAllByTestId('import-icon')[0]).toBeInTheDocument();
Expand Down Expand Up @@ -167,7 +164,7 @@ describe('<FlowList />', () => {

test('should export flow to json file', async () => {
globalThis.URL.createObjectURL = vi.fn();
render(flowList);
render(flowList([...mocks, exportFlow]));

await waitFor(() => {
screen.getAllByTestId('MoreIcon');
Expand All @@ -183,7 +180,7 @@ describe('<FlowList />', () => {
});

test('should create from scratch ', async () => {
render(flowList);
render(flowList());

await waitFor(() => {
expect(screen.getByText('Flows')).toBeInTheDocument();
Expand All @@ -203,7 +200,7 @@ describe('<FlowList />', () => {
});

test('it should pin/unpin the flows', async () => {
render(flowList);
render(flowList([...mocks, pinFlowQuery('2', true), pinFlowQuery('1')]));

await waitFor(() => {
expect(screen.getByText('Flows')).toBeInTheDocument();
Expand All @@ -223,7 +220,7 @@ describe('<FlowList />', () => {
});

test('it should navigate to create page with selected tag', async () => {
render(flowList);
render(flowList());

await waitFor(() => {
expect(screen.getByText('Flows')).toBeInTheDocument();
Expand All @@ -247,7 +244,7 @@ describe('<FlowList />', () => {
});

test('should navigate to edit page on clicking the edit button', async () => {
render(flowList);
render(flowList());

await waitFor(() => {
expect(screen.getByText('Flows')).toBeInTheDocument();
Expand All @@ -267,7 +264,7 @@ describe('<FlowList />', () => {
});

test('should open responder link dialog on clicking the share button', async () => {
render(flowList);
render(flowList());

await waitFor(() => {
expect(screen.getByText('Flows')).toBeInTheDocument();
Expand All @@ -281,7 +278,7 @@ describe('<FlowList />', () => {
});

test('should show warning when no keywords are selected and share button is clicked', async () => {
render(flowList);
render(flowList());

await waitFor(() => {
expect(screen.getByText('Flows')).toBeInTheDocument();
Expand All @@ -297,7 +294,7 @@ describe('<FlowList />', () => {

describe('Template flows', () => {
test('it opens and closes dialog box', async () => {
render(flowList);
render(flowList());

await waitFor(() => {
expect(screen.getByText('Flows')).toBeInTheDocument();
Expand All @@ -322,7 +319,7 @@ describe('Template flows', () => {
});

test('it shows and creates a template flows', async () => {
render(flowList);
render(flowList());

await waitFor(() => {
expect(screen.getByText('Flows')).toBeInTheDocument();
Expand All @@ -348,7 +345,7 @@ describe('Template flows', () => {
});

test('click on Use it for templates', async () => {
render(flowList);
render(flowList());

await waitFor(() => {
expect(screen.getByText('Flows')).toBeInTheDocument();
Expand Down Expand Up @@ -457,3 +454,94 @@ describe('Template flows', () => {
});
});
});

describe('Error handling', () => {
test('should show error notification when export flow fails', async () => {
const exportFlowError = {
request: {
query: EXPORT_FLOW,
variables: { id: '1' },
},
error: new Error('Network error'),
};

render(flowList([...mocks, exportFlowError]));

await waitFor(() => {
screen.getAllByTestId('MoreIcon');
});

fireEvent.click(screen.getAllByTestId('MoreIcon')[0]);

await waitFor(() => {
expect(screen.getAllByTestId('export-icon')[0]).toBeInTheDocument();
});

fireEvent.click(screen.getAllByTestId('export-icon')[0]);

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

test('should show error notification when import flow fails', async () => {
const importFlowError = {
request: {
query: IMPORT_FLOW,
},
error: new Error('Import failed'),
variableMatcher: () => true,
};

class FileReaderMock {
onload: null | ((e: unknown) => void) = null;
result: string | null = null;
readAsText() {
const text = '{"flows":[]}';
this.result = text;
setTimeout(() => {
if (this.onload) {
this.onload({ target: { result: text } } as unknown as ProgressEvent<FileReader>);
}
}, 0);
}
}
vi.stubGlobal('FileReader', FileReaderMock);

render(flowList([...mocks, importFlowError]));

await screen.findAllByTestId('import-icon');
fireEvent.click(screen.getAllByTestId('import-icon')[0]);

const file = new File(['{}'], 'test.json', { type: 'application/json' });
const input = await screen.findByTestId('import');
Object.defineProperty(input, 'files', { value: [file] });
fireEvent.change(input);

await waitFor(() => {
expect(notificationSpy).toHaveBeenCalledWith('An error occured while importing the flow', 'warning');
});
});

test('should show error notification when pin flow fails', async () => {
const pinFlowError = {
request: {
query: PIN_FLOW,
variables: { updateFlowId: '2', input: { isPinned: true } },
},
error: new Error('Pin failed'),
};

render(flowList([...mocks, pinFlowError]));

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

fireEvent.click(screen.getAllByTestId('pin-button')[0]);

await waitFor(() => {
expect(notificationSpy).toHaveBeenCalledWith('Failed to update pin status', 'warning');
});
});
});
Loading
Loading