Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-11-22 21:07:57 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-11-22 21:07:57 +0300
commit277c0c75bf32b40d882c35feafaae90f69c40dd9 (patch)
tree35f3969f59c1886fcfa71812cb2b942c46d9dffe /spec
parent5e44c2ba46e780552317dec29e3b51282dfd5696 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb3
-rw-r--r--spec/frontend/boards/components/board_filtered_search_spec.js10
-rw-r--r--spec/frontend/boards/mock_data.js14
-rw-r--r--spec/frontend/diffs/components/diff_code_quality_spec.js2
-rw-r--r--spec/frontend/diffs/mock_data/diff_code_quality.js28
-rw-r--r--spec/frontend/filtered_search/components/recent_searches_dropdown_content_spec.js3
-rw-r--r--spec/frontend/work_items/components/work_item_detail_modal_spec.js2
-rw-r--r--spec/frontend/work_items/components/work_item_detail_spec.js14
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js11
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_links_spec.js182
-rw-r--r--spec/frontend/work_items/mock_data.js6
-rw-r--r--spec/frontend/work_items/pages/work_item_root_spec.js2
-rw-r--r--spec/helpers/application_helper_spec.rb36
-rw-r--r--spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb36
-rw-r--r--spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb16
-rw-r--r--spec/presenters/project_presenter_spec.rb14
-rw-r--r--spec/scripts/lib/glfm/update_specification_spec.rb197
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--spec/views/projects/_flash_messages.html.haml_spec.rb4
19 files changed, 412 insertions, 169 deletions
diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index 332426de07e..99f1b1ab1ad 100644
--- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
+++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
@@ -449,8 +449,11 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
page.within '.diff-content' do
find('button[data-testid="resolve-discussion-button"]').click
+ wait_for_requests
+
find_field('Reply…').click
+ find('.js-unresolve-checkbox')
find('.js-note-text').set 'testing'
click_button 'Add comment now'
diff --git a/spec/frontend/boards/components/board_filtered_search_spec.js b/spec/frontend/boards/components/board_filtered_search_spec.js
index 6f17e4193a3..db94b392694 100644
--- a/spec/frontend/boards/components/board_filtered_search_spec.js
+++ b/spec/frontend/boards/components/board_filtered_search_spec.js
@@ -30,7 +30,7 @@ describe('BoardFilteredSearch', () => {
{
icon: 'labels',
title: TOKEN_TITLE_LABEL,
- type: 'label',
+ type: TOKEN_TYPE_LABEL,
operators: [
{ value: '=', description: 'is' },
{ value: '!=', description: 'is not' },
@@ -43,7 +43,7 @@ describe('BoardFilteredSearch', () => {
{
icon: 'pencil',
title: TOKEN_TITLE_AUTHOR,
- type: 'author',
+ type: TOKEN_TYPE_AUTHOR,
operators: [
{ value: '=', description: 'is' },
{ value: '!=', description: 'is not' },
@@ -109,7 +109,7 @@ describe('BoardFilteredSearch', () => {
createComponent({ props: { eeFilters: { labelName: ['label'] } } });
expect(findFilteredSearch().props('initialFilterValue')).toEqual([
- { type: 'label', value: { data: 'label', operator: '=' } },
+ { type: TOKEN_TYPE_LABEL, value: { data: 'label', operator: '=' } },
]);
});
});
@@ -158,7 +158,9 @@ describe('BoardFilteredSearch', () => {
['None', url('None')],
['Any', url('Any')],
])('sets the url param %s', (assigneeParam, expected) => {
- const mockFilters = [{ type: 'assignee', value: { data: assigneeParam, operator: '=' } }];
+ const mockFilters = [
+ { type: TOKEN_TYPE_ASSIGNEE, value: { data: assigneeParam, operator: '=' } },
+ ];
jest.spyOn(urlUtility, 'updateHistory');
findFilteredSearch().vm.$emit('onFilter', mockFilters);
diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js
index 0cdab747a8d..30b2320097c 100644
--- a/spec/frontend/boards/mock_data.js
+++ b/spec/frontend/boards/mock_data.js
@@ -2,18 +2,22 @@ import { GlFilteredSearchToken } from '@gitlab/ui';
import { keyBy } from 'lodash';
import { ListType } from '~/boards/constants';
import {
- OPERATORS_IS_NOT,
OPERATORS_IS,
+ OPERATORS_IS_NOT,
TOKEN_TITLE_ASSIGNEE,
TOKEN_TITLE_AUTHOR,
+ TOKEN_TITLE_CONFIDENTIAL,
TOKEN_TITLE_LABEL,
TOKEN_TITLE_MILESTONE,
+ TOKEN_TITLE_MY_REACTION,
TOKEN_TITLE_RELEASE,
TOKEN_TITLE_TYPE,
TOKEN_TYPE_ASSIGNEE,
TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_CONFIDENTIAL,
TOKEN_TYPE_LABEL,
TOKEN_TYPE_MILESTONE,
+ TOKEN_TYPE_MY_REACTION,
TOKEN_TYPE_RELEASE,
TOKEN_TYPE_TYPE,
} from '~/vue_shared/components/filtered_search_bar/constants';
@@ -733,18 +737,18 @@ export const mockMoveData = {
};
export const mockEmojiToken = {
- type: 'my-reaction',
+ type: TOKEN_TYPE_MY_REACTION,
icon: 'thumb-up',
- title: 'My-Reaction',
+ title: TOKEN_TITLE_MY_REACTION,
unique: true,
token: EmojiToken,
fetchEmojis: expect.any(Function),
};
export const mockConfidentialToken = {
- type: 'confidential',
+ type: TOKEN_TYPE_CONFIDENTIAL,
icon: 'eye-slash',
- title: 'Confidential',
+ title: TOKEN_TITLE_CONFIDENTIAL,
unique: true,
token: GlFilteredSearchToken,
operators: OPERATORS_IS,
diff --git a/spec/frontend/diffs/components/diff_code_quality_spec.js b/spec/frontend/diffs/components/diff_code_quality_spec.js
index b5dce4fc924..6232bbee8af 100644
--- a/spec/frontend/diffs/components/diff_code_quality_spec.js
+++ b/spec/frontend/diffs/components/diff_code_quality_spec.js
@@ -34,7 +34,7 @@ describe('DiffCodeQuality', () => {
wrapper = createWrapper(multipleFindingsArr);
const listItems = wrapper.findAll('li');
- expect(wrapper.findAll('li').length).toBe(3);
+ expect(wrapper.findAll('li').length).toBe(5);
listItems.wrappers.map((e, i) => {
return expect(e.text()).toEqual(multipleFindingsArr[i].description);
diff --git a/spec/frontend/diffs/mock_data/diff_code_quality.js b/spec/frontend/diffs/mock_data/diff_code_quality.js
index befab3b676b..7558592f6a4 100644
--- a/spec/frontend/diffs/mock_data/diff_code_quality.js
+++ b/spec/frontend/diffs/mock_data/diff_code_quality.js
@@ -1,25 +1,39 @@
export const multipleFindingsArr = [
{
severity: 'minor',
- description: 'Unexpected Debugger Statement.',
+ description: 'mocked minor Issue',
line: 2,
},
{
severity: 'major',
- description:
- 'Function `aVeryLongFunction` has 52 lines of code (exceeds 25 allowed). Consider refactoring.',
+ description: 'mocked major Issue',
line: 3,
},
{
- severity: 'minor',
- description: 'Arrow function has too many statements (52). Maximum allowed is 30.',
+ severity: 'info',
+ description: 'mocked info Issue',
+ line: 3,
+ },
+ {
+ severity: 'critical',
+ description: 'mocked critical Issue',
+ line: 3,
+ },
+ {
+ severity: 'blocker',
+ description: 'mocked blocker Issue',
line: 3,
},
];
-export const multipleFindings = {
+export const fiveFindings = {
+ filePath: 'index.js',
+ codequality: multipleFindingsArr.slice(0, 5),
+};
+
+export const threeFindings = {
filePath: 'index.js',
- codequality: multipleFindingsArr,
+ codequality: multipleFindingsArr.slice(0, 3),
};
export const singularFinding = {
diff --git a/spec/frontend/filtered_search/components/recent_searches_dropdown_content_spec.js b/spec/frontend/filtered_search/components/recent_searches_dropdown_content_spec.js
index 91457f10bf8..ebed477fa2f 100644
--- a/spec/frontend/filtered_search/components/recent_searches_dropdown_content_spec.js
+++ b/spec/frontend/filtered_search/components/recent_searches_dropdown_content_spec.js
@@ -2,6 +2,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content.vue';
import eventHub from '~/filtered_search/event_hub';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
+import { TOKEN_TYPE_AUTHOR } from '~/vue_shared/components/filtered_search_bar/constants';
describe('Recent Searches Dropdown Content', () => {
let wrapper;
@@ -60,7 +61,7 @@ describe('Recent Searches Dropdown Content', () => {
items: [
'foo',
'author:@root label:~foo bar',
- [{ type: 'author_username', value: { data: 'toby', operator: '=' } }],
+ [{ type: TOKEN_TYPE_AUTHOR, value: { data: 'toby', operator: '=' } }],
],
isLocalStorageAvailable: true,
});
diff --git a/spec/frontend/work_items/components/work_item_detail_modal_spec.js b/spec/frontend/work_items/components/work_item_detail_modal_spec.js
index 4029e47c390..686641800b3 100644
--- a/spec/frontend/work_items/components/work_item_detail_modal_spec.js
+++ b/spec/frontend/work_items/components/work_item_detail_modal_spec.js
@@ -86,7 +86,7 @@ describe('WorkItemDetailModal component', () => {
isModal: true,
workItemId: defaultPropsData.workItemId,
workItemParentId: defaultPropsData.issueGid,
- iid: null,
+ workItemIid: null,
});
});
diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js
index 26777b57797..d6e4b0ece36 100644
--- a/spec/frontend/work_items/components/work_item_detail_spec.js
+++ b/spec/frontend/work_items/components/work_item_detail_spec.js
@@ -11,6 +11,7 @@ import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
+import setWindowLocation from 'helpers/set_window_location_helper';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import WorkItemDetail from '~/work_items/components/work_item_detail.vue';
import WorkItemActions from '~/work_items/components/work_item_actions.vue';
@@ -94,7 +95,6 @@ describe('WorkItemDetail component', () => {
error = undefined,
workItemsMvc2Enabled = false,
fetchByIid = false,
- iidPathQueryParam = undefined,
} = {}) => {
const handlers = [
[workItemQuery, handler],
@@ -108,7 +108,7 @@ describe('WorkItemDetail component', () => {
wrapper = shallowMount(WorkItemDetail, {
apolloProvider: createMockApollo(handlers),
- propsData: { isModal, workItemId, iid: '1' },
+ propsData: { isModal, workItemId, workItemIid: '1' },
data() {
return {
updateInProgress,
@@ -129,18 +129,12 @@ describe('WorkItemDetail component', () => {
WorkItemWeight: true,
WorkItemIteration: true,
},
- mocks: {
- $route: {
- query: {
- iid_path: iidPathQueryParam,
- },
- },
- },
});
};
afterEach(() => {
wrapper.destroy();
+ setWindowLocation('');
});
describe('when there is no `workItemId` prop', () => {
@@ -633,6 +627,8 @@ describe('WorkItemDetail component', () => {
});
it('calls the IID work item query when `useIidInWorkItemsPath` feature flag is true and `iid_path` route parameter is present', async () => {
+ setWindowLocation(`?iid_path=true`);
+
createComponent({ fetchByIid: true, iidPathQueryParam: 'true' });
await waitForPromises();
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
index 1d5472a0473..e345e5fc7cd 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
@@ -76,16 +76,21 @@ describe('WorkItemLinkChild', () => {
it.each`
action | event | emittedEvent
- ${'clicking'} | ${'click'} | ${'click'}
${'doing mouseover on'} | ${'mouseover'} | ${'mouseover'}
${'doing mouseout on'} | ${'mouseout'} | ${'mouseout'}
`('$action item title emit `$emittedEvent` event', ({ event, emittedEvent }) => {
+ titleEl.vm.$emit(event);
+
+ expect(wrapper.emitted(emittedEvent)).toEqual([[]]);
+ });
+
+ it('emits click event with correct parameters on clicking title', () => {
const eventObj = {
preventDefault: jest.fn(),
};
- titleEl.vm.$emit(event, eventObj);
+ titleEl.vm.$emit('click', eventObj);
- expect(wrapper.emitted(emittedEvent)).toEqual([[workItemTask.id, eventObj]]);
+ expect(wrapper.emitted('click')).toEqual([[eventObj]]);
});
});
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
index 66ce2c1becf..fe95a985177 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
@@ -4,20 +4,25 @@ import VueApollo from 'vue-apollo';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import { stubComponent } from 'helpers/stub_component';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import issueDetailsQuery from 'ee_else_ce/work_items/graphql/get_issue_details.query.graphql';
import WorkItemLinks from '~/work_items/components/work_item_links/work_item_links.vue';
import WorkItemLinkChild from '~/work_items/components/work_item_links/work_item_link_child.vue';
+import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
import { FORM_TYPES } from '~/work_items/constants';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
import changeWorkItemParentMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import getWorkItemLinksQuery from '~/work_items/graphql/work_item_links.query.graphql';
+import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
import {
workItemHierarchyResponse,
workItemHierarchyEmptyResponse,
workItemHierarchyNoUpdatePermissionResponse,
changeWorkItemParentMutationResponse,
workItemQueryResponse,
+ projectWorkItemResponse,
} from '../../mock_data';
Vue.use(VueApollo);
@@ -55,6 +60,7 @@ const issueDetailsResponse = (confidential = false) => ({
},
},
});
+const showModal = jest.fn();
describe('WorkItemLinks', () => {
let wrapper;
@@ -71,6 +77,7 @@ describe('WorkItemLinks', () => {
.mockResolvedValue(changeWorkItemParentMutationResponse);
const childWorkItemQueryHandler = jest.fn().mockResolvedValue(workItemQueryResponse);
+ const childWorkItemByIidHandler = jest.fn().mockResolvedValue(projectWorkItemResponse);
const createComponent = async ({
data = {},
@@ -78,6 +85,7 @@ describe('WorkItemLinks', () => {
mutationHandler = mutationChangeParentHandler,
issueDetailsQueryHandler = jest.fn().mockResolvedValue(issueDetailsResponse()),
hasIterationsFeature = false,
+ fetchByIid = false,
} = {}) => {
mockApollo = createMockApollo(
[
@@ -85,6 +93,7 @@ describe('WorkItemLinks', () => {
[changeWorkItemParentMutation, mutationHandler],
[workItemQuery, childWorkItemQueryHandler],
[issueDetailsQuery, issueDetailsQueryHandler],
+ [workItemByIidQuery, childWorkItemByIidHandler],
],
{},
{ addTypename: true },
@@ -100,12 +109,22 @@ describe('WorkItemLinks', () => {
projectPath: 'project/path',
iid: '1',
hasIterationsFeature,
+ glFeatures: {
+ useIidInWorkItemsPath: fetchByIid,
+ },
},
propsData: { issuableId: 1 },
apolloProvider: mockApollo,
mocks: {
$toast,
},
+ stubs: {
+ WorkItemDetailModal: stubComponent(WorkItemDetailModal, {
+ methods: {
+ show: showModal,
+ },
+ }),
+ },
});
await waitForPromises();
@@ -130,6 +149,7 @@ describe('WorkItemLinks', () => {
afterEach(() => {
wrapper.destroy();
mockApollo = null;
+ setWindowLocation('');
});
it('is expanded by default', () => {
@@ -271,49 +291,157 @@ describe('WorkItemLinks', () => {
});
});
- describe('prefetching child items', () => {
- let firstChild;
-
- beforeEach(async () => {
- await createComponent();
+ describe('when parent item is confidential', () => {
+ it('passes correct confidentiality status to form', async () => {
+ await createComponent({
+ issueDetailsQueryHandler: jest.fn().mockResolvedValue(issueDetailsResponse(true)),
+ });
+ findToggleFormDropdown().vm.$emit('click');
+ findToggleAddFormButton().vm.$emit('click');
+ await nextTick();
- firstChild = findFirstWorkItemLinkChild();
+ expect(findAddLinksForm().props('parentConfidential')).toBe(true);
});
+ });
- it('does not fetch the child work item before hovering work item links', () => {
- expect(childWorkItemQueryHandler).not.toHaveBeenCalled();
+ describe('when work item is fetched by id', () => {
+ describe('prefetching child items', () => {
+ let firstChild;
+
+ beforeEach(async () => {
+ await createComponent();
+
+ firstChild = findFirstWorkItemLinkChild();
+ });
+
+ it('does not fetch the child work item by id before hovering work item links', () => {
+ expect(childWorkItemQueryHandler).not.toHaveBeenCalled();
+ });
+
+ it('fetches the child work item by id if link is hovered for 250+ ms', async () => {
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
+ jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
+ await waitForPromises();
+
+ expect(childWorkItemQueryHandler).toHaveBeenCalledWith({
+ id: 'gid://gitlab/WorkItem/2',
+ });
+ });
+
+ it('does not fetch the child work item by id if link is hovered for less than 250 ms', async () => {
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
+ jest.advanceTimersByTime(200);
+ firstChild.vm.$emit('mouseout', firstChild.vm.childItem.id);
+ await waitForPromises();
+
+ expect(childWorkItemQueryHandler).not.toHaveBeenCalled();
+ });
+
+ it('does not fetch work item by iid if link is hovered for 250+ ms', async () => {
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
+ jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
+ await waitForPromises();
+
+ expect(childWorkItemByIidHandler).not.toHaveBeenCalled();
+ });
});
- it('fetches the child work item if link is hovered for 250+ ms', async () => {
- firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
- jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
- await waitForPromises();
+ it('starts prefetching work item by id if URL contains work item id', async () => {
+ setWindowLocation('?work_item_id=5');
+ await createComponent();
expect(childWorkItemQueryHandler).toHaveBeenCalledWith({
- id: 'gid://gitlab/WorkItem/2',
+ id: 'gid://gitlab/WorkItem/5',
});
});
- it('does not fetch the child work item if link is hovered for less than 250 ms', async () => {
- firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
- jest.advanceTimersByTime(200);
- firstChild.vm.$emit('mouseout', firstChild.vm.childItem.id);
- await waitForPromises();
+ it('does not open the modal if work item id URL parameter is not found in child items', async () => {
+ setWindowLocation('?work_item_id=555');
+ await createComponent();
+
+ expect(showModal).not.toHaveBeenCalled();
+ expect(wrapper.findComponent(WorkItemDetailModal).props('workItemId')).toBe(null);
+ });
+
+ it('opens the modal if work item id URL parameter is found in child items', async () => {
+ setWindowLocation('?work_item_id=2');
+ await createComponent();
- expect(childWorkItemQueryHandler).not.toHaveBeenCalled();
+ expect(showModal).toHaveBeenCalled();
+ expect(wrapper.findComponent(WorkItemDetailModal).props('workItemId')).toBe(
+ 'gid://gitlab/WorkItem/2',
+ );
});
});
- describe('when parent item is confidential', () => {
- it('passes correct confidentiality status to form', async () => {
- await createComponent({
- issueDetailsQueryHandler: jest.fn().mockResolvedValue(issueDetailsResponse(true)),
+ describe('when work item is fetched by iid', () => {
+ describe('prefetching child items', () => {
+ let firstChild;
+
+ beforeEach(async () => {
+ setWindowLocation('?iid_path=true');
+ await createComponent({ fetchByIid: true });
+
+ firstChild = findFirstWorkItemLinkChild();
});
- findToggleFormDropdown().vm.$emit('click');
- findToggleAddFormButton().vm.$emit('click');
- await nextTick();
- expect(findAddLinksForm().props('parentConfidential')).toBe(true);
+ it('does not fetch the child work item by iid before hovering work item links', () => {
+ expect(childWorkItemByIidHandler).not.toHaveBeenCalled();
+ });
+
+ it('fetches the child work item by iid if link is hovered for 250+ ms', async () => {
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
+ jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
+ await waitForPromises();
+
+ expect(childWorkItemByIidHandler).toHaveBeenCalledWith({
+ fullPath: 'project/path',
+ iid: '2',
+ });
+ });
+
+ it('does not fetch the child work item by iid if link is hovered for less than 250 ms', async () => {
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
+ jest.advanceTimersByTime(200);
+ firstChild.vm.$emit('mouseout', firstChild.vm.childItem.id);
+ await waitForPromises();
+
+ expect(childWorkItemByIidHandler).not.toHaveBeenCalled();
+ });
+
+ it('does not fetch work item by id if link is hovered for 250+ ms', async () => {
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
+ jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
+ await waitForPromises();
+
+ expect(childWorkItemQueryHandler).not.toHaveBeenCalled();
+ });
});
+
+ it('starts prefetching work item by iid if URL contains work item id', async () => {
+ setWindowLocation('?work_item_iid=5&iid_path=true');
+ await createComponent({ fetchByIid: true });
+
+ expect(childWorkItemByIidHandler).toHaveBeenCalledWith({
+ iid: '5',
+ fullPath: 'project/path',
+ });
+ });
+ });
+
+ it('does not open the modal if work item iid URL parameter is not found in child items', async () => {
+ setWindowLocation('?work_item_iid=555&iid_path=true');
+ await createComponent({ fetchByIid: true });
+
+ expect(showModal).not.toHaveBeenCalled();
+ expect(wrapper.findComponent(WorkItemDetailModal).props('workItemIid')).toBe(null);
+ });
+
+ it('opens the modal if work item iid URL parameter is found in child items', async () => {
+ setWindowLocation('?work_item_iid=2&iid_path=true');
+ await createComponent({ fetchByIid: true });
+
+ expect(showModal).toHaveBeenCalled();
+ expect(wrapper.findComponent(WorkItemDetailModal).props('workItemIid')).toBe('2');
});
});
diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js
index 67fda49919a..6385f12ccea 100644
--- a/spec/frontend/work_items/mock_data.js
+++ b/spec/frontend/work_items/mock_data.js
@@ -723,6 +723,7 @@ export const workItemHierarchyNoUpdatePermissionResponse = {
nodes: [
{
id: 'gid://gitlab/WorkItem/2',
+ iid: '2',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/5',
__typename: 'WorkItemType',
@@ -747,6 +748,7 @@ export const workItemHierarchyNoUpdatePermissionResponse = {
export const workItemTask = {
id: 'gid://gitlab/WorkItem/4',
+ iid: '4',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/5',
__typename: 'WorkItemType',
@@ -761,6 +763,7 @@ export const workItemTask = {
export const confidentialWorkItemTask = {
id: 'gid://gitlab/WorkItem/2',
+ iid: '2',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/5',
__typename: 'WorkItemType',
@@ -775,6 +778,7 @@ export const confidentialWorkItemTask = {
export const closedWorkItemTask = {
id: 'gid://gitlab/WorkItem/3',
+ iid: '3',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/5',
__typename: 'WorkItemType',
@@ -791,6 +795,7 @@ export const workItemHierarchyResponse = {
data: {
workItem: {
id: 'gid://gitlab/WorkItem/1',
+ iid: '1',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/6',
__typename: 'WorkItemType',
@@ -821,6 +826,7 @@ export const workItemHierarchyResponse = {
workItemTask,
{
id: 'gid://gitlab/WorkItem/5',
+ iid: '5',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/5',
__typename: 'WorkItemType',
diff --git a/spec/frontend/work_items/pages/work_item_root_spec.js b/spec/frontend/work_items/pages/work_item_root_spec.js
index 880c4271024..a766962771a 100644
--- a/spec/frontend/work_items/pages/work_item_root_spec.js
+++ b/spec/frontend/work_items/pages/work_item_root_spec.js
@@ -55,7 +55,7 @@ describe('Work items root component', () => {
isModal: false,
workItemId: 'gid://gitlab/WorkItem/1',
workItemParentId: null,
- iid: '1',
+ workItemIid: '1',
});
});
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 7f838167bd2..84b076ec1d2 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -419,7 +419,7 @@ RSpec.describe ApplicationHelper do
end
it 'includes all possible body data elements and associates the project elements with project' do
- expect(helper).to receive(:can?).with(nil, :download_code, project)
+ expect(helper).to receive(:can?).with(nil, :read_code, project)
expect(helper.body_data).to eq(
{
page: 'application',
@@ -437,7 +437,7 @@ RSpec.describe ApplicationHelper do
let_it_be(:project) { create(:project, :repository, group: create(:group)) }
it 'includes all possible body data elements and associates the project elements with project' do
- expect(helper).to receive(:can?).with(nil, :download_code, project)
+ expect(helper).to receive(:can?).with(nil, :read_code, project)
expect(helper.body_data).to eq(
{
page: 'application',
@@ -463,7 +463,7 @@ RSpec.describe ApplicationHelper do
stub_controller_method(:action_name, 'show')
stub_controller_method(:params, { id: issue.id })
- expect(helper).to receive(:can?).with(nil, :download_code, project).and_return(false)
+ expect(helper).to receive(:can?).with(nil, :read_code, project).and_return(false)
expect(helper.body_data).to eq(
{
page: 'projects:issues:show',
@@ -479,12 +479,34 @@ RSpec.describe ApplicationHelper do
end
end
- context 'when current_user has download_code permission' do
- it 'returns find_file with the default branch' do
+ describe 'find_file attribute' do
+ subject { helper.body_data[:find_file] }
+
+ before do
allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ context 'when the project has no repository' do
+ before do
+ allow(project).to receive(:empty_repo?).and_return(true)
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when user cannot read_code for the project' do
+ before do
+ allow(helper).to receive(:can?).with(user, :read_code, project).and_return(false)
+ end
- expect(helper).to receive(:can?).with(user, :download_code, project).and_return(true)
- expect(helper.body_data[:find_file]).to end_with(project.default_branch)
+ it { is_expected.to be_nil }
+ end
+
+ context 'when current_user has read_code permission' do
+ it 'returns find_file with the default branch' do
+ expect(helper).to receive(:can?).with(user, :read_code, project).and_return(true)
+ expect(subject).to end_with(project.default_branch)
+ end
end
end
end
diff --git a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
index 9aba6ac293c..59bfe2042fa 100644
--- a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::RailsCache do
let(:transaction) { Gitlab::Metrics::WebTransaction.new(env) }
let(:subscriber) { described_class.new }
- let(:event) { double(:event, duration: 15.2) }
+ let(:event) { double(:event, duration: 15.2, payload: { key: %w[a b c] }) }
describe '#cache_read' do
it 'increments the cache_read duration' do
@@ -64,6 +64,40 @@ RSpec.describe Gitlab::Metrics::Subscribers::RailsCache do
end
end
+ describe '#cache_read_multi' do
+ subject { subscriber.cache_read_multi(event) }
+
+ context 'with a transaction' do
+ before do
+ allow(subscriber).to receive(:current_transaction)
+ .and_return(transaction)
+ end
+
+ it 'observes multi-key count' do
+ expect(transaction).to receive(:observe)
+ .with(:gitlab_cache_read_multikey_count, event.payload[:key].size)
+
+ subject
+ end
+ end
+
+ context 'with no transaction' do
+ it 'does not observes multi-key count' do
+ expect(transaction).not_to receive(:observe)
+ .with(:gitlab_cache_read_multikey_count, event.payload[:key].size)
+
+ subject
+ end
+ end
+
+ it 'observes read_multi duration' do
+ expect(subscriber).to receive(:observe)
+ .with(:read_multi, event.duration)
+
+ subject
+ end
+ end
+
describe '#cache_write' do
it 'observes write duration' do
expect(subscriber).to receive(:observe)
diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
index 0bea06f602f..e4cb1e74d32 100644
--- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
@@ -273,6 +273,22 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
expect { described_class.track_event('unknown', values: entity1, time: Date.current) }.to raise_error(Gitlab::UsageDataCounters::HLLRedisCounter::UnknownEvent)
end
+ context 'when Rails environment is production' do
+ before do
+ allow(Rails.env).to receive(:development?).and_return(false)
+ allow(Rails.env).to receive(:test?).and_return(false)
+ end
+
+ it 'reports only UnknownEvent exception' do
+ expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
+ .with(Gitlab::UsageDataCounters::HLLRedisCounter::UnknownEvent)
+ .once
+ .and_call_original
+
+ expect { described_class.track_event('unknown', values: entity1, time: Date.current) }.not_to raise_error
+ end
+ end
+
it 'reports an error if Feature.enabled raise an error' do
expect(Feature).to receive(:enabled?).and_raise(StandardError.new)
expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb
index c32cc87afbb..378a7ef8dd6 100644
--- a/spec/presenters/project_presenter_spec.rb
+++ b/spec/presenters/project_presenter_spec.rb
@@ -76,21 +76,21 @@ RSpec.describe ProjectPresenter do
let_it_be(:project) { create(:project, :public, :repository) }
it 'returns files and readme if user has repository access' do
- allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(true)
+ allow(presenter).to receive(:can?).with(nil, :read_code, project).and_return(true)
expect(presenter.default_view).to eq('files')
end
it 'returns wiki if user does not have repository access and can read wiki, which exists' do
allow(project).to receive(:wiki_repository_exists?).and_return(true)
- allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(nil, :read_code, project).and_return(false)
allow(presenter).to receive(:can?).with(nil, :read_wiki, project).and_return(true)
expect(presenter.default_view).to eq('wiki')
end
it 'returns activity if user does not have repository or wiki access' do
- allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(nil, :read_code, project).and_return(false)
allow(presenter).to receive(:can?).with(nil, :read_issue, project).and_return(false)
allow(presenter).to receive(:can?).with(nil, :read_wiki, project).and_return(false)
@@ -117,7 +117,7 @@ RSpec.describe ProjectPresenter do
context 'when the user is allowed to see the code' do
it 'returns the project view' do
- allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(true)
+ allow(presenter).to receive(:can?).with(user, :read_code, project).and_return(true)
expect(presenter.default_view).to eq('readme')
end
@@ -126,7 +126,7 @@ RSpec.describe ProjectPresenter do
context 'with wikis enabled and the right policy for the user' do
before do
project.project_feature.update_attribute(:issues_access_level, 0)
- allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :read_code, project).and_return(false)
end
it 'returns wiki if the user has the right policy and the wiki exists' do
@@ -146,7 +146,7 @@ RSpec.describe ProjectPresenter do
context 'with issues as a feature available' do
it 'return issues' do
- allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :read_code, project).and_return(false)
allow(presenter).to receive(:can?).with(user, :read_issue, project).and_return(true)
allow(presenter).to receive(:can?).with(user, :read_wiki, project).and_return(false)
@@ -157,7 +157,7 @@ RSpec.describe ProjectPresenter do
context 'with no activity, no wikies and no issues' do
it 'returns activity as default' do
project.project_feature.update_attribute(:issues_access_level, 0)
- allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :read_code, project).and_return(false)
allow(presenter).to receive(:can?).with(user, :read_wiki, project).and_return(false)
allow(presenter).to receive(:can?).with(user, :read_issue, project).and_return(false)
diff --git a/spec/scripts/lib/glfm/update_specification_spec.rb b/spec/scripts/lib/glfm/update_specification_spec.rb
index ccf1a8fd26a..ed5650e7310 100644
--- a/spec/scripts/lib/glfm/update_specification_spec.rb
+++ b/spec/scripts/lib/glfm/update_specification_spec.rb
@@ -53,9 +53,9 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
let(:ghfm_spec_txt_examples) do
<<~MARKDOWN
- # Section with Examples
+ # Section with examples
- ## Emphasis and Strong
+ ## Emphasis and strong
```````````````````````````````` example
_EMPHASIS LINE 1_
@@ -101,20 +101,24 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
<<~MARKDOWN
# Official Specification Section with Examples
- Some examples.
+ ```````````````````````````````` example
+ official example
+ .
+ <p>official example</p>
+ ````````````````````````````````
+
MARKDOWN
end
let(:glfm_official_specification_md_contents) do
<<~MARKDOWN
- # GLFM Introduction
-
- GLFM intro text.
+ GLFM official text before examples
- <!-- BEGIN TESTS -->
+ #{described_class::BEGIN_TESTS_COMMENT_LINE_TEXT}
#{glfm_official_specification_md_examples}
- <!-- END TESTS -->
- # Non-example official content
+ #{described_class::END_TESTS_COMMENT_LINE_TEXT}
+
+ GLFM official text after examples
MARKDOWN
end
@@ -122,17 +126,19 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
<<~MARKDOWN
# Internal Extension Section with Examples
- Some examples.
+ ```````````````````````````````` example
+ internal example
+ .
+ <p>internal extension example</p>
+ ````````````````````````````````
MARKDOWN
end
let(:glfm_internal_extensions_md_contents) do
<<~MARKDOWN
- # Non-example internal content
- <!-- BEGIN TESTS -->
+ #{described_class::BEGIN_TESTS_COMMENT_LINE_TEXT}
#{glfm_internal_extensions_md_examples}
- <!-- END TESTS -->
- # More non-example internal content
+ #{described_class::END_TESTS_COMMENT_LINE_TEXT}
MARKDOWN
end
@@ -258,29 +264,46 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
describe 'writing output_example_snapshots/snapshot_spec.md' do
let(:es_snapshot_spec_md_contents) { reread_io(es_snapshot_spec_md_io) }
- before do
- subject.process(skip_spec_html_generation: true)
- end
+ context 'with valid glfm_internal_extensions.md' do
+ before do
+ subject.process(skip_spec_html_generation: true)
+ end
- it 'replaces the header text with the GitLab version' do
- expect(es_snapshot_spec_md_contents).not_to match(/GitHub Flavored Markdown Spec/m)
- expect(es_snapshot_spec_md_contents).not_to match(/^version: \d\.\d/m)
- expect(es_snapshot_spec_md_contents).not_to match(/^date: /m)
+ it 'replaces the header text with the GitLab version' do
+ expect(es_snapshot_spec_md_contents).not_to match(/GitHub Flavored Markdown Spec/m)
+ expect(es_snapshot_spec_md_contents).not_to match(/^version: \d\.\d/m)
+ expect(es_snapshot_spec_md_contents).not_to match(/^date: /m)
- expect(es_snapshot_spec_md_contents).to match(/#{Regexp.escape(described_class::GLFM_SPEC_TXT_HEADER)}/mo)
+ expect(es_snapshot_spec_md_contents).to match(/#{Regexp.escape(described_class::ES_SNAPSHOT_SPEC_MD_HEADER)}/mo)
+ end
+
+ it 'includes header and all examples', :unlimited_max_formatted_output_length do
+ # rubocop:disable Style/StringConcatenation (string contatenation is more readable)
+ expected = described_class::ES_SNAPSHOT_SPEC_MD_HEADER +
+ ghfm_spec_txt_examples +
+ "\n" +
+ glfm_official_specification_md_examples +
+ "\n\n" + # NOTE: We want a blank line between the official and internal examples
+ glfm_internal_extensions_md_examples +
+ "\n"
+ # rubocop:enable Style/StringConcatenation
+ expect(es_snapshot_spec_md_contents).to eq(expected)
+ end
end
- it 'includes header and all examples', :unlimited_max_formatted_output_length do
- # rubocop:disable Style/StringConcatenation (string contatenation is more readable)
- expected = described_class::GLFM_SPEC_TXT_HEADER +
- ghfm_spec_txt_examples +
- "\n" +
- glfm_official_specification_md_examples +
- "\n\n" + # NOTE: We want a blank line between the official and internal examples
- glfm_internal_extensions_md_examples +
- "\n"
- # rubocop:enable Style/StringConcatenation
- expect(es_snapshot_spec_md_contents).to eq(expected)
+ context 'with invalid non-example content in glfm_internal_extensions.md' do
+ let(:glfm_internal_extensions_md_contents) do
+ <<~MARKDOWN
+ THIS TEXT IS NOT ALLOWED IN THIS FILE, ONLY EXAMPLES IN BEGIN/END TEST BLOCK ARE ALLOWED
+ #{described_class::BEGIN_TESTS_COMMENT_LINE_TEXT}
+ #{glfm_internal_extensions_md_examples}
+ #{described_class::END_TESTS_COMMENT_LINE_TEXT}
+ MARKDOWN
+ end
+
+ it 'raises an error' do
+ expect { subject.process(skip_spec_html_generation: true) }.to raise_error /no content is allowed outside/i
+ end
end
end
@@ -294,66 +317,56 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
end
it 'renders expected HTML', :unlimited_max_formatted_output_length do
- # NOTE: We do assertions for both output HTML files in this same `it` example block,
+ # NOTE: We do all assertions for both output HTML files in this same `it` example block,
# because calling a full `subject.process` without `skip_spec_html_generation: true`
- # is very slow, and want to avoid doing it twice.
-
- expected_spec_html = <<~RENDERED_HTML
- <div class="gl-relative markdown-code-block js-markdown-code">
- <pre data-sourcepos="1:1-4:3" lang="yaml" class="code highlight js-syntax-highlight language-yaml" data-lang-params="frontmatter" v-pre="true"><code><span id="LC1" class="line" lang="yaml"><span class="na">title</span><span class="pi">:</span> <span class="s">GitLab Flavored Markdown (GLFM) Spec</span></span>
- <span id="LC2" class="line" lang="yaml"><span class="na">version</span><span class="pi">:</span> <span class="s">alpha</span></span></code></pre>
- <copy-code></copy-code>
- </div>
- <h1 data-sourcepos="5:1-5:19" dir="auto">
- <a id="user-content-glfm-introduction" class="anchor" href="#glfm-introduction" aria-hidden="true"></a>GLFM Introduction</h1>
- <p data-sourcepos="7:1-7:16" dir="auto">GLFM intro text.</p>
-
- <h1 data-sourcepos="10:1-10:46" dir="auto">
- <a id="user-content-official-specification-section-with-examples" class="anchor" href="#official-specification-section-with-examples" aria-hidden="true"></a>Official Specification Section with Examples</h1>
- <p data-sourcepos="12:1-12:14" dir="auto">Some examples.</p>
-
- <h1 data-sourcepos="15:1-15:30" dir="auto">
- <a id="user-content-non-example-official-content" class="anchor" href="#non-example-official-content" aria-hidden="true"></a>Non-example official content</h1>
- RENDERED_HTML
- expect(spec_html_contents).to be == expected_spec_html
-
- expected_snapshot_spec_html = <<~RENDERED_HTML
- <div class="gl-relative markdown-code-block js-markdown-code">
- <pre data-sourcepos="1:1-4:3" lang="yaml" class="code highlight js-syntax-highlight language-yaml" data-lang-params="frontmatter" v-pre="true"><code><span id="LC1" class="line" lang="yaml"><span class="na">title</span><span class="pi">:</span> <span class="s">GitLab Flavored Markdown (GLFM) Spec</span></span>
- <span id="LC2" class="line" lang="yaml"><span class="na">version</span><span class="pi">:</span> <span class="s">alpha</span></span></code></pre>
- <copy-code></copy-code>
- </div>
- <h1 data-sourcepos="5:1-5:23" dir="auto">
- <a id="user-content-section-with-examples" class="anchor" href="#section-with-examples" aria-hidden="true"></a>Section with Examples</h1>
- <h2 data-sourcepos="7:1-7:22" dir="auto">
- <a id="user-content-emphasis-and-strong" class="anchor" href="#emphasis-and-strong" aria-hidden="true"></a>Emphasis and Strong</h2>
- <div class="gl-relative markdown-code-block js-markdown-code">
- <pre data-sourcepos="9:1-12:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">_EMPHASIS LINE 1_</span>
- <span id="LC2" class="line" lang="plaintext">_EMPHASIS LINE 2_</span></code></pre>
- <copy-code></copy-code>
- </div>
- <div class="gl-relative markdown-code-block js-markdown-code">
- <pre data-sourcepos="14:1-17:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;em&gt;EMPHASIS LINE 1&lt;/em&gt;</span>
- <span id="LC2" class="line" lang="plaintext">&lt;em&gt;EMPHASIS LINE 2&lt;/em&gt;&lt;/p&gt;</span></code></pre>
- <copy-code></copy-code>
- </div>
- <div class="gl-relative markdown-code-block js-markdown-code">
- <pre data-sourcepos="19:1-21:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">__STRONG!__</span></code></pre>
- <copy-code></copy-code>
- </div>
- <div class="gl-relative markdown-code-block js-markdown-code">
- <pre data-sourcepos="23:1-25:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">&lt;p&gt;&lt;strong&gt;STRONG!&lt;/strong&gt;&lt;/p&gt;</span></code></pre>
- <copy-code></copy-code>
- </div>
- <p data-sourcepos="27:1-27:36" dir="auto">End of last GitHub examples section.</p>
- <h1 data-sourcepos="29:1-29:46" dir="auto">
- <a id="user-content-official-specification-section-with-examples" class="anchor" href="#official-specification-section-with-examples" aria-hidden="true"></a>Official Specification Section with Examples</h1>
- <p data-sourcepos="31:1-31:14" dir="auto">Some examples.</p>
- <h1 data-sourcepos="34:1-34:42" dir="auto">
- <a id="user-content-internal-extension-section-with-examples" class="anchor" href="#internal-extension-section-with-examples" aria-hidden="true"></a>Internal Extension Section with Examples</h1>
- <p data-sourcepos="36:1-36:14" dir="auto">Some examples.</p>
- RENDERED_HTML
- expect(snapshot_spec_html_contents).to be == expected_snapshot_spec_html
+ # is very slow, and want to avoid doing it multiple times
+ #
+ # We also do fairly loose and minimal assertions around the basic structure of the files.
+ # Otherwise, if we asserted the complete exact structure of the HTML, this would be a
+ # brittle test which would breaks every time that something minor changed around the
+ # GLFM rendering. E.g. classes, ids, attribute ordering, etc. All of this behavior
+ # should be thoroughly covered elsewhere by other testing. If there are regressions
+ # in the update specification logic in the future which are not caught by this example,
+ # additional test coverage can be added as necessary.
+
+ # --------------------
+ # spec.html assertions
+ # --------------------
+
+ # correct title should in a header
+ expect(spec_html_contents).to match(%r{<h1.*#{described_class::GLFM_SPEC_TXT_TITLE}</h1>}o)
+
+ # correct text should be included with correct ordering
+ expect(spec_html_contents)
+ .to match(%r{official text before.*official example.*official text after}m)
+
+ # -----------------------------
+ # snapshot_spec.html assertions
+ # -----------------------------
+
+ # correct title should in a header
+ expect(snapshot_spec_html_contents).to match(%r{<h1.*#{described_class::ES_SNAPSHOT_SPEC_TITLE}</h1>}o)
+
+ # correct example text should be included
+ expect(snapshot_spec_html_contents)
+ .to match(%r{internal example}m)
+
+ # -----------------------------
+ # general formatting assertions
+ # -----------------------------
+
+ md = '_EMPHASIS LINE 1_'
+ html = '&lt;em&gt;EMPHASIS LINE 1&lt;/em&gt;'
+
+ # examples should have markdown and HTML in separate pre+code blocks
+ expected_example_1_regex = "<pre.*<code.*#{md}.*</code></pre>.*<pre.*<code.*#{html}.*</code></pre>"
+ expect(snapshot_spec_html_contents).to match(%r{#{expected_example_1_regex}}m)
+
+ # examples should have proper divs prepended for numbering, links, and styling
+ empty_div_for_example_class = '<div>'
+ examplenum_div = '<div><a href="#example-1">Example 1</a></div>'
+ expect(snapshot_spec_html_contents)
+ .to match(%r{#{empty_div_for_example_class}\n#{examplenum_div}.*#{expected_example_1_regex}.*}m)
end
end
# rubocop:enable RSpec/MultipleMemoizedHelpers
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 8e73073e68b..e083bb06241 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -211,7 +211,6 @@ RSpec.configure do |config|
end
config.before(:suite) do
- Timecop.safe_mode = true
TestEnv.init
# Reload all feature flags definitions
diff --git a/spec/views/projects/_flash_messages.html.haml_spec.rb b/spec/views/projects/_flash_messages.html.haml_spec.rb
index e1858229208..231aa12d920 100644
--- a/spec/views/projects/_flash_messages.html.haml_spec.rb
+++ b/spec/views/projects/_flash_messages.html.haml_spec.rb
@@ -12,10 +12,10 @@ RSpec.describe 'projects/_flash_messages' do
before do
allow(view).to receive(:current_user).and_return(user)
- allow(view).to receive(:can?).with(user, :download_code, project).and_return(true)
+ allow(view).to receive(:can?).with(user, :read_code, project).and_return(true)
end
- context 'when current_user has download_code permission' do
+ context 'when current_user has read_code permission' do
context 'when user has a terraform state' do
let_it_be(:project) { create(:project) }
let_it_be(:terraform_state) { create(:terraform_state, :locked, :with_version, project: project) }