Commit 7c670f05 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 7af887de 19a0c69e
......@@ -1056,7 +1056,6 @@ Rails/SaveBang:
- 'spec/models/note_spec.rb'
- 'spec/models/notification_setting_spec.rb'
- 'spec/models/operations/feature_flag_scope_spec.rb'
- 'spec/models/operations/feature_flag_spec.rb'
- 'spec/models/operations/feature_flags/strategy_spec.rb'
- 'spec/models/operations/feature_flags/user_list_spec.rb'
- 'spec/models/pages_domain_spec.rb'
......
# frozen_string_literal: true
module Resolvers
class BoardResolver < BaseResolver.single
alias_method :parent, :synchronized_object
type Types::BoardType, null: true
argument :id, GraphQL::ID_TYPE,
required: true,
description: 'The board\'s ID'
def resolve(id: nil)
return unless parent
::Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute(create_default_board: false).first
rescue ActiveRecord::RecordNotFound
nil
end
private
def extract_board_id(gid)
GitlabSchema.parse_gid(gid, expected_type: ::Board).model_id
end
end
end
......@@ -64,7 +64,7 @@ module Types
Types::BoardType,
null: true,
description: 'A single board of the group',
resolver: Resolvers::BoardsResolver.single
resolver: Resolvers::BoardResolver
field :label,
Types::LabelType,
......
......@@ -234,7 +234,7 @@ module Types
Types::BoardType,
null: true,
description: 'A single board of the project',
resolver: Resolvers::BoardsResolver.single
resolver: Resolvers::BoardResolver
field :jira_imports,
Types::JiraImportType.connection_type,
......
......@@ -34,9 +34,7 @@ module Git
def can_process_wiki_events?
# TODO: Support activity events for group wikis
# https://gitlab.com/gitlab-org/gitlab/-/issues/209306
return false unless wiki.is_a?(ProjectWiki)
Feature.enabled?(:wiki_events_on_git_push, wiki.container)
wiki.is_a?(ProjectWiki)
end
def push_changes
......
---
title: 'GraphQL: No longer allows to omit ID when querying for a single board.'
merge_request: 43627
author:
type: fixed
---
title: Enable wiki events on git push
merge_request: 43738
author:
type: added
---
name: wiki_events_on_git_push
introduced_by_url:
rollout_issue_url:
group:
type: development
default_enabled: false
......@@ -7125,9 +7125,9 @@ type Group {
"""
board(
"""
Find a board by its ID
The board's ID
"""
id: ID
id: ID!
): Board
"""
......@@ -12761,9 +12761,9 @@ type Project {
"""
board(
"""
Find a board by its ID
The board's ID
"""
id: ID
id: ID!
): Board
"""
......
......@@ -19764,11 +19764,15 @@
"args": [
{
"name": "id",
"description": "Find a board by its ID",
"description": "The board's ID",
"type": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
}
......@@ -37719,11 +37723,15 @@
"args": [
{
"name": "id",
"description": "Find a board by its ID",
"description": "The board's ID",
"type": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
}
......@@ -625,6 +625,13 @@ To get around this, you can [change the group path](../../group/index.md#changin
[change the project path](../../project/settings/index.md#renaming-a-repository) or change the branch
name.
You may also get a `404 Not Found` or `Unknown Manifest` message if you are using
a Docker Engine version earlier than 17.12. Later versions of Docker Engine use
[the v2 API](https://docs.docker.com/registry/spec/manifest-v2-2/).
The images in your GitLab Container Registry must also use the Docker v2 API.
For information on how to update your images, see the [Docker help](https://docs.docker.com/registry/spec/deprecated-schema-v1).
### Troubleshoot as a GitLab server admin
Troubleshooting the GitLab Container Registry, most of the times, requires
......
......@@ -163,48 +163,13 @@ Similar to versioned diff file views, you can see the changes made in a given Wi
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/14902) in **GitLab 12.10.**
> - Git events were [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216014) in **GitLab 13.0.**
> - It's enabled on GitLab.com.
> - Git access activity creation is managed by a feature flag.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-wiki-events-in-git). **(CORE ONLY)**
> - [Feature flag for Git events was removed](https://gitlab.com/gitlab-org/gitlab/-/issues/258665) in **GitLab 13.5**
Wiki events (creation, deletion, and updates) are tracked by GitLab and
displayed on the [user profile](../../profile/index.md#user-profile),
[group](../../group/index.md#view-group-activity),
and [project](../index.md#project-activity) activity pages.
### Enable or disable Wiki events in Git **(CORE ONLY)**
Tracking wiki events through Git is under development and not ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can enable it for your instance.
To enable it:
```ruby
Feature.enable(:wiki_events_on_git_push)
```
To enable for just a particular project:
```ruby
project = Project.find_by_full_path('your-group/your-project')
Feature.enable(:wiki_events_on_git_push, project)
```
To disable it:
```ruby
Feature.disable(:wiki_events_on_git_push)
```
To disable for just a particular project:
```ruby
project = Project.find_by_full_path('your-group/your-project')
Feature.disable(:wiki_events_on_git_push, project)
```
## Adding and editing wiki pages locally
Since wikis are based on Git repositories, you can clone them locally and edit
......
......@@ -41,10 +41,8 @@ export default {
receiveEpicsSuccess: RECEIVE_EPICS_SUCCESS,
}),
...mapActions(['setActiveIssueEpic']),
handleEdit(isEditing) {
if (isEditing) {
this.$refs.epicSelect.handleEditClick();
}
openEpicsDropdown() {
this.$refs.epicSelect.handleEditClick();
},
async setEpic(selectedEpic) {
this.loading = true;
......@@ -80,7 +78,7 @@ export default {
ref="sidebarItem"
:title="__('Epic')"
:loading="loading"
@changed="handleEdit"
@open="openEpicsDropdown"
>
<template v-if="storedEpic.title" #collapsed>
<a class="gl-text-gray-900! gl-font-weight-bold" href="#">
......
......@@ -40,6 +40,7 @@ describe('ee/boards/components/sidebar/board_sidebar_epic_select.vue', () => {
};
const findEpicSelect = () => wrapper.find({ ref: 'epicSelect' });
const findItemWrapper = () => wrapper.find({ ref: 'sidebarItem' });
const findCollapsed = () => wrapper.find('[data-testid="collapsed-content"]');
it('renders "None" when no epic is selected', () => {
......@@ -47,6 +48,13 @@ describe('ee/boards/components/sidebar/board_sidebar_epic_select.vue', () => {
expect(findCollapsed().text()).toBe('None');
});
it('expands the dropdown when editing', () => {
createWrapper();
wrapper.setMethods({ openEpicsDropdown: jest.fn() });
findItemWrapper().vm.$emit('open');
expect(wrapper.vm.openEpicsDropdown).toHaveBeenCalled();
});
describe('when epic is selected', () => {
beforeEach(async () => {
createWrapper();
......
......@@ -13,6 +13,7 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
create(:milestone,
project: project,
title: '12.3',
description: 'The 12.3 milestone',
start_date: Time.zone.parse('2018-12-10'),
due_date: Time.zone.parse('2019-01-10'))
end
......@@ -21,6 +22,7 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
create(:milestone,
project: project,
title: '12.4',
description: 'The 12.4 milestone',
start_date: Time.zone.parse('2019-01-10'),
due_date: Time.zone.parse('2019-02-10'))
end
......@@ -65,10 +67,26 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
create(:release_link,
release: release,
name: 'Runbook',
url: 'https://example.com/runbook',
url: "#{release.project.web_url}/runbook",
link_type: :runbook)
end
let_it_be(:package_link) do
create(:release_link,
release: release,
name: 'Package',
url: 'https://example.com/package',
link_type: :package)
end
let_it_be(:image_link) do
create(:release_link,
release: release,
name: 'Image',
url: 'https://example.com/image',
link_type: :image)
end
after(:all) do
remove_repository(project)
end
......
......@@ -3,12 +3,15 @@ import { mount } from '@vue/test-utils';
import { merge } from 'lodash';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { getJSONFixture } from 'helpers/fixtures';
import ReleaseEditNewApp from '~/releases/components/app_edit_new.vue';
import { release as originalRelease, milestones as originalMilestones } from '../mock_data';
import * as commonUtils from '~/lib/utils/common_utils';
import { BACK_URL_PARAM } from '~/releases/constants';
import AssetLinksForm from '~/releases/components/asset_links_form.vue';
const originalRelease = getJSONFixture('api/releases/release.json');
const originalMilestones = originalRelease.milestones;
describe('Release edit/new component', () => {
let wrapper;
let release;
......
......@@ -2,16 +2,12 @@ import { range as rge } from 'lodash';
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import waitForPromises from 'helpers/wait_for_promises';
import { getJSONFixture } from 'helpers/fixtures';
import ReleasesApp from '~/releases/components/app_index.vue';
import createStore from '~/releases/stores';
import createListModule from '~/releases/stores/modules/list';
import api from '~/api';
import {
pageInfoHeadersWithoutPagination,
pageInfoHeadersWithPagination,
release2 as release,
releases,
} from '../mock_data';
import { pageInfoHeadersWithoutPagination, pageInfoHeadersWithPagination } from '../mock_data';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import ReleasesPagination from '~/releases/components/releases_pagination.vue';
......@@ -25,6 +21,9 @@ jest.mock('~/lib/utils/common_utils', () => ({
const localVue = createLocalVue();
localVue.use(Vuex);
const release = getJSONFixture('api/releases/release.json');
const releases = [release];
describe('Releases App ', () => {
let wrapper;
let fetchReleaseSpy;
......
import Vuex from 'vuex';
import { shallowMount } from '@vue/test-utils';
import { getJSONFixture } from 'helpers/fixtures';
import ReleaseShowApp from '~/releases/components/app_show.vue';
import ReleaseSkeletonLoader from '~/releases/components/release_skeleton_loader.vue';
import { release as originalRelease } from '../mock_data';
import ReleaseBlock from '~/releases/components/release_block.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
const originalRelease = getJSONFixture('api/releases/release.json');
describe('Release show component', () => {
let wrapper;
let release;
......
import Vuex from 'vuex';
import { mount, createLocalVue } from '@vue/test-utils';
import { getJSONFixture } from 'helpers/fixtures';
import AssetLinksForm from '~/releases/components/asset_links_form.vue';
import { release as originalRelease } from '../mock_data';
import * as commonUtils from '~/lib/utils/common_utils';
import { ENTER_KEY } from '~/lib/utils/keys';
import { ASSET_LINK_TYPE, DEFAULT_ASSET_LINK_TYPE } from '~/releases/constants';
......@@ -9,6 +9,8 @@ import { ASSET_LINK_TYPE, DEFAULT_ASSET_LINK_TYPE } from '~/releases/constants';
const localVue = createLocalVue();
localVue.use(Vuex);
const originalRelease = getJSONFixture('api/releases/release.json');
describe('Release edit component', () => {
let wrapper;
let release;
......@@ -223,10 +225,18 @@ describe('Release edit component', () => {
});
});
it('selects the default asset type if no type was provided by the backend', () => {
const selected = wrapper.find({ ref: 'typeSelect' }).element.value;
describe('when no link type was provided by the backend', () => {
beforeEach(() => {
delete release.assets.links[0].linkType;
factory({ release });
});
it('selects the default asset type', () => {
const selected = wrapper.find({ ref: 'typeSelect' }).element.value;
expect(selected).toBe(DEFAULT_ASSET_LINK_TYPE);
expect(selected).toBe(DEFAULT_ASSET_LINK_TYPE);
});
});
});
......
import { mount } from '@vue/test-utils';
import { GlLink, GlIcon } from '@gitlab/ui';
import { getJSONFixture } from 'helpers/fixtures';
import { truncateSha } from '~/lib/utils/text_utility';
import { release as originalRelease } from '../mock_data';
import EvidenceBlock from '~/releases/components/evidence_block.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
const originalRelease = getJSONFixture('api/releases/release.json');
describe('Evidence Block', () => {
let wrapper;
let release;
......@@ -35,7 +37,7 @@ describe('Evidence Block', () => {
});
it('renders the title for the dowload link', () => {
expect(wrapper.find(GlLink).text()).toBe('v1.1.2-evidences-1.json');
expect(wrapper.find(GlLink).text()).toBe(`v1.1-evidences-1.json`);
});
it('renders the correct hover text for the download', () => {
......@@ -43,7 +45,7 @@ describe('Evidence Block', () => {
});
it('renders the correct file link for download', () => {
expect(wrapper.find(GlLink).attributes().download).toBe('v1.1.2-evidences-1.json');
expect(wrapper.find(GlLink).attributes().download).toBe(`v1.1-evidences-1.json`);
});
describe('sha text', () => {
......
import { mount } from '@vue/test-utils';
import { GlCollapse } from '@gitlab/ui';
import { trimText } from 'helpers/text_helper';
import { cloneDeep } from 'lodash';
import { getJSONFixture } from 'helpers/fixtures';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import ReleaseBlockAssets from '~/releases/components/release_block_assets.vue';
import { ASSET_LINK_TYPE } from '~/releases/constants';
import { assets } from '../mock_data';
const { assets } = getJSONFixture('api/releases/release.json');
describe('Release block assets', () => {
let wrapper;
......@@ -31,7 +33,7 @@ describe('Release block assets', () => {
wrapper.findAll('h5').filter(h5 => h5.text() === sections[type]);
beforeEach(() => {
defaultProps = { assets: cloneDeep(assets) };
defaultProps = { assets: convertObjectPropsToCamelCase(assets, { deep: true }) };
});
describe('with default props', () => {
......@@ -43,7 +45,7 @@ describe('Release block assets', () => {
const accordionButton = findAccordionButton();
expect(accordionButton.exists()).toBe(true);
expect(trimText(accordionButton.text())).toBe('Assets 5');
expect(trimText(accordionButton.text())).toBe('Assets 8');
});
it('renders the accordion as expanded by default', () => {
......
import { mount } from '@vue/test-utils';
import { GlLink, GlIcon } from '@gitlab/ui';
import { trimText } from 'helpers/text_helper';
import { getJSONFixture } from 'helpers/fixtures';
import { cloneDeep } from 'lodash';
import ReleaseBlockFooter from '~/releases/components/release_block_footer.vue';
import { release as originalRelease } from '../mock_data';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
const originalRelease = getJSONFixture('api/releases/release.json');
const mockFutureDate = new Date(9999, 0, 0).toISOString();
let mockIsFutureRelease = false;
......
import { shallowMount } from '@vue/test-utils';
import { merge } from 'lodash';
import { GlLink } from '@gitlab/ui';
import { getJSONFixture } from 'helpers/fixtures';
import ReleaseBlockHeader from '~/releases/components/release_block_header.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { release as originalRelease } from '../mock_data';
import { BACK_URL_PARAM } from '~/releases/constants';
const originalRelease = getJSONFixture('api/releases/release.json');
describe('Release block header', () => {
let wrapper;
let release;
......@@ -49,7 +51,7 @@ describe('Release block header', () => {
});
it('renders the title as text', () => {
expect(findHeader().text()).toBe(release.name);
expect(findHeader().text()).toContain(release.name);
expect(findHeaderLink().exists()).toBe(false);
});
});
......
import { mount } from '@vue/test-utils';
import { trimText } from 'helpers/text_helper';
import { getJSONFixture } from 'helpers/fixtures';
import { cloneDeep } from 'lodash';
import ReleaseBlockMetadata from '~/releases/components/release_block_metadata.vue';
import { release as originalRelease } from '../mock_data';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
const originalRelease = getJSONFixture('api/releases/release.json');
const mockFutureDate = new Date(9999, 0, 0).toISOString();
let mockIsFutureRelease = false;
......
import { mount } from '@vue/test-utils';
import { GlProgressBar, GlLink, GlBadge, GlButton } from '@gitlab/ui';
import { trimText } from 'helpers/text_helper';
import { getJSONFixture } from 'helpers/fixtures';
import ReleaseBlockMilestoneInfo from '~/releases/components/release_block_milestone_info.vue';
import { milestones as originalMilestones } from '../mock_data';
import { MAX_MILESTONES_TO_DISPLAY } from '~/releases/constants';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
const { milestones: originalMilestones } = getJSONFixture('api/releases/release.json');
describe('Release block milestone info', () => {
let wrapper;
let milestones;
......@@ -35,7 +37,7 @@ describe('Release block milestone info', () => {
beforeEach(() => factory({ milestones }));
it('renders the correct percentage', () => {
expect(milestoneProgressBarContainer().text()).toContain('41% complete');
expect(milestoneProgressBarContainer().text()).toContain('44% complete');
});
it('renders a progress bar that displays the correct percentage', () => {
......@@ -44,14 +46,24 @@ describe('Release block milestone info', () => {
expect(progressBar.exists()).toBe(true);
expect(progressBar.attributes()).toEqual(
expect.objectContaining({
value: '22',
max: '54',
value: '4',
max: '9',
}),
);
});
it('renders a list of links to all associated milestones', () => {
expect(trimText(milestoneListContainer().text())).toContain('Milestones 13.6 • 13.5');
// The API currently returns the milestones in a non-deterministic order,
// which causes the frontend fixture used by this test to return the
// milestones in one order locally and a different order in the CI pipeline.
// This is a bug and is tracked here: https://gitlab.com/gitlab-org/gitlab/-/issues/259012
// When this bug is fixed this expectation should be updated to
// assert the expected order.
const containerText = trimText(milestoneListContainer().text());
expect(
containerText.includes('Milestones 12.4 • 12.3') ||
containerText.includes('Milestones 12.3 • 12.4'),
).toBe(true);
milestones.forEach((m, i) => {
const milestoneLink = milestoneListContainer()
......@@ -65,7 +77,7 @@ describe('Release block milestone info', () => {
});
it('renders the "Issues" section with a total count of issues associated to the milestone(s)', () => {
const totalIssueCount = 54;
const totalIssueCount = 9;
const issuesContainerText = trimText(issuesContainer().text());
expect(issuesContainerText).toContain(`Issues ${totalIssueCount}`);
......@@ -73,7 +85,7 @@ describe('Release block milestone info', () => {
const badge = issuesContainer().find(GlBadge);
expect(badge.text()).toBe(totalIssueCount.toString());
expect(issuesContainerText).toContain('Open: 32 • Closed: 22');
expect(issuesContainerText).toContain('Open: 5 • Closed: 4');
});
});
......
import $ from 'jquery';
import { mount } from '@vue/test-utils';
import { GlIcon } from '@gitlab/ui';
import { getJSONFixture } from 'helpers/fixtures';
import EvidenceBlock from '~/releases/components/evidence_block.vue';
import ReleaseBlock from '~/releases/components/release_block.vue';
import ReleaseBlockFooter from '~/releases/components/release_block_footer.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { release as originalRelease } from '../mock_data';
import * as commonUtils from '~/lib/utils/common_utils';
import { BACK_URL_PARAM } from '~/releases/constants';
import * as urlUtility from '~/lib/utils/url_utility';
const originalRelease = getJSONFixture('api/releases/release.json');
describe('Release block', () => {
let wrapper;
let release;
......@@ -46,7 +48,7 @@ describe('Release block', () => {
beforeEach(() => factory(release));
it("renders the block with an id equal to the release's tag name", () => {
expect(wrapper.attributes().id).toBe('v0.3');
expect(wrapper.attributes().id).toBe(release.tagName);
});
it(`renders an edit button that links to the "Edit release" page with a "${BACK_URL_PARAM}" parameter`, () => {
......@@ -107,7 +109,7 @@ describe('Release block', () => {
});
it('does not render external label when link is not external', () => {
expect(wrapper.find('.js-assets-list li:nth-child(2) a').text()).not.toContain(
expect(wrapper.find('.js-assets-list li:nth-child(3) a').text()).not.toContain(
'external source',
);
});
......
import { ASSET_LINK_TYPE } from '~/releases/constants';
export const milestones = [
{
id: 50,
iid: 2,
project_id: 18,
title: '13.6',
description: 'The 13.6 milestone!',
state: 'active',
created_at: '2019-08-27T17:22:38.280Z',
updated_at: '2019-08-27T17:22:38.280Z',
due_date: '2019-09-19',
start_date: '2019-08-31',
web_url: 'http://0.0.0.0:3001/root/release-test/-/milestones/2',
issue_stats: {
total: 33,
closed: 19,
},
},
{
id: 49,
iid: 1,
project_id: 18,
title: '13.5',
description: 'The 13.5 milestone!',
state: 'active',
created_at: '2019-08-26T17:55:48.643Z',
updated_at: '2019-08-26T17:55:48.643Z',
due_date: '2019-10-11',
start_date: '2019-08-19',
web_url: 'http://0.0.0.0:3001/root/release-test/-/milestones/1',
issue_stats: {
total: 21,
closed: 3,
},
},
];
export const release = {
name: 'New release',
tag_name: 'v0.3',
tag_path: '/root/release-test/-/tags/v0.3',
description: 'A super nice release!',
description_html: '<p data-sourcepos="1:1-1:21" dir="auto">A super nice release!</p>',
created_at: '2019-08-26T17:54:04.952Z',
released_at: '2019-08-26T17:54:04.807Z',
author: {
id: 1,
name: 'Administrator',
username: 'root',
state: 'active',
avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
web_url: 'http://0.0.0.0:3001/root',
},
commit: {
id: 'c22b0728d1b465f82898c884d32b01aa642f96c1',
short_id: 'c22b0728',
created_at: '2019-08-26T17:47:07.000Z',
parent_ids: [],
title: 'Initial commit',
message: 'Initial commit',
author_name: 'Administrator',
author_email: 'admin@example.com',
authored_date: '2019-08-26T17:47:07.000Z',
committer_name: 'Administrator',
committer_email: 'admin@example.com',
committed_date: '2019-08-26T17:47:07.000Z',
},
commit_path: '/root/release-test/commit/c22b0728d1b465f82898c884d32b01aa642f96c1',
upcoming_release: false,
milestones,
evidences: [
{
filepath:
'https://20592.qa-tunnel.gitlab.info/root/test-deployments/-/releases/v1.1.2/evidences/1.json',
sha: 'fb3a125fd69a0e5048ebfb0ba43eb32ce4911520dd8d',
collected_at: '2018-10-19 15:43:20 +0200',
},
{
filepath:
'https://20592.qa-tunnel.gitlab.info/root/test-deployments/-/releases/v1.1.2/evidences/2.json',
sha: '6ebd17a66e6a861175735416e49cf677678029805712dd71bb805c609e2d9108',
collected_at: '2018-10-19 15:43:20 +0200',
},
{
filepath:
'https://20592.qa-tunnel.gitlab.info/root/test-deployments/-/releases/v1.1.2/evidences/3.json',
sha: '2f65beaf275c3cb4b4e24fb01d481cc475d69c957830833f15338384816b5cba',
collected_at: '2018-10-19 15:43:20 +0200',
},
],
assets: {
count: 5,
sources: [
{
format: 'zip',
url: 'http://0.0.0.0:3001/root/release-test/-/archive/v0.3/release-test-v0.3.zip',
},
{
format: 'tar.gz',
url: 'http://0.0.0.0:3001/root/release-test/-/archive/v0.3/release-test-v0.3.tar.gz',
},
{
format: 'tar.bz2',
url: 'http://0.0.0.0:3001/root/release-test/-/archive/v0.3/release-test-v0.3.tar.bz2',
},
{
format: 'tar',
url: 'http://0.0.0.0:3001/root/release-test/-/archive/v0.3/release-test-v0.3.tar',
},
],
links: [
{
id: 1,
name: 'my link',
url: 'https://google.com',
direct_asset_url: 'https://redirected.google.com',
external: true,
},
{
id: 2,
name: 'my second link',
url:
'https://gitlab.com/gitlab-org/gitlab-foss/-/jobs/artifacts/v11.6.0-rc4/download?job=rspec-mysql+41%2F50',
direct_asset_url: 'https://redirected.google.com',
external: false,
},
],
},
_links: {
self: 'http://0.0.0.0:3001/root/release-test/-/releases/v0.3',
edit_url: 'http://0.0.0.0:3001/root/release-test/-/releases/v0.3/edit',
},
};
export const pageInfoHeadersWithoutPagination = {
'X-NEXT-PAGE': '',
'X-PAGE': '1',
......@@ -152,77 +16,6 @@ export const pageInfoHeadersWithPagination = {
'X-TOTAL-PAGES': '2',
};
export const assets = {
count: 5,
sources: [
{
format: 'zip',
url: 'https://example.gitlab.com/path/to/zip',
},
],
links: [
{
linkType: ASSET_LINK_TYPE.IMAGE,
url: 'https://example.gitlab.com/path/to/image',
directAssetUrl: 'https://example.gitlab.com/path/to/image',
name: 'Example image link',
},
{
linkType: ASSET_LINK_TYPE.PACKAGE,
url: 'https://example.gitlab.com/path/to/package',
directAssetUrl: 'https://example.gitlab.com/path/to/package',
name: 'Example package link',
},
{
linkType: ASSET_LINK_TYPE.RUNBOOK,
url: 'https://example.gitlab.com/path/to/runbook',
directAssetUrl: 'https://example.gitlab.com/path/to/runbook',
name: 'Example runbook link',
},
{
linkType: ASSET_LINK_TYPE.OTHER,
url: 'https://example.gitlab.com/path/to/link',
directAssetUrl: 'https://example.gitlab.com/path/to/link',
name: 'Example link',
},
],
};
export const release2 = {
name: 'Bionic Beaver',
tag_name: '18.04',
description: '## changelog\n\n* line 1\n* line2',
description_html: '<div><h2>changelog</h2><ul><li>line1</li<li>line 2</li></ul></div>',
author_name: 'Release bot',
author_email: 'release-bot@example.com',
created_at: '2012-05-28T05:00:00-07:00',
commit: {
id: '2695effb5807a22ff3d138d593fd856244e155e7',
short_id: '2695effb',
title: 'Initial commit',
created_at: '2017-07-26T11:08:53.000+02:00',
parent_ids: ['2a4b78934375d7f53875269ffd4f45fd83a84ebe'],
message: 'Initial commit',
author: {
avatar_url: 'uploads/-/system/user/avatar/johndoe/avatar.png',
id: 482476,
name: 'John Doe',
path: '/johndoe',
state: 'active',
status_tooltip_html: null,
username: 'johndoe',
web_url: 'https://gitlab.com/johndoe',
},
authored_date: '2012-05-28T04:42:42-07:00',
committer_name: 'Jack Smith',
committer_email: 'jack@example.com',
committed_date: '2012-05-28T04:42:42-07:00',
},
assets,
};
export const releases = [release, release2];
export const graphqlReleasesResponse = {
data: {
project: {
......
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import { getJSONFixture } from 'helpers/fixtures';
import { cloneDeep } from 'lodash';
import * as actions from '~/releases/stores/modules/detail/actions';
import * as types from '~/releases/stores/modules/detail/mutation_types';
import { release as originalRelease } from '../../../mock_data';
import createState from '~/releases/stores/modules/detail/state';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
......@@ -21,6 +21,8 @@ jest.mock('~/lib/utils/url_utility', () => ({
joinPaths: jest.requireActual('~/lib/utils/url_utility').joinPaths,
}));
const originalRelease = getJSONFixture('api/releases/release.json');
describe('Release detail actions', () => {
let state;
let release;
......
import { getJSONFixture } from 'helpers/fixtures';
import createState from '~/releases/stores/modules/detail/state';
import mutations from '~/releases/stores/modules/detail/mutations';
import * as types from '~/releases/stores/modules/detail/mutation_types';
import { release as originalRelease } from '../../../mock_data';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { ASSET_LINK_TYPE, DEFAULT_ASSET_LINK_TYPE } from '~/releases/constants';
const originalRelease = getJSONFixture('api/releases/release.json');
describe('Release detail mutations', () => {
let state;
let release;
......
import { cloneDeep } from 'lodash';
import testAction from 'helpers/vuex_action_helper';
import { getJSONFixture } from 'helpers/fixtures';
import {
fetchReleases,
fetchReleasesGraphQl,
......@@ -17,12 +18,14 @@ import {
} from '~/lib/utils/common_utils';
import {
pageInfoHeadersWithoutPagination,
releases as originalReleases,
graphqlReleasesResponse as originalGraphqlReleasesResponse,
} from '../../../mock_data';
import allReleasesQuery from '~/releases/queries/all_releases.query.graphql';
import { PAGE_SIZE } from '~/releases/constants';
const originalRelease = getJSONFixture('api/releases/release.json');
const originalReleases = [originalRelease];
describe('Releases State actions', () => {
let mockedState;
let releases;
......
import { getJSONFixture } from 'helpers/fixtures';
import createState from '~/releases/stores/modules/list/state';
import mutations from '~/releases/stores/modules/list/mutations';
import * as types from '~/releases/stores/modules/list/mutation_types';
import { parseIntPagination } from '~/lib/utils/common_utils';
import {
pageInfoHeadersWithoutPagination,
releases,
graphqlReleasesResponse,
} from '../../../mock_data';
import { parseIntPagination, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { pageInfoHeadersWithoutPagination, graphqlReleasesResponse } from '../../../mock_data';
import { convertGraphQLResponse } from '~/releases/util';
const originalRelease = getJSONFixture('api/releases/release.json');
const originalReleases = [originalRelease];
describe('Releases Store Mutations', () => {
let stateCopy;
let restPageInfo;
let graphQlPageInfo;
let releases;
beforeEach(() => {
stateCopy = createState({});
restPageInfo = parseIntPagination(pageInfoHeadersWithoutPagination);
graphQlPageInfo = convertGraphQLResponse(graphqlReleasesResponse).paginationInfo;
releases = convertObjectPropsToCamelCase(originalReleases, { deep: true });
});
describe('REQUEST_RELEASES', () => {
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::BoardResolver do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
let(:dummy_gid) { 'gid://gitlab/Board/1' }
shared_examples_for 'group and project boards resolver' do
it 'does not create a default board' do
expect(resolve_board(id: dummy_gid)).to eq nil
end
it 'calls Boards::ListService' do
expect_next_instance_of(Boards::ListService) do |service|
expect(service).to receive(:execute).and_return([])
end
resolve_board(id: dummy_gid)
end
it 'requires an ID' do
expect do
resolve(described_class, obj: board_parent, args: {}, ctx: { current_user: user })
end.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
end
context 'when querying for a single board' do
let(:board1) { create(:board, name: 'One', resource_parent: board_parent) }
it 'returns specified board' do
expect(resolve_board(id: global_id_of(board1))).to eq board1
end
it 'returns nil if board not found' do
outside_parent = create(board_parent.class.underscore.to_sym) # rubocop:disable Rails/SaveBang
outside_board = create(:board, name: 'outside board', resource_parent: outside_parent)
expect(resolve_board(id: global_id_of(outside_board))).to eq nil
end
end
end
describe '#resolve' do
context 'when there is no parent' do
let(:board_parent) { nil }
it 'returns nil if parent is nil' do
expect(resolve_board(id: dummy_gid)).to eq(nil)
end
end
context 'when project boards' do
let(:board_parent) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
it_behaves_like 'group and project boards resolver'
end
context 'when group boards' do
let(:board_parent) { create(:group) }
it_behaves_like 'group and project boards resolver'
end
end
def resolve_board(id:)
resolve(described_class, obj: board_parent, args: { id: id }, ctx: { current_user: user })
end
end
......@@ -25,7 +25,7 @@ RSpec.describe Operations::FeatureFlag do
context 'a version 1 feature flag' do
it 'is valid if associated with Operations::FeatureFlagScope models' do
project = create(:project)
feature_flag = described_class.create({ name: 'test', project: project, version: 1,
feature_flag = described_class.create!({ name: 'test', project: project, version: 1,
scopes_attributes: [{ environment_scope: '*', active: false }] })
expect(feature_flag).to be_valid
......@@ -33,9 +33,10 @@ RSpec.describe Operations::FeatureFlag do
it 'is invalid if associated with Operations::FeatureFlags::Strategy models' do
project = create(:project)
feature_flag = described_class.create({ name: 'test', project: project, version: 1,
feature_flag = described_class.new({ name: 'test', project: project, version: 1,
strategies_attributes: [{ name: 'default', parameters: {} }] })
expect(feature_flag.valid?).to eq(false)
expect(feature_flag.errors.messages).to eq({
version_associations: ["version 1 feature flags may not have strategies"]
})
......@@ -45,9 +46,10 @@ RSpec.describe Operations::FeatureFlag do
context 'a version 2 feature flag' do
it 'is invalid if associated with Operations::FeatureFlagScope models' do
project = create(:project)
feature_flag = described_class.create({ name: 'test', project: project, version: 2,
feature_flag = described_class.new({ name: 'test', project: project, version: 2,
scopes_attributes: [{ environment_scope: '*', active: false }] })
expect(feature_flag.valid?).to eq(false)
expect(feature_flag.errors.messages).to eq({
version_associations: ["version 2 feature flags may not have scopes"]
})
......@@ -55,7 +57,7 @@ RSpec.describe Operations::FeatureFlag do
it 'is valid if associated with Operations::FeatureFlags::Strategy models' do
project = create(:project)
feature_flag = described_class.create({ name: 'test', project: project, version: 2,
feature_flag = described_class.create!({ name: 'test', project: project, version: 2,
strategies_attributes: [{ name: 'default', parameters: {} }] })
expect(feature_flag).to be_valid
......@@ -75,7 +77,7 @@ RSpec.describe Operations::FeatureFlag do
it 'defaults to 1 if unspecified' do
project = create(:project)
feature_flag = described_class.create(name: 'my_flag', project: project, active: true)
feature_flag = described_class.create!(name: 'my_flag', project: project, active: true)
expect(feature_flag).to be_valid
expect(feature_flag.version_before_type_cast).to eq(1)
......@@ -113,14 +115,14 @@ RSpec.describe Operations::FeatureFlag do
context 'with a version 1 feature flag' do
it 'creates a default scope' do
feature_flag = described_class.create({ name: 'test', project: project, scopes_attributes: [], version: 1 })
feature_flag = described_class.create!({ name: 'test', project: project, scopes_attributes: [], version: 1 })
expect(feature_flag.scopes.count).to eq(1)
expect(feature_flag.scopes.first.environment_scope).to eq('*')
end
it 'allows specifying the default scope in the parameters' do
feature_flag = described_class.create({ name: 'test', project: project,
feature_flag = described_class.create!({ name: 'test', project: project,
scopes_attributes: [{ environment_scope: '*', active: false },
{ environment_scope: 'review/*', active: true }], version: 1 })
......@@ -131,7 +133,7 @@ RSpec.describe Operations::FeatureFlag do
context 'with a version 2 feature flag' do
it 'does not create a default scope' do
feature_flag = described_class.create({ name: 'test', project: project, scopes_attributes: [], version: 2 })
feature_flag = described_class.create!({ name: 'test', project: project, scopes_attributes: [], version: 2 })
expect(feature_flag.scopes).to eq([])
end
......
......@@ -254,24 +254,6 @@ RSpec.describe Git::WikiPushService, services: true do
service.execute
end
end
context 'the wiki_events_on_git_push feature is disabled' do
before do
stub_feature_flags(wiki_events_on_git_push: false)
end
it_behaves_like 'a no-op push'
context 'but is enabled for a given container' do
before do
stub_feature_flags(wiki_events_on_git_push: wiki.container)
end
it 'creates events' do
expect { process_changes { write_new_page } }.to change(Event, :count).by(1)
end
end
end
end
end
......
......@@ -90,7 +90,7 @@ RSpec.shared_examples 'group and project boards query' do
it_behaves_like 'a working graphql query' do
before do
post_graphql(query_single_board, current_user: current_user)
post_graphql(query_single_board("id: \"gid://gitlab/Board/1\""), current_user: current_user)
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment