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>2021-11-15 21:12:21 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-11-15 21:12:21 +0300
commit4481a56a94c579f52e1cdef1cc1f4995f0ee1412 (patch)
tree9b8d431d53057e6351dd56718d212b2c89b19312 /spec
parente5c7d631a84940c66e46f1824ba7ce0c7f1d0ea4 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/issues/user_uses_quick_actions_spec.rb1
-rw-r--r--spec/features/projects/cluster_agents_spec.rb2
-rw-r--r--spec/frontend/clusters_list/components/agents_spec.js35
-rw-r--r--spec/frontend/clusters_list/components/clusters_empty_state_spec.js39
-rw-r--r--spec/frontend/clusters_list/components/clusters_main_view_spec.js34
-rw-r--r--spec/frontend/clusters_list/components/clusters_spec.js18
-rw-r--r--spec/frontend/clusters_list/components/clusters_view_all_spec.js243
-rw-r--r--spec/frontend/clusters_list/components/install_agent_modal_spec.js2
-rw-r--r--spec/frontend/clusters_list/mocks/apollo.js3
-rw-r--r--spec/frontend/clusters_list/store/mutations_spec.js8
-rw-r--r--spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb22
-rw-r--r--spec/graphql/types/ci/pipeline_scope_enum_spec.rb11
-rw-r--r--spec/graphql/types/ci/pipeline_status_enum_spec.rb11
-rw-r--r--spec/initializers/100_patch_omniauth_oauth2_spec.rb45
-rw-r--r--spec/models/ci/trigger_spec.rb4
-rw-r--r--spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb40
16 files changed, 461 insertions, 57 deletions
diff --git a/spec/features/issues/user_uses_quick_actions_spec.rb b/spec/features/issues/user_uses_quick_actions_spec.rb
index d88b816b186..c6d743ed38f 100644
--- a/spec/features/issues/user_uses_quick_actions_spec.rb
+++ b/spec/features/issues/user_uses_quick_actions_spec.rb
@@ -44,5 +44,6 @@ RSpec.describe 'Issues > User uses quick actions', :js do
it_behaves_like 'move quick action'
it_behaves_like 'zoom quick actions'
it_behaves_like 'clone quick action'
+ it_behaves_like 'promote_to_incident quick action'
end
end
diff --git a/spec/features/projects/cluster_agents_spec.rb b/spec/features/projects/cluster_agents_spec.rb
index 98ee6ca161f..3ef710169f0 100644
--- a/spec/features/projects/cluster_agents_spec.rb
+++ b/spec/features/projects/cluster_agents_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe 'ClusterAgents', :js do
end
it 'displays empty state', :aggregate_failures do
- expect(page).to have_content('Connect with a GitLab Agent')
+ expect(page).to have_content('Install new Agent')
expect(page).to have_selector('.empty-state')
end
end
diff --git a/spec/frontend/clusters_list/components/agents_spec.js b/spec/frontend/clusters_list/components/agents_spec.js
index 3bd163c74eb..2dec7cdc973 100644
--- a/spec/frontend/clusters_list/components/agents_spec.js
+++ b/spec/frontend/clusters_list/components/agents_spec.js
@@ -14,7 +14,7 @@ localVue.use(VueApollo);
describe('Agents', () => {
let wrapper;
- const propsData = {
+ const defaultProps = {
defaultBranchName: 'default',
};
const provideData = {
@@ -22,12 +22,12 @@ describe('Agents', () => {
kasAddress: 'kas.example.com',
};
- const createWrapper = ({ agents = [], pageInfo = null, trees = [] }) => {
+ const createWrapper = ({ props = {}, agents = [], pageInfo = null, trees = [], count = 0 }) => {
const provide = provideData;
const apolloQueryResponse = {
data: {
project: {
- clusterAgents: { nodes: agents, pageInfo, tokens: { nodes: [] } },
+ clusterAgents: { nodes: agents, pageInfo, tokens: { nodes: [] }, count },
repository: { tree: { trees: { nodes: trees, pageInfo } } },
},
},
@@ -40,7 +40,10 @@ describe('Agents', () => {
wrapper = shallowMount(Agents, {
localVue,
apolloProvider,
- propsData,
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
provide: provideData,
});
@@ -80,6 +83,8 @@ describe('Agents', () => {
},
];
+ const count = 2;
+
const trees = [
{
name: 'agent-2',
@@ -120,7 +125,7 @@ describe('Agents', () => {
];
beforeEach(() => {
- return createWrapper({ agents, trees });
+ return createWrapper({ agents, count, trees });
});
it('should render agent table', () => {
@@ -132,6 +137,10 @@ describe('Agents', () => {
expect(findAgentTable().props('agents')).toMatchObject(expectedAgentsList);
});
+ it('should emit agents count to the parent component', () => {
+ expect(wrapper.emitted().onAgentsLoad).toEqual([[count]]);
+ });
+
describe('when the agent has recently connected tokens', () => {
it('should set agent status to active', () => {
expect(findAgentTable().props('agents')).toMatchObject(expectedAgentsList);
@@ -179,6 +188,20 @@ describe('Agents', () => {
it('should pass pageInfo to the pagination component', () => {
expect(findPaginationButtons().props()).toMatchObject(pageInfo);
});
+
+ describe('when limit is passed from the parent component', () => {
+ beforeEach(() => {
+ return createWrapper({
+ props: { limit: 6 },
+ agents,
+ pageInfo,
+ });
+ });
+
+ it('should not render pagination buttons', () => {
+ expect(findPaginationButtons().exists()).toBe(false);
+ });
+ });
});
});
@@ -235,7 +258,7 @@ describe('Agents', () => {
beforeEach(() => {
wrapper = shallowMount(Agents, {
mocks,
- propsData,
+ propsData: defaultProps,
provide: provideData,
});
diff --git a/spec/frontend/clusters_list/components/clusters_empty_state_spec.js b/spec/frontend/clusters_list/components/clusters_empty_state_spec.js
index bda0e6c110c..f7e1791d0f7 100644
--- a/spec/frontend/clusters_list/components/clusters_empty_state_spec.js
+++ b/spec/frontend/clusters_list/components/clusters_empty_state_spec.js
@@ -11,6 +11,10 @@ const canAddCluster = true;
describe('ClustersEmptyStateComponent', () => {
let wrapper;
+ const propsData = {
+ isChildComponent: false,
+ };
+
const provideData = {
clustersEmptyStateImage,
emptyStateHelpText: null,
@@ -27,6 +31,7 @@ describe('ClustersEmptyStateComponent', () => {
beforeEach(() => {
wrapper = shallowMountExtended(ClustersEmptyState, {
store: ClusterStore(entryData),
+ propsData,
provide: provideData,
stubs: { GlEmptyState },
});
@@ -36,8 +41,10 @@ describe('ClustersEmptyStateComponent', () => {
wrapper.destroy();
});
- it('should render the action button', () => {
- expect(findButton().exists()).toBe(true);
+ describe('when the component is loaded independently', () => {
+ it('should render the action button', () => {
+ expect(findButton().exists()).toBe(true);
+ });
});
describe('when the help text is not provided', () => {
@@ -46,11 +53,31 @@ describe('ClustersEmptyStateComponent', () => {
});
});
+ describe('when the component is loaded as a child component', () => {
+ beforeEach(() => {
+ propsData.isChildComponent = true;
+ wrapper = shallowMountExtended(ClustersEmptyState, {
+ store: ClusterStore(entryData),
+ propsData,
+ provide: provideData,
+ });
+ });
+
+ afterEach(() => {
+ propsData.isChildComponent = false;
+ });
+
+ it('should not render the action button', () => {
+ expect(findButton().exists()).toBe(false);
+ });
+ });
+
describe('when the help text is provided', () => {
beforeEach(() => {
provideData.emptyStateHelpText = emptyStateHelpText;
wrapper = shallowMountExtended(ClustersEmptyState, {
store: ClusterStore(entryData),
+ propsData,
provide: provideData,
});
});
@@ -61,8 +88,14 @@ describe('ClustersEmptyStateComponent', () => {
});
describe('when the user cannot add clusters', () => {
+ entryData.canAddCluster = false;
beforeEach(() => {
- wrapper.vm.$store.state.canAddCluster = false;
+ wrapper = shallowMountExtended(ClustersEmptyState, {
+ store: ClusterStore(entryData),
+ propsData,
+ provide: provideData,
+ stubs: { GlEmptyState },
+ });
});
it('should disable the button', () => {
expect(findButton().props('disabled')).toBe(true);
diff --git a/spec/frontend/clusters_list/components/clusters_main_view_spec.js b/spec/frontend/clusters_list/components/clusters_main_view_spec.js
index 3869a2a8736..c2233e5d39c 100644
--- a/spec/frontend/clusters_list/components/clusters_main_view_spec.js
+++ b/spec/frontend/clusters_list/components/clusters_main_view_spec.js
@@ -1,7 +1,14 @@
import { GlTabs, GlTab } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ClustersMainView from '~/clusters_list/components/clusters_main_view.vue';
-import { CLUSTERS_TABS } from '~/clusters_list/constants';
+import InstallAgentModal from '~/clusters_list/components/install_agent_modal.vue';
+import {
+ AGENT,
+ CERTIFICATE_BASED,
+ CLUSTERS_TABS,
+ MAX_CLUSTERS_LIST,
+ MAX_LIST_COUNT,
+} from '~/clusters_list/constants';
const defaultBranchName = 'default-branch';
@@ -26,6 +33,7 @@ describe('ClustersMainViewComponent', () => {
const findAllTabs = () => wrapper.findAllComponents(GlTab);
const findGlTabAtIndex = (index) => findAllTabs().at(index);
const findComponent = () => wrapper.findByTestId('clusters-tab-component');
+ const findModal = () => wrapper.findComponent(InstallAgentModal);
it('renders `GlTabs` with `syncActiveTabWithQueryParams` and `queryParamName` props set', () => {
expect(findTabs().exists()).toBe(true);
@@ -40,11 +48,16 @@ describe('ClustersMainViewComponent', () => {
expect(findComponent().props('defaultBranchName')).toBe(defaultBranchName);
});
+ it('passes correct max-agents param to the modal', () => {
+ expect(findModal().props('maxAgents')).toBe(MAX_CLUSTERS_LIST);
+ });
+
describe('tabs', () => {
it.each`
- tabTitle | queryParamValue | lineNumber
- ${'Agent'} | ${'agent'} | ${0}
- ${'Certificate based'} | ${'certificate_based'} | ${1}
+ tabTitle | queryParamValue | lineNumber
+ ${'All'} | ${'all'} | ${0}
+ ${'Agent'} | ${AGENT} | ${1}
+ ${'Certificate based'} | ${CERTIFICATE_BASED} | ${2}
`(
'renders correct tab title and query param value',
({ tabTitle, queryParamValue, lineNumber }) => {
@@ -53,4 +66,17 @@ describe('ClustersMainViewComponent', () => {
},
);
});
+
+ describe('when the child component emits the tab change event', () => {
+ beforeEach(() => {
+ findComponent().vm.$emit('changeTab', AGENT);
+ });
+ it('changes the tab', () => {
+ expect(findTabs().attributes('value')).toBe('1');
+ });
+
+ it('passes correct max-agents param to the modal', () => {
+ expect(findModal().props('maxAgents')).toBe(MAX_LIST_COUNT);
+ });
+ });
});
diff --git a/spec/frontend/clusters_list/components/clusters_spec.js b/spec/frontend/clusters_list/components/clusters_spec.js
index de511788b08..a34202c789d 100644
--- a/spec/frontend/clusters_list/components/clusters_spec.js
+++ b/spec/frontend/clusters_list/components/clusters_spec.js
@@ -48,9 +48,9 @@ describe('Clusters', () => {
mock.onGet(`${endpoint}?page=${header['x-page']}`).reply(response, body, header);
};
- const mountWrapper = () => {
+ const createWrapper = ({ propsData = {} }) => {
store = ClusterStore(entryData);
- wrapper = mount(Clusters, { provide: provideData, store, stubs: { GlTable } });
+ wrapper = mount(Clusters, { propsData, provide: provideData, store, stubs: { GlTable } });
return axios.waitForAll();
};
@@ -70,7 +70,7 @@ describe('Clusters', () => {
mock = new MockAdapter(axios);
mockPollingApi(200, apiData, paginationHeader());
- return mountWrapper();
+ return createWrapper({});
});
afterEach(() => {
@@ -105,6 +105,16 @@ describe('Clusters', () => {
expect(findEmptyState().exists()).toBe(true);
});
});
+
+ describe('when is loaded as a child component', () => {
+ beforeEach(() => {
+ createWrapper({ limit: 6 });
+ });
+
+ it("shouldn't render pagination buttons", () => {
+ expect(findPaginatedButtons().exists()).toBe(false);
+ });
+ });
});
describe('cluster icon', () => {
@@ -248,7 +258,7 @@ describe('Clusters', () => {
beforeEach(() => {
mockPollingApi(200, apiData, paginationHeader(totalFirstPage, perPage, 1));
- return mountWrapper();
+ return createWrapper({});
});
it('should load to page 1 with header values', () => {
diff --git a/spec/frontend/clusters_list/components/clusters_view_all_spec.js b/spec/frontend/clusters_list/components/clusters_view_all_spec.js
new file mode 100644
index 00000000000..6ef56beddee
--- /dev/null
+++ b/spec/frontend/clusters_list/components/clusters_view_all_spec.js
@@ -0,0 +1,243 @@
+import { GlCard, GlLoadingIcon, GlButton, GlSprintf, GlBadge } from '@gitlab/ui';
+import { createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import ClustersViewAll from '~/clusters_list/components/clusters_view_all.vue';
+import Agents from '~/clusters_list/components/agents.vue';
+import Clusters from '~/clusters_list/components/clusters.vue';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import {
+ AGENT,
+ CERTIFICATE_BASED,
+ AGENT_CARD_INFO,
+ CERTIFICATE_BASED_CARD_INFO,
+ MAX_CLUSTERS_LIST,
+ INSTALL_AGENT_MODAL_ID,
+} from '~/clusters_list/constants';
+import { sprintf } from '~/locale';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+const addClusterPath = '/path/to/add/cluster';
+const defaultBranchName = 'default-branch';
+
+describe('ClustersViewAllComponent', () => {
+ let wrapper;
+
+ const event = {
+ preventDefault: jest.fn(),
+ };
+
+ const propsData = {
+ defaultBranchName,
+ };
+
+ const provideData = {
+ addClusterPath,
+ };
+
+ const entryData = {
+ loadingClusters: false,
+ totalClusters: 0,
+ };
+
+ const findCards = () => wrapper.findAllComponents(GlCard);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findAgentsComponent = () => wrapper.findComponent(Agents);
+ const findClustersComponent = () => wrapper.findComponent(Clusters);
+ const findCardsContainer = () => wrapper.findByTestId('clusters-cards-container');
+ const findAgentCardTitle = () => wrapper.findByTestId('agent-card-title');
+ const findRecommendedBadge = () => wrapper.findComponent(GlBadge);
+ const findClustersCardTitle = () => wrapper.findByTestId('clusters-card-title');
+ const findFooterButton = (line) => findCards().at(line).findComponent(GlButton);
+
+ const createStore = (initialState) =>
+ new Vuex.Store({
+ state: initialState,
+ });
+
+ const createWrapper = ({ initialState }) => {
+ wrapper = shallowMountExtended(ClustersViewAll, {
+ localVue,
+ store: createStore(initialState),
+ propsData,
+ provide: provideData,
+ directives: {
+ GlModalDirective: createMockDirective(),
+ },
+ stubs: { GlCard, GlSprintf },
+ });
+ };
+
+ beforeEach(() => {
+ createWrapper({ initialState: entryData });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when agents and clusters are not loaded', () => {
+ const initialState = {
+ loadingClusters: true,
+ totalClusters: 0,
+ };
+ beforeEach(() => {
+ createWrapper({ initialState });
+ });
+
+ it('should show the loading icon', () => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+ });
+
+ describe('when both agents and clusters are loaded', () => {
+ beforeEach(() => {
+ findAgentsComponent().vm.$emit('onAgentsLoad', 6);
+ });
+
+ it("shouldn't show the loading icon", () => {
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+
+ it('should make content visible', () => {
+ expect(findCardsContainer().isVisible()).toBe(true);
+ });
+
+ it('should render 2 cards', () => {
+ expect(findCards().length).toBe(2);
+ });
+ });
+
+ describe('agents card', () => {
+ it('should show recommended badge', () => {
+ expect(findRecommendedBadge().exists()).toBe(true);
+ });
+
+ it('should render Agents component', () => {
+ expect(findAgentsComponent().exists()).toBe(true);
+ });
+
+ it('should pass the limit prop', () => {
+ expect(findAgentsComponent().props('limit')).toBe(MAX_CLUSTERS_LIST);
+ });
+
+ it('should pass the default-branch-name prop', () => {
+ expect(findAgentsComponent().props('defaultBranchName')).toBe(defaultBranchName);
+ });
+
+ describe('when there are no agents', () => {
+ it('should show the empty title', () => {
+ expect(findAgentCardTitle().text()).toBe(AGENT_CARD_INFO.emptyTitle);
+ });
+
+ it('should show install new Agent button in the footer', () => {
+ expect(findFooterButton(0).exists()).toBe(true);
+ });
+
+ it('should render correct modal id for the agent link', () => {
+ const binding = getBinding(findFooterButton(0).element, 'gl-modal-directive');
+
+ expect(binding.value).toBe(INSTALL_AGENT_MODAL_ID);
+ });
+ });
+
+ describe('when the agents are present', () => {
+ const findFooterLink = () => wrapper.findByTestId('agents-tab-footer-link');
+ const agentsNumber = 7;
+
+ beforeEach(() => {
+ findAgentsComponent().vm.$emit('onAgentsLoad', agentsNumber);
+ });
+
+ it('should show the correct title', () => {
+ expect(findAgentCardTitle().text()).toBe(
+ sprintf(AGENT_CARD_INFO.title, { number: MAX_CLUSTERS_LIST, total: agentsNumber }),
+ );
+ });
+
+ it('should show the link to the Agents tab in the footer', () => {
+ expect(findFooterLink().exists()).toBe(true);
+ expect(findFooterLink().text()).toBe(
+ sprintf(AGENT_CARD_INFO.footerText, { number: agentsNumber }),
+ );
+ expect(findFooterLink().attributes('href')).toBe(`?tab=${AGENT}`);
+ });
+
+ describe('when clicking on the footer link', () => {
+ beforeEach(() => {
+ findFooterLink().vm.$emit('click', event);
+ });
+
+ it('should trigger tab change', () => {
+ expect(wrapper.emitted('changeTab')).toEqual([[AGENT]]);
+ });
+ });
+ });
+ });
+
+ describe('clusters tab', () => {
+ it('should pass the limit prop', () => {
+ expect(findClustersComponent().props('limit')).toBe(MAX_CLUSTERS_LIST);
+ });
+
+ it('should pass the is-child-component prop', () => {
+ expect(findClustersComponent().props('isChildComponent')).toBe(true);
+ });
+
+ describe('when there are no clusters', () => {
+ it('should show the empty title', () => {
+ expect(findClustersCardTitle().text()).toBe(CERTIFICATE_BASED_CARD_INFO.emptyTitle);
+ });
+
+ it('should show install new Agent button in the footer', () => {
+ expect(findFooterButton(1).exists()).toBe(true);
+ });
+
+ it('should render correct href for the button in the footer', () => {
+ expect(findFooterButton(1).attributes('href')).toBe(addClusterPath);
+ });
+ });
+
+ describe('when the clusters are present', () => {
+ const findFooterLink = () => wrapper.findByTestId('clusters-tab-footer-link');
+
+ const clustersNumber = 7;
+ const initialState = {
+ loadingClusters: false,
+ totalClusters: clustersNumber,
+ };
+
+ beforeEach(() => {
+ createWrapper({ initialState });
+ });
+
+ it('should show the correct title', () => {
+ expect(findClustersCardTitle().text()).toBe(
+ sprintf(CERTIFICATE_BASED_CARD_INFO.title, {
+ number: MAX_CLUSTERS_LIST,
+ total: clustersNumber,
+ }),
+ );
+ });
+
+ it('should show the link to the Clusters tab in the footer', () => {
+ expect(findFooterLink().exists()).toBe(true);
+ expect(findFooterLink().text()).toBe(
+ sprintf(CERTIFICATE_BASED_CARD_INFO.footerText, { number: clustersNumber }),
+ );
+ });
+
+ describe('when clicking on the footer link', () => {
+ beforeEach(() => {
+ findFooterLink().vm.$emit('click', event);
+ });
+
+ it('should trigger tab change', () => {
+ expect(wrapper.emitted('changeTab')).toEqual([[CERTIFICATE_BASED]]);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/clusters_list/components/install_agent_modal_spec.js b/spec/frontend/clusters_list/components/install_agent_modal_spec.js
index 89c99bb260e..6c2ea45b99b 100644
--- a/spec/frontend/clusters_list/components/install_agent_modal_spec.js
+++ b/spec/frontend/clusters_list/components/install_agent_modal_spec.js
@@ -24,6 +24,7 @@ localVue.use(VueApollo);
const projectPath = 'path/to/project';
const defaultBranchName = 'default';
+const maxAgents = MAX_LIST_COUNT;
describe('InstallAgentModal', () => {
let wrapper;
@@ -56,6 +57,7 @@ describe('InstallAgentModal', () => {
const propsData = {
defaultBranchName,
+ maxAgents,
};
wrapper = shallowMount(InstallAgentModal, {
diff --git a/spec/frontend/clusters_list/mocks/apollo.js b/spec/frontend/clusters_list/mocks/apollo.js
index eeed494883f..1a7ef84a6d9 100644
--- a/spec/frontend/clusters_list/mocks/apollo.js
+++ b/spec/frontend/clusters_list/mocks/apollo.js
@@ -16,6 +16,7 @@ const pageInfo = {
hasPreviousPage: false,
startCursor: '',
};
+const count = 1;
export const createAgentResponse = {
data: {
@@ -64,7 +65,7 @@ export const createAgentTokenErrorResponse = {
export const getAgentResponse = {
data: {
project: {
- clusterAgents: { nodes: [{ ...agent, tokens }], pageInfo },
+ clusterAgents: { nodes: [{ ...agent, tokens }], pageInfo, count },
repository: {
tree: {
trees: { nodes: [{ ...agent, path: null }], pageInfo },
diff --git a/spec/frontend/clusters_list/store/mutations_spec.js b/spec/frontend/clusters_list/store/mutations_spec.js
index f8723bfcdfc..ae264eee449 100644
--- a/spec/frontend/clusters_list/store/mutations_spec.js
+++ b/spec/frontend/clusters_list/store/mutations_spec.js
@@ -57,4 +57,12 @@ describe('Admin statistics panel mutations', () => {
expect(state.page).toBe(123);
});
});
+
+ describe(`${types.SET_CLUSTERS_PER_PAGE}`, () => {
+ it('changes clustersPerPage value', () => {
+ mutations[types.SET_CLUSTERS_PER_PAGE](state, 123);
+
+ expect(state.clustersPerPage).toBe(123);
+ });
+ });
});
diff --git a/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb b/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
index 865e892b12d..3fcfa967452 100644
--- a/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
+++ b/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
@@ -20,23 +20,37 @@ RSpec.describe ResolvesPipelines do
let_it_be(:project) { create(:project, :private) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
let_it_be(:failed_pipeline) { create(:ci_pipeline, :failed, project: project) }
+ let_it_be(:success_pipeline) { create(:ci_pipeline, :success, project: project) }
let_it_be(:ref_pipeline) { create(:ci_pipeline, project: project, ref: 'awesome-feature') }
let_it_be(:sha_pipeline) { create(:ci_pipeline, project: project, sha: 'deadbeef') }
+ let_it_be(:all_pipelines) do
+ [
+ pipeline,
+ failed_pipeline,
+ success_pipeline,
+ ref_pipeline,
+ sha_pipeline
+ ]
+ end
before do
project.add_developer(current_user)
end
- it { is_expected.to have_graphql_arguments(:status, :ref, :sha, :source) }
+ it { is_expected.to have_graphql_arguments(:status, :scope, :ref, :sha, :source) }
it 'finds all pipelines' do
- expect(resolve_pipelines).to contain_exactly(pipeline, failed_pipeline, ref_pipeline, sha_pipeline)
+ expect(resolve_pipelines).to contain_exactly(*all_pipelines)
end
it 'allows filtering by status' do
expect(resolve_pipelines(status: 'failed')).to contain_exactly(failed_pipeline)
end
+ it 'allows filtering by scope' do
+ expect(resolve_pipelines(scope: 'finished')).to contain_exactly(failed_pipeline, success_pipeline)
+ end
+
it 'allows filtering by ref' do
expect(resolve_pipelines(ref: 'awesome-feature')).to contain_exactly(ref_pipeline)
end
@@ -54,7 +68,7 @@ RSpec.describe ResolvesPipelines do
end
it 'does not filter by source' do
- expect(resolve_pipelines(source: 'web')).to contain_exactly(pipeline, failed_pipeline, ref_pipeline, sha_pipeline, source_pipeline)
+ expect(resolve_pipelines(source: 'web')).to contain_exactly(*all_pipelines, source_pipeline)
end
end
@@ -64,7 +78,7 @@ RSpec.describe ResolvesPipelines do
end
it 'returns all the pipelines' do
- expect(resolve_pipelines).to contain_exactly(pipeline, failed_pipeline, ref_pipeline, sha_pipeline, source_pipeline)
+ expect(resolve_pipelines).to contain_exactly(*all_pipelines, source_pipeline)
end
end
end
diff --git a/spec/graphql/types/ci/pipeline_scope_enum_spec.rb b/spec/graphql/types/ci/pipeline_scope_enum_spec.rb
new file mode 100644
index 00000000000..9dc6e5c6fae
--- /dev/null
+++ b/spec/graphql/types/ci/pipeline_scope_enum_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::PipelineScopeEnum do
+ it 'exposes all pipeline scopes' do
+ expect(described_class.values.keys).to contain_exactly(
+ *::Ci::PipelinesFinder::ALLOWED_SCOPES.keys.map(&:to_s)
+ )
+ end
+end
diff --git a/spec/graphql/types/ci/pipeline_status_enum_spec.rb b/spec/graphql/types/ci/pipeline_status_enum_spec.rb
new file mode 100644
index 00000000000..2d6683c6384
--- /dev/null
+++ b/spec/graphql/types/ci/pipeline_status_enum_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::PipelineStatusEnum do
+ it 'exposes all pipeline states' do
+ expect(described_class.values.keys).to contain_exactly(
+ *::Ci::Pipeline.all_state_names.map(&:to_s).map(&:upcase)
+ )
+ end
+end
diff --git a/spec/initializers/100_patch_omniauth_oauth2_spec.rb b/spec/initializers/100_patch_omniauth_oauth2_spec.rb
index 0c436e4ef45..c30a1cdeafa 100644
--- a/spec/initializers/100_patch_omniauth_oauth2_spec.rb
+++ b/spec/initializers/100_patch_omniauth_oauth2_spec.rb
@@ -2,12 +2,10 @@
require 'spec_helper'
-RSpec.describe 'OmniAuth::Strategies::OAuth2', type: :strategy do
- let(:strategy) { [OmniAuth::Strategies::OAuth2] }
-
+RSpec.describe 'OmniAuth::Strategies::OAuth2' do
it 'verifies the gem version' do
current_version = OmniAuth::OAuth2::VERSION
- expected_version = '1.7.1'
+ expected_version = '1.7.2'
expect(current_version).to eq(expected_version), <<~EOF
New version #{current_version} of the `omniauth-oauth2` gem detected!
@@ -18,39 +16,18 @@ RSpec.describe 'OmniAuth::Strategies::OAuth2', type: :strategy do
EOF
end
- context 'when a custom error message is passed from an OAuth2 provider' do
- let(:message) { 'Please go to https://evil.com' }
- let(:state) { 'secret' }
- let(:callback_path) { '/users/auth/oauth2/callback' }
- let(:params) { { state: state, error: 'evil_key', error_description: message } }
- let(:error) { last_request.env['omniauth.error'] }
-
- before do
- env('rack.session', { 'omniauth.state' => state })
- end
-
- it 'returns the custom error message if the state is valid' do
- get callback_path, **params
-
- expect(error.message).to eq("evil_key | #{message}")
- end
+ context 'when a Faraday exception is raised' do
+ where(exception: [Faraday::TimeoutError, Faraday::ConnectionFailed])
- it 'returns the custom `error_reason` message if the `error_description` is blank' do
- get callback_path, **params.merge(error_description: ' ', error_reason: 'custom reason')
-
- expect(error.message).to eq('evil_key | custom reason')
- end
-
- it 'returns a CSRF error if the state is invalid' do
- get callback_path, **params.merge(state: 'invalid')
-
- expect(error.message).to eq('csrf_detected | CSRF detected')
- end
+ with_them do
+ it 'passes the exception to OmniAuth' do
+ instance = OmniAuth::Strategies::OAuth2.new(double)
- it 'returns a CSRF error if the state is missing' do
- get callback_path, **params.without(:state)
+ expect(instance).to receive(:original_callback_phase) { raise exception, 'message' }
+ expect(instance).to receive(:fail!).with(:timeout, kind_of(exception))
- expect(error.message).to eq('csrf_detected | CSRF detected')
+ instance.callback_phase
+ end
end
end
end
diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb
index 4ba6c6e50f7..c254279a32f 100644
--- a/spec/models/ci/trigger_spec.rb
+++ b/spec/models/ci/trigger_spec.rb
@@ -57,4 +57,8 @@ RSpec.describe Ci::Trigger do
it { is_expected.to eq(false) }
end
end
+
+ it_behaves_like 'includes Limitable concern' do
+ subject { build(:ci_trigger, owner: project.owner, project: project) }
+ end
end
diff --git a/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb
new file mode 100644
index 00000000000..5167d27f8b9
--- /dev/null
+++ b/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'promote_to_incident quick action' do
+ describe '/promote_to_incident' do
+ context 'when issue can be promoted' do
+ it 'promotes issue to incident' do
+ add_note('/promote_to_incident')
+
+ expect(issue.reload.issue_type).to eq('incident')
+ expect(page).to have_content('Issue has been promoted to incident')
+ end
+ end
+
+ context 'when issue is already an incident' do
+ let(:issue) { create(:incident, project: project) }
+
+ it 'does not promote the issue' do
+ add_note('/promote_to_incident')
+
+ expect(page).to have_content('Could not apply promote_to_incident command')
+ end
+ end
+
+ context 'when user does not have permissions' do
+ let(:guest) { create(:user) }
+
+ before do
+ sign_in(guest)
+ visit project_issue_path(project, issue)
+ wait_for_all_requests
+ end
+
+ it 'does not promote the issue' do
+ add_note('/promote_to_incident')
+
+ expect(page).to have_content('Could not apply promote_to_incident command')
+ end
+ end
+ end
+end