diff --git a/hermes-console-vue/src/components/theme-switch/ThemeSwitch.spec.ts b/hermes-console-vue/src/components/theme-switch/ThemeSwitch.spec.ts index 8e6fd72775..340a29dfe1 100644 --- a/hermes-console-vue/src/components/theme-switch/ThemeSwitch.spec.ts +++ b/hermes-console-vue/src/components/theme-switch/ThemeSwitch.spec.ts @@ -24,26 +24,26 @@ describe('ThemeSwitch', () => { ['dark', 'light'], ])( 'should change Vuetify theme on button click (initial theme: %s)', - (initialTheme: string, changedTheme: string) => { + async (initialTheme: string, changedTheme: string) => { // given const { getByRole } = render(ThemeSwitch, {}, vuetify); vuetify.theme.global.name.value = initialTheme; // when - fireEvent.click(getByRole('button')); + await fireEvent.click(getByRole('button')); // then expect(vuetify.theme.global.name.value).toBe(changedTheme); }, ); - it('should persist theme preference in local storage', () => { + it('should persist theme preference in local storage', async () => { // given const { getByRole } = render(ThemeSwitch, {}, vuetify); vuetify.theme.global.name.value = 'light'; // when - fireEvent.click(getByRole('button')); + await fireEvent.click(getByRole('button')); // then expect(localStorage.getItem('hermes-console-theme')).toBe('dark'); diff --git a/hermes-console-vue/src/dummy/groups.ts b/hermes-console-vue/src/dummy/groups.ts index 577022d305..e299c359b4 100644 --- a/hermes-console-vue/src/dummy/groups.ts +++ b/hermes-console-vue/src/dummy/groups.ts @@ -1,3 +1,5 @@ +import type { Group } from '@/composables/use-groups/useGroups'; + export const dummyGroupNames = [ 'pl.allegro.public.offer', 'pl.allegro.public.offer.product', @@ -6,3 +8,18 @@ export const dummyGroupNames = [ 'pl.allegro.public.group', 'pl.allegro.public.admin', ]; + +export const dummyGroups: Group[] = [ + { + name: 'pl.allegro.offer', + topics: ['pl.allegro.offer.OfferEventV1'], + }, + { + name: 'pl.allegro.offer.product', + topics: [ + 'pl.allegro.offer.product.ProductEventV1', + 'pl.allegro.offer.product.ProductEventV2', + ], + }, + ...dummyGroupNames.slice(2).map((name) => ({ name, topics: [] })), +]; diff --git a/hermes-console-vue/src/i18n/en-US.ts b/hermes-console-vue/src/i18n/en-US.ts index 3a30ff4fdc..81f28d6f18 100644 --- a/hermes-console-vue/src/i18n/en-US.ts +++ b/hermes-console-vue/src/i18n/en-US.ts @@ -30,7 +30,7 @@ const en_US = { index: '#', name: 'Group name', noGroups: 'No groups found', - topicsChip: 'topics: {topicsAmount}', + topicsChip: 'topics: ', appliedFilter: '(applied filter: “{filter}”)', }, heading: 'Groups', diff --git a/hermes-console-vue/src/views/group-topics/group-topics-breadcrumbs/GroupTopicsBreadcrumbs.spec.ts b/hermes-console-vue/src/views/group-topics/group-topics-breadcrumbs/GroupTopicsBreadcrumbs.spec.ts new file mode 100644 index 0000000000..4a2dd25b03 --- /dev/null +++ b/hermes-console-vue/src/views/group-topics/group-topics-breadcrumbs/GroupTopicsBreadcrumbs.spec.ts @@ -0,0 +1,30 @@ +import { render } from '@/utils/test-utils'; +import GroupTopicsBreadcrumbs from '@/views/group-topics/group-topics-breadcrumbs/GroupTopicsBreadcrumbs.vue'; + +describe('GroupTopicsBreadcrumbs', () => { + it('should render `home` breadcrumb with an anchor to a home page', () => { + // given + const { getByText } = render(GroupTopicsBreadcrumbs); + + // when + const element = getByText( + 'groupTopics.groupTopicsBreadcrumbs.home', + ) as HTMLAnchorElement; + + // then + expect(element).toHaveAttribute('href', '/'); + }); + + it('should render `group` breadcrumb with anchor to groups listing', () => { + // given + const { getByText } = render(GroupTopicsBreadcrumbs); + + // when + const element = getByText( + 'groupTopics.groupTopicsBreadcrumbs.groups', + ) as HTMLAnchorElement; + + // then + expect(element).toHaveAttribute('href', '/groups/'); + }); +}); diff --git a/hermes-console-vue/src/views/group-topics/group-topics-breadcrumbs/GroupTopicsBreadcrumbs.vue b/hermes-console-vue/src/views/group-topics/group-topics-breadcrumbs/GroupTopicsBreadcrumbs.vue index 618e48124b..c773aa6d25 100644 --- a/hermes-console-vue/src/views/group-topics/group-topics-breadcrumbs/GroupTopicsBreadcrumbs.vue +++ b/hermes-console-vue/src/views/group-topics/group-topics-breadcrumbs/GroupTopicsBreadcrumbs.vue @@ -14,11 +14,11 @@ }, { title: t('groupTopics.groupTopicsBreadcrumbs.groups'), - href: '/groups', + href: '/groups/', }, { title: props.groupId, - href: `/groups/${props.groupId}`, + href: `/groups/${props.groupId}/`, }, ]; diff --git a/hermes-console-vue/src/views/groups/GroupsView.spec.ts b/hermes-console-vue/src/views/groups/GroupsView.spec.ts new file mode 100644 index 0000000000..a56816e65a --- /dev/null +++ b/hermes-console-vue/src/views/groups/GroupsView.spec.ts @@ -0,0 +1,27 @@ +import { fireEvent, waitFor } from '@testing-library/vue'; +import { render } from '@/utils/test-utils'; +import GroupsView from '@/views/groups/GroupsView.vue'; + +describe('GroupsView', () => { + it('should render', () => { + // when + const { queryByText } = render(GroupsView); + + // then + expect(queryByText('groups.heading')).toBeInTheDocument(); + expect(queryByText('groups.groupForm.createTitle')).not.toBeInTheDocument(); + }); + + it('should open modal on `new group` button click', async () => { + // given + const { queryByText } = render(GroupsView); + + // when + await fireEvent.click(queryByText('groups.actions.create')!); + + // then + await waitFor(() => { + expect(queryByText('groups.groupForm.createTitle')).toBeInTheDocument(); + }); + }); +}); diff --git a/hermes-console-vue/src/views/groups/group-form/GroupForm.spec.ts b/hermes-console-vue/src/views/groups/group-form/GroupForm.spec.ts new file mode 100644 index 0000000000..8310f4c0aa --- /dev/null +++ b/hermes-console-vue/src/views/groups/group-form/GroupForm.spec.ts @@ -0,0 +1,92 @@ +import { fireEvent, waitFor } from '@testing-library/vue'; +import { render } from '@/utils/test-utils'; +import GroupForm from '@/views/groups/group-form/GroupForm.vue'; + +describe('GroupForm', () => { + it('should render form (create variant)', () => { + // given + const props = { + dialogOpen: true, + operation: 'create', + }; + + // when + const { queryByText } = render(GroupForm, { props }); + + // then + expect(queryByText('groups.groupForm.createTitle')).toBeInTheDocument(); + expect(queryByText('groups.groupForm.edu')).toBeInTheDocument(); + }); + + it('should render form (edit variant)', () => { + // given + const props = { + dialogOpen: true, + operation: 'edit', + }; + + // when + const { queryByText } = render(GroupForm, { props }); + + // then + expect(queryByText('groups.groupForm.editTitle')).toBeInTheDocument(); + expect(queryByText('groups.groupForm.edu')).not.toBeInTheDocument(); + }); + + it('should not show name validation error for untouched form', () => { + // given + const props = { + dialogOpen: true, + operation: 'create', + }; + + // when + const { queryByText } = render(GroupForm, { props }); + + // then + expect( + queryByText('groups.groupForm.validation.groupName'), + ).not.toBeInTheDocument(); + }); + + it('should show name validation error after invalid form submit', async () => { + // given + const props = { + dialogOpen: true, + operation: 'create', + }; + + // when + const { getByText } = render(GroupForm, { props }); + await fireEvent.click(getByText('groups.groupForm.save')); + + // then + await waitFor(() => { + expect( + getByText('groups.groupForm.validation.groupName'), + ).toBeInTheDocument(); + }); + }); + + it('should not show name validation error for valid group name', async () => { + // given + const props = { + dialogOpen: true, + operation: 'create', + }; + + // when + const { getByLabelText, queryByText } = render(GroupForm, { props }); + + await fireEvent.update( + getByLabelText('groups.groupForm.groupName'), + 'pl.allegro.sample', + ); + await fireEvent.click(queryByText('groups.groupForm.save')!); + + // then + expect( + queryByText('groups.groupForm.validation.groupName'), + ).not.toBeInTheDocument(); + }); +}); diff --git a/hermes-console-vue/src/views/groups/group-listing/GroupListing.spec.ts b/hermes-console-vue/src/views/groups/group-listing/GroupListing.spec.ts new file mode 100644 index 0000000000..89eaa936a9 --- /dev/null +++ b/hermes-console-vue/src/views/groups/group-listing/GroupListing.spec.ts @@ -0,0 +1,81 @@ +import { dummyGroups } from '@/dummy/groups'; +import { render } from '@/utils/test-utils'; +import { within } from '@testing-library/vue'; +import GroupListing from '@/views/groups/group-listing/GroupListing.vue'; + +describe('GroupListing', () => { + it('should render group listing with no filter applied', () => { + // given + const props = { + groups: dummyGroups, + }; + + // when + const { getByText } = render(GroupListing, { props }); + + // then + dummyGroups.forEach(({ name, topics }, index) => { + const groupRow = getByText(name).closest('tr')!; + expect(within(groupRow).getByText(`${index + 1}`)).toBeInTheDocument(); + expect(within(groupRow).getByText(name)).toBeInTheDocument(); + expect( + within(groupRow).getByText( + `groups.groupListing.topicsChip ${topics.length}`, + ), + ).toBeInTheDocument(); + }); + }); + + it('should render group listing with a filter applied', () => { + // given + const props = { + groups: dummyGroups, + filter: 'pl.allegro.offer', + }; + + // when + const groups = render(GroupListing, { props }) + .getAllByText(/pl\.allegro\.offer/) + .map((group) => group.closest('tr')); + + // then + expect(groups).toHaveLength(2); + expect(within(groups[0]!).getByText(/topicsChip 1/)).toBeInTheDocument(); + expect(within(groups[1]!).getByText(/topicsChip 2/)).toBeInTheDocument(); + }); + + it('should render group listing with a filter applied (no results)', () => { + // given + const props = { + groups: dummyGroups, + filter: 'pl.allegro.i18n.DummyEventV1', + }; + + // when + const rows = render(GroupListing, { props }).getAllByRole('row'); + + // then + expect(rows).toHaveLength(2); + expect(within(rows[0]!).getByText(/index/)).toBeInTheDocument(); + expect(within(rows[0]!).getByText(/name/)).toBeInTheDocument(); + expect(within(rows[1]!).getByText(/noGroups/)).toBeInTheDocument(); + expect(within(rows[1]!).getByText(/appliedFilter/)).toBeInTheDocument(); + }); + + it('should render an empty group listing without filter applied', () => { + // given + const props = { + groups: [], + }; + + // when + const rows = render(GroupListing, { props }).getAllByRole('row'); + + // then + expect(rows).toHaveLength(2); + expect(within(rows[1]!).getByText(/noGroups/)).toBeInTheDocument(); + expect( + within(rows[1]!).queryByText(/appliedFilter/), + ).not.toBeInTheDocument(); + }); +}); diff --git a/hermes-console-vue/src/views/groups/group-listing/GroupListing.vue b/hermes-console-vue/src/views/groups/group-listing/GroupListing.vue index 8bf712eae0..a1575c199b 100644 --- a/hermes-console-vue/src/views/groups/group-listing/GroupListing.vue +++ b/hermes-console-vue/src/views/groups/group-listing/GroupListing.vue @@ -53,11 +53,8 @@ density="comfortable" variant="flat" > - {{ - t('groups.groupListing.topicsChip', { - topicsAmount: group.topics.length, - }) - }} + {{ t('groups.groupListing.topicsChip') }} + {{ group.topics.length }}