From 8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 17 Dec 2020 11:59:07 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-7-stable-ee --- spec/frontend/groups/components/app_spec.js | 2 +- spec/frontend/groups/components/group_item_spec.js | 49 +++++++++++++++ .../components/visibility_level_dropdown_spec.js | 73 ++++++++++++++++++++++ .../frontend/groups/members/components/app_spec.js | 30 +++++++-- spec/frontend/groups/members/index_spec.js | 32 ++++++++-- spec/frontend/groups/members/utils_spec.js | 2 + spec/frontend/groups/store/groups_store_spec.js | 27 +++++--- spec/frontend/groups/store/utils_spec.js | 44 +++++++++++++ 8 files changed, 237 insertions(+), 22 deletions(-) create mode 100644 spec/frontend/groups/components/visibility_level_dropdown_spec.js create mode 100644 spec/frontend/groups/store/utils_spec.js (limited to 'spec/frontend/groups') diff --git a/spec/frontend/groups/components/app_spec.js b/spec/frontend/groups/components/app_spec.js index 691f8896d74..72d8e23f28b 100644 --- a/spec/frontend/groups/components/app_spec.js +++ b/spec/frontend/groups/components/app_spec.js @@ -35,7 +35,7 @@ describe('AppComponent', () => { let mock; let getGroupsSpy; - const store = new GroupsStore(false); + const store = new GroupsStore({ hideProjects: false }); const service = new GroupsService(mockEndpoint); const createShallowComponent = (hideProjects = false) => { diff --git a/spec/frontend/groups/components/group_item_spec.js b/spec/frontend/groups/components/group_item_spec.js index 83acbb152b5..32bae812c86 100644 --- a/spec/frontend/groups/components/group_item_spec.js +++ b/spec/frontend/groups/components/group_item_spec.js @@ -2,6 +2,7 @@ import Vue from 'vue'; import mountComponent from 'helpers/vue_mount_component_helper'; import groupItemComponent from '~/groups/components/group_item.vue'; import groupFolderComponent from '~/groups/components/group_folder.vue'; +import { getGroupItemMicrodata } from '~/groups/store/utils'; import eventHub from '~/groups/event_hub'; import * as urlUtilities from '~/lib/utils/url_utility'; import { mockParentGroupItem, mockChildren } from '../mock_data'; @@ -30,6 +31,11 @@ describe('GroupItemComponent', () => { vm.$destroy(); }); + const withMicrodata = group => ({ + ...group, + microdata: getGroupItemMicrodata(group), + }); + describe('computed', () => { describe('groupDomId', () => { it('should return ID string suffixed with group ID', () => { @@ -212,4 +218,47 @@ describe('GroupItemComponent', () => { expect(vm.$el.querySelector('.group-list-tree')).toBeDefined(); }); }); + describe('schema.org props', () => { + describe('when showSchemaMarkup is disabled on the group', () => { + it.each(['itemprop', 'itemtype', 'itemscope'], 'it does not set %s', attr => { + expect(vm.$el.getAttribute(attr)).toBeNull(); + }); + it.each( + ['.js-group-avatar', '.js-group-name', '.js-group-description'], + 'it does not set `itemprop` on sub-nodes', + selector => { + expect(vm.$el.querySelector(selector).getAttribute('itemprop')).toBeNull(); + }, + ); + }); + describe('when group has microdata', () => { + beforeEach(() => { + const group = withMicrodata({ + ...mockParentGroupItem, + avatarUrl: 'http://foo.bar', + description: 'Foo Bar', + }); + + vm = createComponent(group); + }); + + it.each` + attr | value + ${'itemscope'} | ${'itemscope'} + ${'itemtype'} | ${'https://schema.org/Organization'} + ${'itemprop'} | ${'subOrganization'} + `('it does set correct $attr', ({ attr, value } = {}) => { + expect(vm.$el.getAttribute(attr)).toBe(value); + }); + + it.each` + selector | propValue + ${'[data-testid="group-avatar"]'} | ${'logo'} + ${'[data-testid="group-name"]'} | ${'name'} + ${'[data-testid="group-description"]'} | ${'description'} + `('it does set correct $selector', ({ selector, propValue } = {}) => { + expect(vm.$el.querySelector(selector).getAttribute('itemprop')).toBe(propValue); + }); + }); + }); }); diff --git a/spec/frontend/groups/components/visibility_level_dropdown_spec.js b/spec/frontend/groups/components/visibility_level_dropdown_spec.js new file mode 100644 index 00000000000..bf9508dc768 --- /dev/null +++ b/spec/frontend/groups/components/visibility_level_dropdown_spec.js @@ -0,0 +1,73 @@ +import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import Component from '~/groups/components/visibility_level_dropdown.vue'; + +describe('Visibility Level Dropdown', () => { + let wrapper; + + const options = [ + { level: 0, label: 'Private', description: 'Private description' }, + { level: 20, label: 'Public', description: 'Public description' }, + ]; + const defaultLevel = 0; + + const createComponent = propsData => { + wrapper = shallowMount(Component, { + propsData, + }); + }; + + beforeEach(() => { + createComponent({ + visibilityLevelOptions: options, + defaultLevel, + }); + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const hiddenInputValue = () => + wrapper.find("input[name='group[visibility_level]']").attributes('value'); + const dropdownText = () => wrapper.find(GlDropdown).props('text'); + const findDropdownItems = () => + wrapper.findAll(GlDropdownItem).wrappers.map(option => ({ + text: option.text(), + secondaryText: option.props('secondaryText'), + })); + + describe('Default values', () => { + it('sets the value of the hidden input to the default value', () => { + expect(hiddenInputValue()).toBe(options[0].level.toString()); + }); + + it('sets the text of the dropdown to the default value', () => { + expect(dropdownText()).toBe(options[0].label); + }); + + it('shows all dropdown options', () => { + expect(findDropdownItems()).toEqual( + options.map(({ label, description }) => ({ text: label, secondaryText: description })), + ); + }); + }); + + describe('Selecting an option', () => { + beforeEach(() => { + wrapper + .findAll(GlDropdownItem) + .at(1) + .vm.$emit('click'); + }); + + it('sets the value of the hidden input to the selected value', () => { + expect(hiddenInputValue()).toBe(options[1].level.toString()); + }); + + it('sets the text of the dropdown to the selected value', () => { + expect(dropdownText()).toBe(options[1].label); + }); + }); +}); diff --git a/spec/frontend/groups/members/components/app_spec.js b/spec/frontend/groups/members/components/app_spec.js index de9f30649e9..208e2fc35b6 100644 --- a/spec/frontend/groups/members/components/app_spec.js +++ b/spec/frontend/groups/members/components/app_spec.js @@ -3,12 +3,10 @@ import { nextTick } from 'vue'; import Vuex from 'vuex'; import { GlAlert } from '@gitlab/ui'; import App from '~/groups/members/components/app.vue'; +import FilterSortContainer from '~/members/components/filter_sort/filter_sort_container.vue'; import * as commonUtils from '~/lib/utils/common_utils'; -import { - RECEIVE_MEMBER_ROLE_ERROR, - HIDE_ERROR, -} from '~/vuex_shared/modules/members/mutation_types'; -import mutations from '~/vuex_shared/modules/members/mutations'; +import { RECEIVE_MEMBER_ROLE_ERROR, HIDE_ERROR } from '~/members/store/mutation_types'; +import mutations from '~/members/store/mutations'; describe('GroupMembersApp', () => { const localVue = createLocalVue(); @@ -17,7 +15,7 @@ describe('GroupMembersApp', () => { let wrapper; let store; - const createComponent = (state = {}) => { + const createComponent = (state = {}, options = {}) => { store = new Vuex.Store({ state: { showError: true, @@ -30,10 +28,12 @@ describe('GroupMembersApp', () => { wrapper = shallowMount(App, { localVue, store, + ...options, }); }; const findAlert = () => wrapper.find(GlAlert); + const findFilterSortContainer = () => wrapper.find(FilterSortContainer); beforeEach(() => { commonUtils.scrollToElement = jest.fn(); @@ -86,4 +86,22 @@ describe('GroupMembersApp', () => { expect(findAlert().exists()).toBe(false); }); }); + + describe.each` + featureFlagValue | exists + ${true} | ${true} + ${false} | ${false} + `( + 'when `group_members_filtered_search` feature flag is $featureFlagValue', + ({ featureFlagValue, exists }) => { + it(`${exists ? 'renders' : 'does not render'} FilterSortContainer`, () => { + createComponent( + {}, + { provide: { glFeatures: { groupMembersFilteredSearch: featureFlagValue } } }, + ); + + expect(findFilterSortContainer().exists()).toBe(exists); + }); + }, + ); }); diff --git a/spec/frontend/groups/members/index_spec.js b/spec/frontend/groups/members/index_spec.js index aaa36665c45..5c717e53229 100644 --- a/spec/frontend/groups/members/index_spec.js +++ b/spec/frontend/groups/members/index_spec.js @@ -9,12 +9,13 @@ describe('initGroupMembersApp', () => { let wrapper; const setup = () => { - vm = initGroupMembersApp( - el, - ['account'], - { table: { 'data-qa-selector': 'members_list' } }, - () => ({}), - ); + vm = initGroupMembersApp(el, { + tableFields: ['account'], + tableAttrs: { table: { 'data-qa-selector': 'members_list' } }, + tableSortableFields: ['account'], + requestFormatter: () => ({}), + filteredSearchBar: { show: false }, + }); wrapper = createWrapper(vm); }; @@ -22,6 +23,7 @@ describe('initGroupMembersApp', () => { el = document.createElement('div'); el.setAttribute('data-members', membersJsonString); el.setAttribute('data-group-id', '234'); + el.setAttribute('data-can-manage-members', 'true'); el.setAttribute('data-member-path', '/groups/foo-bar/-/group_members/:id'); window.gon = { current_user_id: 123 }; @@ -61,6 +63,12 @@ describe('initGroupMembersApp', () => { expect(vm.$store.state.sourceId).toBe(234); }); + it('parses and sets `data-can-manage-members` as `canManageMembers` in Vuex store', () => { + setup(); + + expect(vm.$store.state.canManageMembers).toBe(true); + }); + it('parses and sets `members` in Vuex store', () => { setup(); @@ -79,12 +87,24 @@ describe('initGroupMembersApp', () => { expect(vm.$store.state.tableAttrs).toEqual({ table: { 'data-qa-selector': 'members_list' } }); }); + it('sets `tableSortableFields` in Vuex store', () => { + setup(); + + expect(vm.$store.state.tableSortableFields).toEqual(['account']); + }); + it('sets `requestFormatter` in Vuex store', () => { setup(); expect(vm.$store.state.requestFormatter()).toEqual({}); }); + it('sets `filteredSearchBar` in Vuex store', () => { + setup(); + + expect(vm.$store.state.filteredSearchBar).toEqual({ show: false }); + }); + it('sets `memberPath` in Vuex store', () => { setup(); diff --git a/spec/frontend/groups/members/utils_spec.js b/spec/frontend/groups/members/utils_spec.js index b0921c7642f..68945174e9d 100644 --- a/spec/frontend/groups/members/utils_spec.js +++ b/spec/frontend/groups/members/utils_spec.js @@ -13,6 +13,7 @@ describe('group member utils', () => { el = document.createElement('div'); el.setAttribute('data-members', membersJsonString); el.setAttribute('data-group-id', '234'); + el.setAttribute('data-can-manage-members', 'true'); }); afterEach(() => { @@ -23,6 +24,7 @@ describe('group member utils', () => { expect(parseDataAttributes(el)).toEqual({ members: membersParsed, sourceId: 234, + canManageMembers: true, }); }); }); diff --git a/spec/frontend/groups/store/groups_store_spec.js b/spec/frontend/groups/store/groups_store_spec.js index 7d12f73d270..8ac5d7099f1 100644 --- a/spec/frontend/groups/store/groups_store_spec.js +++ b/spec/frontend/groups/store/groups_store_spec.js @@ -1,4 +1,5 @@ import GroupsStore from '~/groups/store/groups_store'; +import { getGroupItemMicrodata } from '~/groups/store/utils'; import { mockGroups, mockSearchedGroups, @@ -17,9 +18,9 @@ describe('ProjectsStore', () => { expect(Object.keys(store.state).length).toBe(2); expect(Array.isArray(store.state.groups)).toBeTruthy(); expect(Object.keys(store.state.pageInfo).length).toBe(0); - expect(store.hideProjects).not.toBeDefined(); + expect(store.hideProjects).toBeFalsy(); - store = new GroupsStore(true); + store = new GroupsStore({ hideProjects: true }); expect(store.hideProjects).toBeTruthy(); }); @@ -86,22 +87,30 @@ describe('ProjectsStore', () => { describe('formatGroupItem', () => { it('should parse group item object and return updated object', () => { - let store; - let updatedGroupItem; - - store = new GroupsStore(); - updatedGroupItem = store.formatGroupItem(mockRawChildren[0]); + const store = new GroupsStore(); + const updatedGroupItem = store.formatGroupItem(mockRawChildren[0]); expect(Object.keys(updatedGroupItem).indexOf('fullName')).toBeGreaterThan(-1); expect(updatedGroupItem.childrenCount).toBe(mockRawChildren[0].children_count); expect(updatedGroupItem.isChildrenLoading).toBe(false); expect(updatedGroupItem.isBeingRemoved).toBe(false); + expect(updatedGroupItem.microdata).toEqual({}); + }); - store = new GroupsStore(true); - updatedGroupItem = store.formatGroupItem(mockRawChildren[0]); + it('with hideProjects', () => { + const store = new GroupsStore({ hideProjects: true }); + const updatedGroupItem = store.formatGroupItem(mockRawChildren[0]); expect(Object.keys(updatedGroupItem).indexOf('fullName')).toBeGreaterThan(-1); expect(updatedGroupItem.childrenCount).toBe(mockRawChildren[0].subgroup_count); + expect(updatedGroupItem.microdata).toEqual({}); + }); + + it('with showSchemaMarkup', () => { + const store = new GroupsStore({ showSchemaMarkup: true }); + const updatedGroupItem = store.formatGroupItem(mockRawChildren[0]); + + expect(updatedGroupItem.microdata).toEqual(getGroupItemMicrodata(mockRawChildren[0])); }); }); diff --git a/spec/frontend/groups/store/utils_spec.js b/spec/frontend/groups/store/utils_spec.js new file mode 100644 index 00000000000..0961d4c72b4 --- /dev/null +++ b/spec/frontend/groups/store/utils_spec.js @@ -0,0 +1,44 @@ +import { getGroupItemMicrodata } from '~/groups/store/utils'; + +describe('~/groups/store/utils', () => { + describe('getGroupItemMetadata', () => { + it('has default type', () => { + expect(getGroupItemMicrodata({ type: 'silly' })).toMatchInlineSnapshot(` + Object { + "descriptionItemprop": "description", + "imageItemprop": "image", + "itemprop": "owns", + "itemscope": true, + "itemtype": "https://schema.org/Thing", + "nameItemprop": "name", + } + `); + }); + + it('has group props', () => { + expect(getGroupItemMicrodata({ type: 'group' })).toMatchInlineSnapshot(` + Object { + "descriptionItemprop": "description", + "imageItemprop": "logo", + "itemprop": "subOrganization", + "itemscope": true, + "itemtype": "https://schema.org/Organization", + "nameItemprop": "name", + } + `); + }); + + it('has project props', () => { + expect(getGroupItemMicrodata({ type: 'project' })).toMatchInlineSnapshot(` + Object { + "descriptionItemprop": "description", + "imageItemprop": "image", + "itemprop": "owns", + "itemscope": true, + "itemtype": "https://schema.org/SoftwareSourceCode", + "nameItemprop": "name", + } + `); + }); + }); +}); -- cgit v1.2.3