Commit 6f4b2afc authored by Simon Knox's avatar Simon Knox Committed by Shinya Maeda

Load correct iteration on iteration report

Also unquarantine test, as it will load correct item now
Remove sign_in as not needed to view
parent 6566448f
...@@ -13,6 +13,10 @@ class ApplicationRecord < ActiveRecord::Base ...@@ -13,6 +13,10 @@ class ApplicationRecord < ActiveRecord::Base
where(id: ids) where(id: ids)
end end
def self.iid_in(iids)
where(iid: iids)
end
def self.id_not_in(ids) def self.id_not_in(ids)
where.not(id: ids) where.not(id: ids)
end end
......
...@@ -602,6 +602,14 @@ class Project < ApplicationRecord ...@@ -602,6 +602,14 @@ class Project < ApplicationRecord
end end
end end
def self.projects_user_can(projects, user, action)
projects = where(id: projects)
DeclarativePolicy.user_scope do
projects.select { |project| Ability.allowed?(user, action, project) }
end
end
# This scope returns projects where user has access to both the project and the feature. # This scope returns projects where user has access to both the project and the feature.
def self.filter_by_feature_visibility(feature, user) def self.filter_by_feature_visibility(feature, user)
with_feature_available_for_user(feature, user) with_feature_available_for_user(feature, user)
......
...@@ -5094,6 +5094,11 @@ type Group { ...@@ -5094,6 +5094,11 @@ type Group {
""" """
id: ID id: ID
"""
The internal ID of the Iteration to look up
"""
iid: ID
""" """
Returns the last _n_ elements from the list. Returns the last _n_ elements from the list.
""" """
...@@ -6301,6 +6306,11 @@ type Iteration { ...@@ -6301,6 +6306,11 @@ type Iteration {
""" """
id: ID! id: ID!
"""
Internal ID of the iteration
"""
iid: ID!
""" """
Timestamp of the iteration start date Timestamp of the iteration start date
""" """
......
...@@ -14100,6 +14100,16 @@ ...@@ -14100,6 +14100,16 @@
}, },
"defaultValue": null "defaultValue": null
}, },
{
"name": "iid",
"description": "The internal ID of the Iteration to look up",
"type": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
},
"defaultValue": null
},
{ {
"name": "after", "name": "after",
"description": "Returns the elements in the list that come after the specified cursor.", "description": "Returns the elements in the list that come after the specified cursor.",
...@@ -17329,6 +17339,24 @@ ...@@ -17329,6 +17339,24 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "iid",
"description": "Internal ID of the iteration",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "startDate", "name": "startDate",
"description": "Timestamp of the iteration start date", "description": "Timestamp of the iteration start date",
...@@ -946,6 +946,7 @@ Represents an iteration object. ...@@ -946,6 +946,7 @@ Represents an iteration object.
| `description` | String | Description of the iteration | | `description` | String | Description of the iteration |
| `dueDate` | Time | Timestamp of the iteration due date | | `dueDate` | Time | Timestamp of the iteration due date |
| `id` | ID! | ID of the iteration | | `id` | ID! | ID of the iteration |
| `iid` | ID! | Internal ID of the iteration |
| `startDate` | Time | Timestamp of the iteration start date | | `startDate` | Time | Timestamp of the iteration start date |
| `state` | IterationState! | State of the iteration | | `state` | IterationState! | State of the iteration |
| `title` | String! | Title of the iteration | | `title` | String! | Title of the iteration |
......
...@@ -10,7 +10,6 @@ import { ...@@ -10,7 +10,6 @@ import {
} from '@gitlab/ui'; } from '@gitlab/ui';
import { formatDate } from '~/lib/utils/datetime_utility'; import { formatDate } from '~/lib/utils/datetime_utility';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import IterationForm from './iteration_form.vue'; import IterationForm from './iteration_form.vue';
import query from '../queries/group_iteration.query.graphql'; import query from '../queries/group_iteration.query.graphql';
...@@ -37,7 +36,7 @@ export default { ...@@ -37,7 +36,7 @@ export default {
variables() { variables() {
return { return {
groupPath: this.groupPath, groupPath: this.groupPath,
id: getIdFromGraphQLId(this.iterationId), iid: this.iterationIid,
}; };
}, },
update(data) { update(data) {
...@@ -57,7 +56,7 @@ export default { ...@@ -57,7 +56,7 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
iterationId: { iterationIid: {
type: String, type: String,
required: true, required: true,
}, },
......
...@@ -51,7 +51,7 @@ export function initIterationForm() { ...@@ -51,7 +51,7 @@ export function initIterationForm() {
export function initIterationReport() { export function initIterationReport() {
const el = document.querySelector('.js-iteration'); const el = document.querySelector('.js-iteration');
const { groupPath, iterationId, editIterationPath } = el.dataset; const { groupPath, iterationIid, editIterationPath } = el.dataset;
const canEdit = parseBoolean(el.dataset.canEdit); const canEdit = parseBoolean(el.dataset.canEdit);
return new Vue({ return new Vue({
...@@ -61,7 +61,7 @@ export function initIterationReport() { ...@@ -61,7 +61,7 @@ export function initIterationReport() {
return createElement(IterationReport, { return createElement(IterationReport, {
props: { props: {
groupPath, groupPath,
iterationId, iterationIid,
canEdit, canEdit,
editIterationPath, editIterationPath,
}, },
......
query GroupIteration($groupPath: ID!, $id: ID!) { query GroupIteration($groupPath: ID!, $iid: ID!) {
group(fullPath: $groupPath) { group(fullPath: $groupPath) {
iterations(id: $id, first: 1) { iterations(iid: $iid, first: 1) {
nodes { nodes {
title title
state state
iid
id id
description description
webPath webPath
......
...@@ -13,15 +13,19 @@ class IterationsFinder ...@@ -13,15 +13,19 @@ class IterationsFinder
include FinderMethods include FinderMethods
include TimeFrameFilter include TimeFrameFilter
attr_reader :params attr_reader :params, :current_user
def initialize(params = {}) def initialize(current_user, params = {})
@params = params @params = params
@current_user = current_user
end end
def execute def execute
filter_permissions
items = Iteration.all items = Iteration.all
items = by_id(items) items = by_id(items)
items = by_iid(items)
items = by_groups_and_projects(items) items = by_groups_and_projects(items)
items = by_title(items) items = by_title(items)
items = by_search_title(items) items = by_search_title(items)
...@@ -33,6 +37,31 @@ class IterationsFinder ...@@ -33,6 +37,31 @@ class IterationsFinder
private private
def filter_permissions
filter_allowed_projects
filter_allowed_groups
# Only allow either one project_id or one group_id when filtering by `iid`
if params[:iid] && params.slice(:project_ids, :group_ids).keys.count > 1
raise ArgumentError, 'You can specify only one scope if you use iid filter'
end
end
def filter_allowed_projects
return unless params[:project_ids].present?
projects = Project.id_in(params[:project_ids])
params[:project_ids] = Project.projects_user_can(projects, current_user, :read_iteration)
end
def filter_allowed_groups
return unless params[:group_ids].present?
groups = Group.id_in(params[:group_ids])
params[:group_ids] = Group.groups_user_can(groups, current_user, :read_iteration)
end
def by_groups_and_projects(items) def by_groups_and_projects(items)
items.for_projects_and_groups(params[:project_ids], params[:group_ids]) items.for_projects_and_groups(params[:project_ids], params[:group_ids])
end end
...@@ -45,6 +74,10 @@ class IterationsFinder ...@@ -45,6 +74,10 @@ class IterationsFinder
end end
end end
def by_iid(items)
params[:iid].present? ? items.iid_in(params[:iid]) : items
end
def by_title(items) def by_title(items)
if params[:title] if params[:title]
items.with_title(params[:title]) items.with_title(params[:title])
......
...@@ -14,6 +14,9 @@ module Resolvers ...@@ -14,6 +14,9 @@ module Resolvers
argument :id, GraphQL::ID_TYPE, argument :id, GraphQL::ID_TYPE,
required: false, required: false,
description: 'The ID of the Iteration to look up' description: 'The ID of the Iteration to look up'
argument :iid, GraphQL::ID_TYPE,
required: false,
description: 'The internal ID of the Iteration to look up'
type Types::IterationType, null: true type Types::IterationType, null: true
...@@ -22,7 +25,7 @@ module Resolvers ...@@ -22,7 +25,7 @@ module Resolvers
authorize! authorize!
iterations = IterationsFinder.new(iterations_finder_params(args)).execute iterations = IterationsFinder.new(context[:current_user], iterations_finder_params(args)).execute
Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(iterations) Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(iterations)
end end
...@@ -32,6 +35,7 @@ module Resolvers ...@@ -32,6 +35,7 @@ module Resolvers
def iterations_finder_params(args) def iterations_finder_params(args)
{ {
id: args[:id], id: args[:id],
iid: args[:iid],
state: args[:state] || 'all', state: args[:state] || 'all',
start_date: args[:start_date], start_date: args[:start_date],
end_date: args[:end_date], end_date: args[:end_date],
......
...@@ -12,6 +12,9 @@ module Types ...@@ -12,6 +12,9 @@ module Types
field :id, GraphQL::ID_TYPE, null: false, field :id, GraphQL::ID_TYPE, null: false,
description: 'ID of the iteration' description: 'ID of the iteration'
field :iid, GraphQL::ID_TYPE, null: false,
description: 'Internal ID of the iteration'
field :title, GraphQL::STRING_TYPE, null: false, field :title, GraphQL::STRING_TYPE, null: false,
description: 'Title of the iteration' description: 'Title of the iteration'
......
...@@ -142,7 +142,7 @@ module EE ...@@ -142,7 +142,7 @@ module EE
end end
class_methods do class_methods do
def groups_user_can_read_epics(groups, user, same_root: false) def groups_user_can(groups, user, action, same_root: false)
groups = ::Gitlab::GroupPlansPreloader.new.preload(groups) groups = ::Gitlab::GroupPlansPreloader.new.preload(groups)
# if we are sure that all groups have the same root group, we can # if we are sure that all groups have the same root group, we can
...@@ -152,10 +152,14 @@ module EE ...@@ -152,10 +152,14 @@ module EE
preset_root_ancestor_for(groups) if same_root preset_root_ancestor_for(groups) if same_root
DeclarativePolicy.user_scope do DeclarativePolicy.user_scope do
groups.select { |group| Ability.allowed?(user, :read_epic, group) } groups.select { |group| Ability.allowed?(user, action, group) }
end end
end end
def groups_user_can_read_epics(groups, user, same_root: false)
groups_user_can(groups, user, :read_epic, same_root: same_root)
end
def preset_root_ancestor_for(groups) def preset_root_ancestor_for(groups)
return groups if groups.size < 2 return groups if groups.size < 2
......
...@@ -3,4 +3,4 @@ ...@@ -3,4 +3,4 @@
- page_title _("Iterations") - page_title _("Iterations")
- if Feature.enabled?(:group_iterations, @group) - if Feature.enabled?(:group_iterations, @group)
.js-iteration{ data: { group_path: @group.full_path, iteration_id: params[:id], can_edit: can?(current_user, :admin_iteration, @group).to_s, preview_markdown_path: preview_markdown_path(@group) } } .js-iteration{ data: { group_path: @group.full_path, iteration_iid: params[:id], can_edit: can?(current_user, :admin_iteration, @group).to_s, preview_markdown_path: preview_markdown_path(@group) } }
...@@ -77,7 +77,7 @@ module EE ...@@ -77,7 +77,7 @@ module EE
def find_iteration_with_finder(parent, params) def find_iteration_with_finder(parent, params)
finder_params = iteration_finder_params(parent) finder_params = iteration_finder_params(parent)
::IterationsFinder.new(finder_params).find_by(params) ::IterationsFinder.new(context[:current_user], finder_params).find_by(params)
end end
# rubocop:enable CodeReuse/ActiveRecord # rubocop:enable CodeReuse/ActiveRecord
......
...@@ -130,9 +130,9 @@ module EE ...@@ -130,9 +130,9 @@ module EE
end end
def find_iterations(project, params = {}) def find_iterations(project, params = {})
group_ids = project.group.self_and_ancestors.select(:id) if project.group group_ids = project.group.self_and_ancestors.map(&:id) if project.group
::IterationsFinder.new(params.merge(project_ids: [project.id], group_ids: group_ids)).execute ::IterationsFinder.new(current_user, params.merge(project_ids: [project.id], group_ids: group_ids)).execute
end end
desc _('Publish to status page') desc _('Publish to status page')
......
...@@ -16,7 +16,6 @@ module Gitlab ...@@ -16,7 +16,6 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def preload(groups) def preload(groups)
groups_and_ancestors = groups_and_ancestors_for(groups) groups_and_ancestors = groups_and_ancestors_for(groups)
# A Hash mapping group IDs to their corresponding Group instances. # A Hash mapping group IDs to their corresponding Group instances.
groups_map = groups_and_ancestors.each_with_object({}) do |group, hash| groups_map = groups_and_ancestors.each_with_object({}) do |group, hash|
hash[group.id] = group hash[group.id] = group
......
...@@ -5,25 +5,24 @@ require 'spec_helper' ...@@ -5,25 +5,24 @@ require 'spec_helper'
RSpec.describe 'User views iteration' do RSpec.describe 'User views iteration' do
let_it_be(:now) { Time.now } let_it_be(:now) { Time.now }
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
let_it_be(:iteration) { create(:iteration, :skip_future_date_validation, group: group, start_date: now - 1.day, due_date: now) } let_it_be(:iteration) { create(:iteration, :skip_future_date_validation, group: group, start_date: now - 1.day, due_date: now) }
around do |example| context 'with license' do
Timecop.freeze { example.run }
end
before do
sign_in(user)
end
context 'view an iteration', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/222915' do
before do before do
visit group_iteration_path(iteration.group, iteration) stub_licensed_features(iterations: true)
end end
it 'shows iteration info and dates' do context 'view an iteration', :js do
expect(page).to have_content(iteration.title) before do
expect(page).to have_content(iteration.description) visit group_iteration_path(iteration.group, iteration)
end
it 'shows iteration info and dates' do
expect(page).to have_content(iteration.title)
expect(page).to have_content(iteration.description)
expect(page).to have_content(iteration.start_date.strftime('%b %-d, %Y'))
expect(page).to have_content(iteration.due_date.strftime('%b %-d, %Y'))
end
end end
end end
end end
...@@ -4,127 +4,160 @@ require 'spec_helper' ...@@ -4,127 +4,160 @@ require 'spec_helper'
RSpec.describe IterationsFinder do RSpec.describe IterationsFinder do
let(:now) { Time.now } let(:now) { Time.now }
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group, :private) }
let_it_be(:project_1) { create(:project, namespace: group) } let_it_be(:project_1) { create(:project, namespace: group) }
let_it_be(:project_2) { create(:project, namespace: group) } let_it_be(:project_2) { create(:project, namespace: group) }
let_it_be(:user) { create(:user) }
let!(:started_group_iteration) { create(:started_iteration, :skip_future_date_validation, group: group, title: 'one test', start_date: now - 1.day, due_date: now) } let!(:started_group_iteration) { create(:started_iteration, :skip_future_date_validation, group: group, title: 'one test', start_date: now - 1.day, due_date: now) }
let!(:upcoming_group_iteration) { create(:iteration, group: group, start_date: 1.day.from_now, due_date: 2.days.from_now) } let!(:upcoming_group_iteration) { create(:iteration, group: group, start_date: 1.day.from_now, due_date: 2.days.from_now) }
let!(:iteration_from_project_1) { create(:started_iteration, project: project_1, start_date: 2.days.from_now, due_date: 3.days.from_now) } let!(:iteration_from_project_1) { create(:started_iteration, project: project_1, start_date: 2.days.from_now, due_date: 3.days.from_now) }
let!(:iteration_from_project_2) { create(:started_iteration, project: project_2, start_date: 4.days.from_now, due_date: 5.days.from_now) } let!(:iteration_from_project_2) { create(:started_iteration, project: project_2, start_date: 4.days.from_now, due_date: 5.days.from_now) }
let(:project_ids) { [project_1.id, project_2.id] } let(:project_ids) { [project_1.id, project_2.id] }
subject { described_class.new(params).execute } subject { described_class.new(user, params).execute }
context 'iterations for projects' do context 'without permissions' do
let(:params) { { project_ids: project_ids, state: 'all' } } context 'groups and projects' do
let(:params) { { project_ids: project_ids, group_ids: group.id, state: 'all' } }
it 'returns iterations for projects' do it 'returns iterations for groups and projects' do
expect(subject).to contain_exactly(iteration_from_project_1, iteration_from_project_2) expect(subject).to be_empty
end
end end
end end
context 'iterations for groups' do context 'with permissions' do
let(:params) { { group_ids: group.id, state: 'all' } } before do
group.add_reporter(user)
it 'returns iterations for groups' do project_1.add_reporter(user)
expect(subject).to contain_exactly(started_group_iteration, upcoming_group_iteration) project_2.add_reporter(user)
end end
end
context 'iterations for groups and project' do context 'iterations for projects' do
let(:params) { { project_ids: project_ids, group_ids: group.id, state: 'all' } } let(:params) { { project_ids: project_ids, state: 'all' } }
it 'returns iterations for groups and projects' do it 'returns iterations for projects' do
expect(subject).to contain_exactly(started_group_iteration, upcoming_group_iteration, iteration_from_project_1, iteration_from_project_2) expect(subject).to contain_exactly(iteration_from_project_1, iteration_from_project_2)
end
end end
it 'orders iterations by due date' do context 'iterations for groups' do
iteration = create(:iteration, :skip_future_date_validation, group: group, start_date: now - 3.days, due_date: now - 2.days) let(:params) { { group_ids: group.id, state: 'all' } }
expect(subject.first).to eq(iteration) it 'returns iterations for groups' do
expect(subject.second).to eq(started_group_iteration) expect(subject).to contain_exactly(started_group_iteration, upcoming_group_iteration)
expect(subject.third).to eq(upcoming_group_iteration) end
end end
end
context 'with filters' do context 'iterations for groups and project' do
let(:params) do let(:params) { { project_ids: project_ids, group_ids: group.id, state: 'all' } }
{
project_ids: project_ids,
group_ids: group.id,
state: 'all'
}
end
before do it 'returns iterations for groups and projects' do
started_group_iteration.close expect(subject).to contain_exactly(started_group_iteration, upcoming_group_iteration, iteration_from_project_1, iteration_from_project_2)
iteration_from_project_1.close end
end
it 'filters by started state' do it 'orders iterations by due date' do
params[:state] = 'started' iteration = create(:iteration, :skip_future_date_validation, group: group, start_date: now - 3.days, due_date: now - 2.days)
expect(subject).to contain_exactly(iteration_from_project_2) expect(subject.first).to eq(iteration)
expect(subject.second).to eq(started_group_iteration)
expect(subject.third).to eq(upcoming_group_iteration)
end
end end
it 'filters by opened state' do context 'with filters' do
params[:state] = 'opened' let(:params) do
{
project_ids: project_ids,
group_ids: group.id,
state: 'all'
}
end
expect(subject).to contain_exactly(upcoming_group_iteration, iteration_from_project_2) before do
end started_group_iteration.close
iteration_from_project_1.close
end
it 'filters by closed state' do it 'filters by started state' do
params[:state] = 'closed' params[:state] = 'started'
expect(subject).to contain_exactly(started_group_iteration, iteration_from_project_1) expect(subject).to contain_exactly(iteration_from_project_2)
end end
it 'filters by title' do it 'filters by opened state' do
params[:title] = 'one test' params[:state] = 'opened'
expect(subject.to_a).to contain_exactly(started_group_iteration) expect(subject).to contain_exactly(upcoming_group_iteration, iteration_from_project_2)
end end
it 'filters by search_title' do it 'filters by closed state' do
params[:search_title] = 'one t' params[:state] = 'closed'
expect(subject.to_a).to contain_exactly(started_group_iteration) expect(subject).to contain_exactly(started_group_iteration, iteration_from_project_1)
end end
it 'filters by ID' do it 'filters by title' do
params[:id] = iteration_from_project_1.id params[:title] = 'one test'
expect(subject).to contain_exactly(iteration_from_project_1) expect(subject.to_a).to contain_exactly(started_group_iteration)
end end
context 'by timeframe' do it 'filters by search_title' do
it 'returns iterations with start_date and due_date between timeframe' do params[:search_title] = 'one t'
params.merge!(start_date: now - 1.day, end_date: 3.days.from_now)
expect(subject).to match_array([started_group_iteration, upcoming_group_iteration, iteration_from_project_1]) expect(subject.to_a).to contain_exactly(started_group_iteration)
end end
it 'returns iterations which start before the timeframe' do it 'filters by ID' do
iteration = create(:iteration, :skip_future_date_validation, project: project_2, start_date: now - 5.days, due_date: now - 3.days) params[:id] = iteration_from_project_1.id
params.merge!(start_date: now - 3.days, end_date: now - 2.days)
expect(subject).to match_array([iteration]) expect(subject).to contain_exactly(iteration_from_project_1)
end end
it 'returns iterations which end after the timeframe' do context 'by timeframe' do
iteration = create(:iteration, project: project_2, start_date: 6.days.from_now, due_date: 2.weeks.from_now) it 'returns iterations with start_date and due_date between timeframe' do
params.merge!(start_date: 6.days.from_now, end_date: 7.days.from_now) params.merge!(start_date: now - 1.day, end_date: 3.days.from_now)
expect(subject).to match_array([started_group_iteration, upcoming_group_iteration, iteration_from_project_1])
end
it 'returns iterations which start before the timeframe' do
iteration = create(:iteration, :skip_future_date_validation, project: project_2, start_date: now - 5.days, due_date: now - 3.days)
params.merge!(start_date: now - 3.days, end_date: now - 2.days)
expect(subject).to match_array([iteration])
end
expect(subject).to match_array([iteration]) it 'returns iterations which end after the timeframe' do
iteration = create(:iteration, project: project_2, start_date: 6.days.from_now, due_date: 2.weeks.from_now)
params.merge!(start_date: 6.days.from_now, end_date: 7.days.from_now)
expect(subject).to match_array([iteration])
end
end end
end end
end
describe '#find_by' do describe 'iid' do
it 'finds a single iteration' do let(:params) do
finder = described_class.new(project_ids: [project_1.id], state: 'all') {
project_ids: project_ids,
group_ids: group.id,
iid: iteration_from_project_1.iid
}
end
it 'only accepts one of project_id or group_id' do
expect { subject }.to raise_error(ArgumentError, 'You can specify only one scope if you use iid filter')
end
end
expect(finder.find_by(iid: iteration_from_project_1.iid)).to eq(iteration_from_project_1) describe '#find_by' do
it 'finds a single iteration' do
finder = described_class.new(user, project_ids: [project_1.id], state: 'all')
expect(finder.find_by(iid: iteration_from_project_1.iid)).to eq(iteration_from_project_1)
end
end end
end end
end end
...@@ -6,7 +6,7 @@ describe('Iterations tabs', () => { ...@@ -6,7 +6,7 @@ describe('Iterations tabs', () => {
let wrapper; let wrapper;
const defaultProps = { const defaultProps = {
groupPath: 'gitlab-org', groupPath: 'gitlab-org',
iterationId: '3', iterationIid: '3',
}; };
const findTopbar = () => wrapper.find({ ref: 'topbar' }); const findTopbar = () => wrapper.find({ ref: 'topbar' });
......
...@@ -30,9 +30,9 @@ RSpec.describe Resolvers::IterationsResolver do ...@@ -30,9 +30,9 @@ RSpec.describe Resolvers::IterationsResolver do
context 'without parameters' do context 'without parameters' do
it 'calls IterationsFinder to retrieve all iterations' do it 'calls IterationsFinder to retrieve all iterations' do
params = { id: nil, group_ids: group.id, state: 'all', start_date: nil, end_date: nil, search_title: nil } params = { id: nil, iid: nil, group_ids: group.id, state: 'all', start_date: nil, end_date: nil, search_title: nil }
expect(IterationsFinder).to receive(:new).with(params).and_call_original expect(IterationsFinder).to receive(:new).with(current_user, params).and_call_original
resolve_group_iterations resolve_group_iterations
end end
...@@ -44,11 +44,12 @@ RSpec.describe Resolvers::IterationsResolver do ...@@ -44,11 +44,12 @@ RSpec.describe Resolvers::IterationsResolver do
end_date = start_date + 1.hour end_date = start_date + 1.hour
search = 'wow' search = 'wow'
id = 1 id = 1
params = { id: id, group_ids: group.id, state: 'closed', start_date: start_date, end_date: end_date, search_title: search } iid = 2
params = { id: id, iid: iid, group_ids: group.id, state: 'closed', start_date: start_date, end_date: end_date, search_title: search }
expect(IterationsFinder).to receive(:new).with(params).and_call_original expect(IterationsFinder).to receive(:new).with(current_user, params).and_call_original
resolve_group_iterations(start_date: start_date, end_date: end_date, state: 'closed', title: search, id: id) resolve_group_iterations(start_date: start_date, end_date: end_date, state: 'closed', title: search, id: id, iid: iid)
end end
end end
......
...@@ -221,11 +221,17 @@ RSpec.describe QuickActions::InterpretService do ...@@ -221,11 +221,17 @@ RSpec.describe QuickActions::InterpretService do
end end
context 'when iteration exists' do context 'when iteration exists' do
it 'assigns an iteration to an issue' do context 'with permissions' do
_, updates, message = service.execute(content, issue) before do
group.add_developer(current_user)
end
expect(updates).to eq(iteration: iteration) it 'assigns an iteration to an issue' do
expect(message).to eq("Set the iteration to #{iteration.to_reference}.") _, updates, message = service.execute(content, issue)
expect(updates).to eq(iteration: iteration)
expect(message).to eq("Set the iteration to #{iteration.to_reference}.")
end
end end
context 'when the user does not have enough permissions' do context 'when the user does not have enough permissions' do
......
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