summaryrefslogtreecommitdiff
path: root/spec/requests/api/graphql/project
diff options
context:
space:
mode:
Diffstat (limited to 'spec/requests/api/graphql/project')
-rw-r--r--spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb61
-rw-r--r--spec/requests/api/graphql/project/alert_management/alerts_spec.rb139
-rw-r--r--spec/requests/api/graphql/project/grafana_integration_spec.rb10
-rw-r--r--spec/requests/api/graphql/project/issue/design_collection/version_spec.rb216
-rw-r--r--spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb113
-rw-r--r--spec/requests/api/graphql/project/issue/designs/designs_spec.rb388
-rw-r--r--spec/requests/api/graphql/project/issue/designs/notes_spec.rb70
-rw-r--r--spec/requests/api/graphql/project/issue_spec.rb189
-rw-r--r--spec/requests/api/graphql/project/issues_spec.rb209
-rw-r--r--spec/requests/api/graphql/project/jira_import_spec.rb3
10 files changed, 1291 insertions, 107 deletions
diff --git a/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb b/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb
new file mode 100644
index 00000000000..ffd328429ef
--- /dev/null
+++ b/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe 'getting Alert Management Alert counts by status' do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:alert_1) { create(:alert_management_alert, :resolved, project: project) }
+ let_it_be(:alert_2) { create(:alert_management_alert, project: project) }
+ let_it_be(:other_project_alert) { create(:alert_management_alert) }
+ let(:params) { {} }
+
+ let(:fields) do
+ <<~QUERY
+ #{all_graphql_fields_for('AlertManagementAlertStatusCountsType'.classify)}
+ QUERY
+ end
+
+ let(:query) do
+ graphql_query_for(
+ 'project',
+ { 'fullPath' => project.full_path },
+ query_graphql_field('alertManagementAlertStatusCounts', params, fields)
+ )
+ end
+
+ context 'with alert data' do
+ let(:alert_counts) { graphql_data.dig('project', 'alertManagementAlertStatusCounts') }
+
+ context 'without project permissions' do
+ let(:user) { create(:user) }
+
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it_behaves_like 'a working graphql query'
+ it { expect(alert_counts).to be nil }
+ end
+
+ context 'with project permissions' do
+ before do
+ project.add_developer(current_user)
+ post_graphql(query, current_user: current_user)
+ end
+
+ it_behaves_like 'a working graphql query'
+ it 'returns the correct counts for each status' do
+ expect(alert_counts).to eq(
+ 'open' => 1,
+ 'all' => 2,
+ 'triggered' => 1,
+ 'acknowledged' => 0,
+ 'resolved' => 1,
+ 'ignored' => 0
+ )
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/alert_management/alerts_spec.rb b/spec/requests/api/graphql/project/alert_management/alerts_spec.rb
new file mode 100644
index 00000000000..c226e659364
--- /dev/null
+++ b/spec/requests/api/graphql/project/alert_management/alerts_spec.rb
@@ -0,0 +1,139 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe 'getting Alert Management Alerts' do
+ include GraphqlHelpers
+
+ let_it_be(:payload) { { 'custom' => { 'alert' => 'payload' } } }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:resolved_alert) { create(:alert_management_alert, :all_fields, :resolved, project: project, issue: nil, severity: :low) }
+ let_it_be(:triggered_alert) { create(:alert_management_alert, :all_fields, project: project, severity: :critical, payload: payload) }
+ let_it_be(:other_project_alert) { create(:alert_management_alert, :all_fields) }
+ let(:params) { {} }
+
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ #{all_graphql_fields_for('AlertManagementAlert'.classify)}
+ }
+ QUERY
+ end
+
+ let(:query) do
+ graphql_query_for(
+ 'project',
+ { 'fullPath' => project.full_path },
+ query_graphql_field('alertManagementAlerts', params, fields)
+ )
+ end
+
+ context 'with alert data' do
+ let(:alerts) { graphql_data.dig('project', 'alertManagementAlerts', 'nodes') }
+
+ context 'without project permissions' do
+ let(:user) { create(:user) }
+
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it { expect(alerts).to be nil }
+ end
+
+ context 'with project permissions' do
+ before do
+ project.add_developer(current_user)
+ post_graphql(query, current_user: current_user)
+ end
+
+ let(:first_alert) { alerts.first }
+ let(:second_alert) { alerts.second }
+
+ it_behaves_like 'a working graphql query'
+
+ it { expect(alerts.size).to eq(2) }
+
+ it 'returns the correct properties of the alerts' do
+ expect(first_alert).to include(
+ 'iid' => triggered_alert.iid.to_s,
+ 'issueIid' => triggered_alert.issue_iid.to_s,
+ 'title' => triggered_alert.title,
+ 'description' => triggered_alert.description,
+ 'severity' => triggered_alert.severity.upcase,
+ 'status' => 'TRIGGERED',
+ 'monitoringTool' => triggered_alert.monitoring_tool,
+ 'service' => triggered_alert.service,
+ 'hosts' => triggered_alert.hosts,
+ 'eventCount' => triggered_alert.events,
+ 'startedAt' => triggered_alert.started_at.strftime('%Y-%m-%dT%H:%M:%SZ'),
+ 'endedAt' => nil,
+ 'details' => { 'custom.alert' => 'payload' },
+ 'createdAt' => triggered_alert.created_at.strftime('%Y-%m-%dT%H:%M:%SZ'),
+ 'updatedAt' => triggered_alert.updated_at.strftime('%Y-%m-%dT%H:%M:%SZ')
+ )
+
+ expect(second_alert).to include(
+ 'iid' => resolved_alert.iid.to_s,
+ 'issueIid' => nil,
+ 'status' => 'RESOLVED',
+ 'endedAt' => resolved_alert.ended_at.strftime('%Y-%m-%dT%H:%M:%SZ')
+ )
+ end
+
+ context 'with iid given' do
+ let(:params) { { iid: resolved_alert.iid.to_s } }
+
+ it_behaves_like 'a working graphql query'
+
+ it { expect(alerts.size).to eq(1) }
+ it { expect(first_alert['iid']).to eq(resolved_alert.iid.to_s) }
+ end
+
+ context 'with statuses given' do
+ let(:params) { 'statuses: [TRIGGERED, ACKNOWLEDGED]' }
+
+ it_behaves_like 'a working graphql query'
+
+ it { expect(alerts.size).to eq(1) }
+ it { expect(first_alert['iid']).to eq(triggered_alert.iid.to_s) }
+ end
+
+ context 'sorting data given' do
+ let(:params) { 'sort: SEVERITY_DESC' }
+ let(:iids) { alerts.map { |a| a['iid'] } }
+
+ it_behaves_like 'a working graphql query'
+
+ it 'sorts in the correct order' do
+ expect(iids).to eq [resolved_alert.iid.to_s, triggered_alert.iid.to_s]
+ end
+
+ context 'ascending order' do
+ let(:params) { 'sort: SEVERITY_ASC' }
+
+ it 'sorts in the correct order' do
+ expect(iids).to eq [triggered_alert.iid.to_s, resolved_alert.iid.to_s]
+ end
+ end
+ end
+
+ context 'searching' do
+ let(:params) { { search: resolved_alert.title } }
+
+ it_behaves_like 'a working graphql query'
+
+ it { expect(alerts.size).to eq(1) }
+ it { expect(first_alert['iid']).to eq(resolved_alert.iid.to_s) }
+
+ context 'unknown criteria' do
+ let(:params) { { search: 'something random' } }
+
+ it { expect(alerts.size).to eq(0) }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/grafana_integration_spec.rb b/spec/requests/api/graphql/project/grafana_integration_spec.rb
index e7155934b3a..c9bc6c1a68e 100644
--- a/spec/requests/api/graphql/project/grafana_integration_spec.rb
+++ b/spec/requests/api/graphql/project/grafana_integration_spec.rb
@@ -35,7 +35,7 @@ describe 'Getting Grafana Integration' do
it_behaves_like 'a working graphql query'
- it { expect(integration_data).to be nil }
+ specify { expect(integration_data).to be nil }
end
context 'with project admin permissions' do
@@ -45,16 +45,16 @@ describe 'Getting Grafana Integration' do
it_behaves_like 'a working graphql query'
- it { expect(integration_data['token']).to eql grafana_integration.masked_token }
- it { expect(integration_data['grafanaUrl']).to eql grafana_integration.grafana_url }
+ specify { expect(integration_data['token']).to eql grafana_integration.masked_token }
+ specify { expect(integration_data['grafanaUrl']).to eql grafana_integration.grafana_url }
- it do
+ specify do
expect(
integration_data['createdAt']
).to eql grafana_integration.created_at.strftime('%Y-%m-%dT%H:%M:%SZ')
end
- it do
+ specify do
expect(
integration_data['updatedAt']
).to eql grafana_integration.updated_at.strftime('%Y-%m-%dT%H:%M:%SZ')
diff --git a/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb b/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb
new file mode 100644
index 00000000000..04f445b4318
--- /dev/null
+++ b/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb
@@ -0,0 +1,216 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)' do
+ include GraphqlHelpers
+ include DesignManagementTestHelpers
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:stranger) { create(:user) }
+ let_it_be(:old_version) do
+ create(:design_version, issue: issue,
+ created_designs: create_list(:design, 3, issue: issue))
+ end
+ let_it_be(:version) do
+ create(:design_version, issue: issue,
+ modified_designs: old_version.designs,
+ created_designs: create_list(:design, 2, issue: issue))
+ end
+
+ let(:current_user) { developer }
+
+ def query(vq = version_fields)
+ graphql_query_for(:project, { fullPath: project.full_path },
+ query_graphql_field(:issue, { iid: issue.iid.to_s },
+ query_graphql_field(:design_collection, nil,
+ query_graphql_field(:version, { sha: version.sha }, vq))))
+ end
+
+ let(:post_query) { post_graphql(query, current_user: current_user) }
+ let(:path_prefix) { %w[project issue designCollection version] }
+
+ let(:data) { graphql_data.dig(*path) }
+
+ before do
+ enable_design_management
+ project.add_developer(developer)
+ end
+
+ describe 'scalar fields' do
+ let(:path) { path_prefix }
+ let(:version_fields) { query_graphql_field(:sha) }
+
+ before do
+ post_query
+ end
+
+ { id: ->(x) { x.to_global_id.to_s }, sha: ->(x) { x.sha } }.each do |field, value|
+ describe ".#{field}" do
+ let(:version_fields) { query_graphql_field(field) }
+
+ it "retrieves the #{field}" do
+ expect(data).to match(a_hash_including(field.to_s => value[version]))
+ end
+ end
+ end
+ end
+
+ describe 'design_at_version' do
+ let(:path) { path_prefix + %w[designAtVersion] }
+ let(:design) { issue.designs.visible_at_version(version).to_a.sample }
+ let(:design_at_version) { build(:design_at_version, design: design, version: version) }
+
+ let(:version_fields) do
+ query_graphql_field(:design_at_version, dav_params, 'id filename')
+ end
+
+ shared_examples :finds_dav do
+ it 'finds all the designs as of the given version' do
+ post_query
+
+ expect(data).to match(
+ a_hash_including(
+ 'id' => global_id_of(design_at_version),
+ 'filename' => design.filename
+ ))
+ end
+
+ context 'when the current_user is not authorized' do
+ let(:current_user) { stranger }
+
+ it 'returns nil' do
+ post_query
+
+ expect(data).to be_nil
+ end
+ end
+ end
+
+ context 'by ID' do
+ let(:dav_params) { { id: global_id_of(design_at_version) } }
+
+ include_examples :finds_dav
+ end
+
+ context 'by filename' do
+ let(:dav_params) { { filename: design.filename } }
+
+ include_examples :finds_dav
+ end
+
+ context 'by design_id' do
+ let(:dav_params) { { design_id: global_id_of(design) } }
+
+ include_examples :finds_dav
+ end
+ end
+
+ describe 'designs_at_version' do
+ let(:path) { path_prefix + %w[designsAtVersion edges] }
+ let(:version_fields) do
+ query_graphql_field(:designs_at_version, dav_params, 'edges { node { id filename } }')
+ end
+
+ let(:dav_params) { nil }
+
+ let(:results) do
+ issue.designs.visible_at_version(version).map do |d|
+ dav = build(:design_at_version, design: d, version: version)
+ { 'id' => global_id_of(dav), 'filename' => d.filename }
+ end
+ end
+
+ it 'finds all the designs as of the given version' do
+ post_query
+
+ expect(data.pluck('node')).to match_array(results)
+ end
+
+ describe 'filtering' do
+ let(:designs) { issue.designs.sample(3) }
+ let(:filenames) { designs.map(&:filename) }
+ let(:ids) do
+ designs.map { |d| global_id_of(build(:design_at_version, design: d, version: version)) }
+ end
+
+ before do
+ post_query
+ end
+
+ describe 'by filename' do
+ let(:dav_params) { { filenames: filenames } }
+
+ it 'finds the designs by filename' do
+ expect(data.map { |e| e.dig('node', 'id') }).to match_array(ids)
+ end
+ end
+
+ describe 'by design-id' do
+ let(:dav_params) { { ids: designs.map { |d| global_id_of(d) } } }
+
+ it 'finds the designs by id' do
+ expect(data.map { |e| e.dig('node', 'filename') }).to match_array(filenames)
+ end
+ end
+ end
+
+ describe 'pagination' do
+ let(:end_cursor) { graphql_data_at(*path_prefix, :designs_at_version, :page_info, :end_cursor) }
+
+ let(:ids) do
+ ::DesignManagement::Design.visible_at_version(version).order(:id).map do |d|
+ global_id_of(build(:design_at_version, design: d, version: version))
+ end
+ end
+
+ let(:version_fields) do
+ query_graphql_field(:designs_at_version, { first: 2 }, fields)
+ end
+
+ let(:cursored_query) do
+ frag = query_graphql_field(:designs_at_version, { after: end_cursor }, fields)
+ query(frag)
+ end
+
+ let(:fields) { ['pageInfo { endCursor }', 'edges { node { id } }'] }
+
+ def response_values(data = graphql_data)
+ data.dig(*path).map { |e| e.dig('node', 'id') }
+ end
+
+ it 'sorts designs for reliable pagination' do
+ post_graphql(query, current_user: current_user)
+
+ expect(response_values).to match_array(ids.take(2))
+
+ post_graphql(cursored_query, current_user: current_user)
+
+ new_data = Gitlab::Json.parse(response.body).fetch('data')
+
+ expect(response_values(new_data)).to match_array(ids.drop(2))
+ end
+ end
+ end
+
+ describe 'designs' do
+ let(:path) { path_prefix + %w[designs edges] }
+ let(:version_fields) do
+ query_graphql_field(:designs, nil, 'edges { node { id filename } }')
+ end
+
+ let(:results) do
+ version.designs.map do |design|
+ { 'id' => global_id_of(design), 'filename' => design.filename }
+ end
+ end
+
+ it 'finds all the designs as of the given version' do
+ post_query
+
+ expect(data.pluck('node')).to match_array(results)
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb b/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb
new file mode 100644
index 00000000000..18787bf925d
--- /dev/null
+++ b/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb
@@ -0,0 +1,113 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Getting versions related to an issue' do
+ include GraphqlHelpers
+ include DesignManagementTestHelpers
+
+ let_it_be(:issue) { create(:issue) }
+
+ let_it_be(:version_a) do
+ create(:design_version, issue: issue)
+ end
+ let_it_be(:version_b) do
+ create(:design_version, issue: issue)
+ end
+ let_it_be(:version_c) do
+ create(:design_version, issue: issue)
+ end
+ let_it_be(:version_d) do
+ create(:design_version, issue: issue)
+ end
+
+ let_it_be(:owner) { issue.project.owner }
+
+ def version_query(params = version_params)
+ query_graphql_field(:versions, params, version_query_fields)
+ end
+
+ let(:version_params) { nil }
+
+ let(:version_query_fields) { ['edges { node { sha } }'] }
+
+ let(:project) { issue.project }
+ let(:current_user) { owner }
+
+ let(:query) { make_query }
+
+ def make_query(vq = version_query)
+ graphql_query_for(:project, { fullPath: project.full_path },
+ query_graphql_field(:issue, { iid: issue.iid.to_s },
+ query_graphql_field(:design_collection, {}, vq)))
+ end
+
+ let(:design_collection) do
+ graphql_data_at(:project, :issue, :design_collection)
+ end
+
+ def response_values(data = graphql_data, key = 'sha')
+ path = %w[project issue designCollection versions edges]
+ data.dig(*path).map { |e| e.dig('node', key) }
+ end
+
+ before do
+ enable_design_management
+ end
+
+ it 'returns the design filename' do
+ post_graphql(query, current_user: current_user)
+
+ expect(response_values).to match_array([version_a, version_b, version_c, version_d].map(&:sha))
+ end
+
+ describe 'filter by sha' do
+ let(:sha) { version_b.sha }
+
+ let(:version_params) { { earlier_or_equal_to_sha: sha } }
+
+ it 'finds only those versions at or before the given cut-off' do
+ post_graphql(query, current_user: current_user)
+
+ expect(response_values).to contain_exactly(version_a.sha, version_b.sha)
+ end
+ end
+
+ describe 'filter by id' do
+ let(:id) { global_id_of(version_c) }
+
+ let(:version_params) { { earlier_or_equal_to_id: id } }
+
+ it 'finds only those versions at or before the given cut-off' do
+ post_graphql(query, current_user: current_user)
+
+ expect(response_values).to contain_exactly(version_a.sha, version_b.sha, version_c.sha)
+ end
+ end
+
+ describe 'pagination' do
+ let(:end_cursor) { design_collection.dig('versions', 'pageInfo', 'endCursor') }
+
+ let(:ids) { issue.design_collection.versions.ordered.map(&:sha) }
+
+ let(:query) { make_query(version_query(first: 2)) }
+
+ let(:cursored_query) do
+ make_query(version_query(after: end_cursor))
+ end
+
+ let(:version_query_fields) { ['pageInfo { endCursor }', 'edges { node { sha } }'] }
+
+ it 'sorts designs for reliable pagination' do
+ post_graphql(query, current_user: current_user)
+
+ expect(response_values).to match_array(ids.take(2))
+
+ post_graphql(cursored_query, current_user: current_user)
+
+ new_data = Gitlab::Json.parse(response.body).fetch('data')
+
+ expect(response_values(new_data)).to match_array(ids.drop(2))
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/issue/designs/designs_spec.rb b/spec/requests/api/graphql/project/issue/designs/designs_spec.rb
new file mode 100644
index 00000000000..b6fd0d91bda
--- /dev/null
+++ b/spec/requests/api/graphql/project/issue/designs/designs_spec.rb
@@ -0,0 +1,388 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Getting designs related to an issue' do
+ include GraphqlHelpers
+ include DesignManagementTestHelpers
+
+ let_it_be(:design) { create(:design, :with_smaller_image_versions, versions_count: 1) }
+ let_it_be(:current_user) { design.project.owner }
+ let(:design_query) do
+ <<~NODE
+ designs {
+ edges {
+ node {
+ id
+ filename
+ fullPath
+ event
+ image
+ imageV432x230
+ }
+ }
+ }
+ NODE
+ end
+ let(:issue) { design.issue }
+ let(:project) { issue.project }
+ let(:query) { make_query }
+ let(:design_collection) do
+ graphql_data_at(:project, :issue, :design_collection)
+ end
+ let(:design_response) do
+ design_collection.dig('designs', 'edges').first['node']
+ end
+
+ def make_query(dq = design_query)
+ designs_field = query_graphql_field(:design_collection, {}, dq)
+ issue_field = query_graphql_field(:issue, { iid: issue.iid.to_s }, designs_field)
+
+ graphql_query_for(:project, { fullPath: project.full_path }, issue_field)
+ end
+
+ def design_image_url(design, ref: nil, size: nil)
+ Gitlab::UrlBuilder.build(design, ref: ref, size: size)
+ end
+
+ context 'when the feature is available' do
+ before do
+ enable_design_management
+ end
+
+ it 'returns the design properties correctly' do
+ version_sha = design.versions.first.sha
+
+ post_graphql(query, current_user: current_user)
+
+ expect(design_response).to eq(
+ 'id' => design.to_global_id.to_s,
+ 'event' => 'CREATION',
+ 'fullPath' => design.full_path,
+ 'filename' => design.filename,
+ 'image' => design_image_url(design, ref: version_sha),
+ 'imageV432x230' => design_image_url(design, ref: version_sha, size: :v432x230)
+ )
+ end
+
+ context 'when the v432x230-sized design image has not been processed' do
+ before do
+ allow_next_instance_of(DesignManagement::DesignV432x230Uploader) do |uploader|
+ allow(uploader).to receive(:file).and_return(nil)
+ end
+ end
+
+ it 'returns nil for the v432x230-sized design image' do
+ post_graphql(query, current_user: current_user)
+
+ expect(design_response['imageV432x230']).to be_nil
+ end
+ end
+
+ describe 'pagination' do
+ before do
+ create_list(:design, 5, :with_file, issue: issue)
+ project.add_developer(current_user)
+ post_graphql(query, current_user: current_user)
+ end
+
+ let(:issue) { create(:issue) }
+
+ let(:end_cursor) { design_collection.dig('designs', 'pageInfo', 'endCursor') }
+
+ let(:ids) { issue.designs.order(:id).map { |d| global_id_of(d) } }
+
+ let(:query) { make_query(designs_fragment(first: 2)) }
+
+ let(:design_query_fields) { 'pageInfo { endCursor } edges { node { id } }' }
+
+ let(:cursored_query) do
+ make_query(designs_fragment(after: end_cursor))
+ end
+
+ def designs_fragment(params)
+ query_graphql_field(:designs, params, design_query_fields)
+ end
+
+ def response_ids(data = graphql_data)
+ path = %w[project issue designCollection designs edges]
+ data.dig(*path).map { |e| e.dig('node', 'id') }
+ end
+
+ it 'sorts designs for reliable pagination' do
+ expect(response_ids).to match_array(ids.take(2))
+
+ post_graphql(cursored_query, current_user: current_user)
+
+ new_data = Gitlab::Json.parse(response.body).fetch('data')
+
+ expect(response_ids(new_data)).to match_array(ids.drop(2))
+ end
+ end
+
+ context 'with versions' do
+ let_it_be(:version) { design.versions.take }
+ let(:design_query) do
+ <<~NODE
+ designs {
+ edges {
+ node {
+ filename
+ versions {
+ edges {
+ node {
+ id
+ sha
+ }
+ }
+ }
+ }
+ }
+ }
+ NODE
+ end
+
+ it 'includes the version id' do
+ post_graphql(query, current_user: current_user)
+
+ version_id = design_response['versions']['edges'].first['node']['id']
+
+ expect(version_id).to eq(version.to_global_id.to_s)
+ end
+
+ it 'includes the version sha' do
+ post_graphql(query, current_user: current_user)
+
+ version_sha = design_response['versions']['edges'].first['node']['sha']
+
+ expect(version_sha).to eq(version.sha)
+ end
+ end
+
+ describe 'viewing a design board at a particular version' do
+ let_it_be(:issue) { design.issue }
+ let_it_be(:second_design, reload: true) { create(:design, :with_smaller_image_versions, issue: issue, versions_count: 1) }
+ let_it_be(:deleted_design) { create(:design, :with_versions, issue: issue, deleted: true, versions_count: 1) }
+ let(:all_versions) { issue.design_versions.ordered.reverse }
+ let(:design_query) do
+ <<~NODE
+ designs(atVersion: "#{version.to_global_id}") {
+ edges {
+ node {
+ id
+ image
+ imageV432x230
+ event
+ versions {
+ edges {
+ node {
+ id
+ }
+ }
+ }
+ }
+ }
+ }
+ NODE
+ end
+ let(:design_response) do
+ design_collection['designs']['edges']
+ end
+
+ def global_id(object)
+ object.to_global_id.to_s
+ end
+
+ # Filters just design nodes from the larger `design_response`
+ def design_nodes
+ design_response.map do |response|
+ response['node']
+ end
+ end
+
+ # Filters just version nodes from the larger `design_response`
+ def version_nodes
+ design_response.map do |response|
+ response.dig('node', 'versions', 'edges')
+ end
+ end
+
+ context 'viewing the original version, when one design was created' do
+ let(:version) { all_versions.first }
+
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it 'only returns the first design' do
+ expect(design_nodes).to contain_exactly(
+ a_hash_including('id' => global_id(design))
+ )
+ end
+
+ it 'returns the correct full-sized design image' do
+ expect(design_nodes).to contain_exactly(
+ a_hash_including('image' => design_image_url(design, ref: version.sha))
+ )
+ end
+
+ it 'returns the correct v432x230-sized design image' do
+ expect(design_nodes).to contain_exactly(
+ a_hash_including('imageV432x230' => design_image_url(design, ref: version.sha, size: :v432x230))
+ )
+ end
+
+ it 'returns the correct event for the design in this version' do
+ expect(design_nodes).to contain_exactly(
+ a_hash_including('event' => 'CREATION')
+ )
+ end
+
+ it 'only returns one version record for the design (the original version)' do
+ expect(version_nodes).to eq([
+ [{ 'node' => { 'id' => global_id(version) } }]
+ ])
+ end
+ end
+
+ context 'viewing the second version, when one design was created' do
+ let(:version) { all_versions.second }
+
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it 'only returns the first two designs' do
+ expect(design_nodes).to contain_exactly(
+ a_hash_including('id' => global_id(design)),
+ a_hash_including('id' => global_id(second_design))
+ )
+ end
+
+ it 'returns the correct full-sized design images' do
+ expect(design_nodes).to contain_exactly(
+ a_hash_including('image' => design_image_url(design, ref: version.sha)),
+ a_hash_including('image' => design_image_url(second_design, ref: version.sha))
+ )
+ end
+
+ it 'returns the correct v432x230-sized design images' do
+ expect(design_nodes).to contain_exactly(
+ a_hash_including('imageV432x230' => design_image_url(design, ref: version.sha, size: :v432x230)),
+ a_hash_including('imageV432x230' => design_image_url(second_design, ref: version.sha, size: :v432x230))
+ )
+ end
+
+ it 'returns the correct events for the designs in this version' do
+ expect(design_nodes).to contain_exactly(
+ a_hash_including('event' => 'NONE'),
+ a_hash_including('event' => 'CREATION')
+ )
+ end
+
+ it 'returns the correct versions records for both designs' do
+ expect(version_nodes).to eq([
+ [{ 'node' => { 'id' => global_id(design.versions.first) } }],
+ [{ 'node' => { 'id' => global_id(second_design.versions.first) } }]
+ ])
+ end
+ end
+
+ context 'viewing the last version, when one design was deleted and one was updated' do
+ let(:version) { all_versions.last }
+ let!(:second_design_update) do
+ create(:design_action, :with_image_v432x230, design: second_design, version: version, event: 'modification')
+ end
+
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it 'does not include the deleted design' do
+ # The design does exist in the version
+ expect(version.designs).to include(deleted_design)
+
+ # But the GraphQL API does not include it in these results
+ expect(design_nodes).to contain_exactly(
+ a_hash_including('id' => global_id(design)),
+ a_hash_including('id' => global_id(second_design))
+ )
+ end
+
+ it 'returns the correct full-sized design images' do
+ expect(design_nodes).to contain_exactly(
+ a_hash_including('image' => design_image_url(design, ref: version.sha)),
+ a_hash_including('image' => design_image_url(second_design, ref: version.sha))
+ )
+ end
+
+ it 'returns the correct v432x230-sized design images' do
+ expect(design_nodes).to contain_exactly(
+ a_hash_including('imageV432x230' => design_image_url(design, ref: version.sha, size: :v432x230)),
+ a_hash_including('imageV432x230' => design_image_url(second_design, ref: version.sha, size: :v432x230))
+ )
+ end
+
+ it 'returns the correct events for the designs in this version' do
+ expect(design_nodes).to contain_exactly(
+ a_hash_including('event' => 'NONE'),
+ a_hash_including('event' => 'MODIFICATION')
+ )
+ end
+
+ it 'returns all versions records for the designs' do
+ expect(version_nodes).to eq([
+ [
+ { 'node' => { 'id' => global_id(design.versions.first) } }
+ ],
+ [
+ { 'node' => { 'id' => global_id(second_design.versions.second) } },
+ { 'node' => { 'id' => global_id(second_design.versions.first) } }
+ ]
+ ])
+ end
+ end
+ end
+
+ describe 'a design with note annotations' do
+ let_it_be(:note) { create(:diff_note_on_design, noteable: design) }
+
+ let(:design_query) do
+ <<~NODE
+ designs {
+ edges {
+ node {
+ notesCount
+ notes {
+ edges {
+ node {
+ id
+ }
+ }
+ }
+ }
+ }
+ }
+ NODE
+ end
+
+ let(:design_response) do
+ design_collection['designs']['edges'].first['node']
+ end
+
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it 'returns the notes for the design' do
+ expect(design_response.dig('notes', 'edges')).to eq(
+ ['node' => { 'id' => note.to_global_id.to_s }]
+ )
+ end
+
+ it 'returns a note_count for the design' do
+ expect(design_response['notesCount']).to eq(1)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/issue/designs/notes_spec.rb b/spec/requests/api/graphql/project/issue/designs/notes_spec.rb
new file mode 100644
index 00000000000..0207bb9123a
--- /dev/null
+++ b/spec/requests/api/graphql/project/issue/designs/notes_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Getting designs related to an issue' do
+ include GraphqlHelpers
+ include DesignManagementTestHelpers
+
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:design) { create(:design, :with_file, versions_count: 1, issue: issue) }
+ let_it_be(:current_user) { project.owner }
+ let_it_be(:note) { create(:diff_note_on_design, noteable: design, project: project) }
+
+ before do
+ enable_design_management
+
+ note
+ end
+
+ it_behaves_like 'a working graphql query' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+ end
+
+ it 'is not too deep for anonymous users' do
+ note_fields = <<~FIELDS
+ id
+ author { name }
+ FIELDS
+
+ post_graphql(query(note_fields), current_user: nil)
+
+ designs_data = graphql_data['project']['issue']['designs']['designs']
+ design_data = designs_data['edges'].first['node']
+ note_data = design_data['notes']['edges'].first['node']
+
+ expect(note_data['id']).to eq(note.to_global_id.to_s)
+ end
+
+ def query(note_fields = all_graphql_fields_for(Note))
+ design_node = <<~NODE
+ designs {
+ edges {
+ node {
+ notes {
+ edges {
+ node {
+ #{note_fields}
+ }
+ }
+ }
+ }
+ }
+ }
+ NODE
+ graphql_query_for(
+ 'project',
+ { 'fullPath' => design.project.full_path },
+ query_graphql_field(
+ 'issue',
+ { iid: design.issue.iid.to_s },
+ query_graphql_field(
+ 'designs', {}, design_node
+ )
+ )
+ )
+ end
+end
diff --git a/spec/requests/api/graphql/project/issue_spec.rb b/spec/requests/api/graphql/project/issue_spec.rb
new file mode 100644
index 00000000000..92d2f9d0d31
--- /dev/null
+++ b/spec/requests/api/graphql/project/issue_spec.rb
@@ -0,0 +1,189 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Query.project(fullPath).issue(iid)' do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:issue_b) { create(:issue, project: project) }
+ let_it_be(:developer) { create(:user) }
+ let(:current_user) { developer }
+
+ let_it_be(:project_params) { { 'fullPath' => project.full_path } }
+ let_it_be(:issue_params) { { 'iid' => issue.iid.to_s } }
+ let_it_be(:issue_fields) { 'title' }
+
+ let(:query) do
+ graphql_query_for('project', project_params, project_fields)
+ end
+
+ let(:project_fields) do
+ query_graphql_field(:issue, issue_params, issue_fields)
+ end
+
+ shared_examples 'being able to fetch a design-like object by ID' do
+ let(:design) { design_a }
+ let(:path) { %w[project issue designCollection] + [GraphqlHelpers.fieldnamerize(object_field_name)] }
+
+ let(:design_fields) do
+ [
+ query_graphql_field(:filename),
+ query_graphql_field(:project, nil, query_graphql_field(:id))
+ ]
+ end
+
+ let(:design_collection_fields) do
+ query_graphql_field(object_field_name, object_params, object_fields)
+ end
+
+ let(:object_fields) { design_fields }
+
+ context 'the ID is passed' do
+ let(:object_params) { { id: global_id_of(object) } }
+ let(:result_fields) { {} }
+
+ let(:expected_fields) do
+ result_fields.merge({ 'filename' => design.filename, 'project' => id_hash(project) })
+ end
+
+ it 'retrieves the object' do
+ post_query
+
+ data = graphql_data.dig(*path)
+
+ expect(data).to match(a_hash_including(expected_fields))
+ end
+
+ context 'the user is unauthorized' do
+ let(:current_user) { create(:user) }
+
+ it_behaves_like 'a failure to find anything'
+ end
+ end
+
+ context 'without parameters' do
+ let(:object_params) { nil }
+
+ it 'raises an error' do
+ post_query
+
+ expect(graphql_errors).to include(no_argument_error)
+ end
+ end
+
+ context 'attempting to retrieve an object from a different issue' do
+ let(:object_params) { { id: global_id_of(object_on_other_issue) } }
+
+ it_behaves_like 'a failure to find anything'
+ end
+ end
+
+ before do
+ project.add_developer(developer)
+ end
+
+ let(:post_query) { post_graphql(query, current_user: current_user) }
+
+ describe '.designCollection' do
+ include DesignManagementTestHelpers
+
+ let_it_be(:design_a) { create(:design, issue: issue) }
+ let_it_be(:version_a) { create(:design_version, issue: issue, created_designs: [design_a]) }
+
+ let(:issue_fields) do
+ query_graphql_field(:design_collection, dc_params, design_collection_fields)
+ end
+
+ let(:dc_params) { nil }
+ let(:design_collection_fields) { nil }
+
+ before do
+ enable_design_management
+ end
+
+ describe '.design' do
+ let(:object) { design }
+ let(:object_field_name) { :design }
+
+ let(:no_argument_error) do
+ custom_graphql_error(path, a_string_matching(%r/id or filename/))
+ end
+
+ let_it_be(:object_on_other_issue) { create(:design, issue: issue_b) }
+
+ it_behaves_like 'being able to fetch a design-like object by ID'
+
+ it_behaves_like 'being able to fetch a design-like object by ID' do
+ let(:object_params) { { filename: design.filename } }
+ end
+ end
+
+ describe '.version' do
+ let(:version) { version_a }
+ let(:path) { %w[project issue designCollection version] }
+
+ let(:design_collection_fields) do
+ query_graphql_field(:version, version_params, 'id sha')
+ end
+
+ context 'no parameters' do
+ let(:version_params) { nil }
+
+ it 'raises an error' do
+ post_query
+
+ expect(graphql_errors).to include(custom_graphql_error(path, a_string_matching(%r/id or sha/)))
+ end
+ end
+
+ shared_examples 'a successful query for a version' do
+ it 'finds the version' do
+ post_query
+
+ data = graphql_data.dig(*path)
+
+ expect(data).to match(
+ a_hash_including('id' => global_id_of(version),
+ 'sha' => version.sha)
+ )
+ end
+ end
+
+ context '(sha: STRING_TYPE)' do
+ let(:version_params) { { sha: version.sha } }
+
+ it_behaves_like 'a successful query for a version'
+ end
+
+ context '(id: ID_TYPE)' do
+ let(:version_params) { { id: global_id_of(version) } }
+
+ it_behaves_like 'a successful query for a version'
+ end
+ end
+
+ describe '.designAtVersion' do
+ it_behaves_like 'being able to fetch a design-like object by ID' do
+ let(:object) { build(:design_at_version, design: design, version: version) }
+ let(:object_field_name) { :design_at_version }
+
+ let(:version) { version_a }
+
+ let(:result_fields) { { 'version' => id_hash(version) } }
+ let(:object_fields) do
+ design_fields + [query_graphql_field(:version, nil, query_graphql_field(:id))]
+ end
+
+ let(:no_argument_error) { missing_required_argument(path, :id) }
+
+ let(:object_on_other_issue) { build(:design_at_version, issue: issue_b) }
+ end
+ end
+ end
+
+ def id_hash(object)
+ a_hash_including('id' => global_id_of(object))
+ end
+end
diff --git a/spec/requests/api/graphql/project/issues_spec.rb b/spec/requests/api/graphql/project/issues_spec.rb
index 4ce7a3912a3..91fce3eed92 100644
--- a/spec/requests/api/graphql/project/issues_spec.rb
+++ b/spec/requests/api/graphql/project/issues_spec.rb
@@ -45,8 +45,8 @@ describe 'getting an issue list for a project' do
it 'includes discussion locked' do
post_graphql(query, current_user: current_user)
- expect(issues_data[0]['node']['discussionLocked']).to eq false
- expect(issues_data[1]['node']['discussionLocked']).to eq true
+ expect(issues_data[0]['node']['discussionLocked']).to eq(false)
+ expect(issues_data[1]['node']['discussionLocked']).to eq(true)
end
context 'when limiting the number of results' do
@@ -79,7 +79,7 @@ describe 'getting an issue list for a project' do
post_graphql(query)
- expect(issues_data).to eq []
+ expect(issues_data).to eq([])
end
end
@@ -118,131 +118,138 @@ describe 'getting an issue list for a project' do
end
describe 'sorting and pagination' do
- let(:start_cursor) { graphql_data['project']['issues']['pageInfo']['startCursor'] }
- let(:end_cursor) { graphql_data['project']['issues']['pageInfo']['endCursor'] }
+ let_it_be(:data_path) { [:project, :issues] }
- context 'when sorting by due date' do
- let(:sort_project) { create(:project, :public) }
-
- let!(:due_issue1) { create(:issue, project: sort_project, due_date: 3.days.from_now) }
- let!(:due_issue2) { create(:issue, project: sort_project, due_date: nil) }
- let!(:due_issue3) { create(:issue, project: sort_project, due_date: 2.days.ago) }
- let!(:due_issue4) { create(:issue, project: sort_project, due_date: nil) }
- let!(:due_issue5) { create(:issue, project: sort_project, due_date: 1.day.ago) }
-
- let(:params) { 'sort: DUE_DATE_ASC' }
-
- def query(issue_params = params)
- graphql_query_for(
- 'project',
- { 'fullPath' => sort_project.full_path },
- <<~ISSUES
- issues(#{issue_params}) {
- pageInfo {
- endCursor
- }
- edges {
- node {
- iid
- dueDate
- }
- }
- }
- ISSUES
- )
- end
+ def pagination_query(params, page_info)
+ graphql_query_for(
+ 'project',
+ { 'fullPath' => sort_project.full_path },
+ "issues(#{params}) { #{page_info} edges { node { iid dueDate } } }"
+ )
+ end
- before do
- post_graphql(query, current_user: current_user)
- end
+ def pagination_results_data(data)
+ data.map { |issue| issue.dig('node', 'iid').to_i }
+ end
- it_behaves_like 'a working graphql query'
+ context 'when sorting by due date' do
+ let_it_be(:sort_project) { create(:project, :public) }
+ let_it_be(:due_issue1) { create(:issue, project: sort_project, due_date: 3.days.from_now) }
+ let_it_be(:due_issue2) { create(:issue, project: sort_project, due_date: nil) }
+ let_it_be(:due_issue3) { create(:issue, project: sort_project, due_date: 2.days.ago) }
+ let_it_be(:due_issue4) { create(:issue, project: sort_project, due_date: nil) }
+ let_it_be(:due_issue5) { create(:issue, project: sort_project, due_date: 1.day.ago) }
context 'when ascending' do
- it 'sorts issues' do
- expect(grab_iids).to eq [due_issue3.iid, due_issue5.iid, due_issue1.iid, due_issue4.iid, due_issue2.iid]
- end
-
- context 'when paginating' do
- let(:params) { 'sort: DUE_DATE_ASC, first: 2' }
-
- it 'sorts issues' do
- expect(grab_iids).to eq [due_issue3.iid, due_issue5.iid]
-
- cursored_query = query("sort: DUE_DATE_ASC, after: \"#{end_cursor}\"")
- post_graphql(cursored_query, current_user: current_user)
- response_data = JSON.parse(response.body)['data']['project']['issues']['edges']
-
- expect(grab_iids(response_data)).to eq [due_issue1.iid, due_issue4.iid, due_issue2.iid]
- end
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { 'DUE_DATE_ASC' }
+ let(:first_param) { 2 }
+ let(:expected_results) { [due_issue3.iid, due_issue5.iid, due_issue1.iid, due_issue4.iid, due_issue2.iid] }
end
end
context 'when descending' do
- let(:params) { 'sort: DUE_DATE_DESC' }
-
- it 'sorts issues' do
- expect(grab_iids).to eq [due_issue1.iid, due_issue5.iid, due_issue3.iid, due_issue4.iid, due_issue2.iid]
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { 'DUE_DATE_DESC' }
+ let(:first_param) { 2 }
+ let(:expected_results) { [due_issue1.iid, due_issue5.iid, due_issue3.iid, due_issue4.iid, due_issue2.iid] }
end
+ end
+ end
- context 'when paginating' do
- let(:params) { 'sort: DUE_DATE_DESC, first: 2' }
-
- it 'sorts issues' do
- expect(grab_iids).to eq [due_issue1.iid, due_issue5.iid]
-
- cursored_query = query("sort: DUE_DATE_DESC, after: \"#{end_cursor}\"")
- post_graphql(cursored_query, current_user: current_user)
- response_data = JSON.parse(response.body)['data']['project']['issues']['edges']
+ context 'when sorting by relative position' do
+ let_it_be(:sort_project) { create(:project, :public) }
+ let_it_be(:relative_issue1) { create(:issue, project: sort_project, relative_position: 2000) }
+ let_it_be(:relative_issue2) { create(:issue, project: sort_project, relative_position: nil) }
+ let_it_be(:relative_issue3) { create(:issue, project: sort_project, relative_position: 1000) }
+ let_it_be(:relative_issue4) { create(:issue, project: sort_project, relative_position: nil) }
+ let_it_be(:relative_issue5) { create(:issue, project: sort_project, relative_position: 500) }
- expect(grab_iids(response_data)).to eq [due_issue3.iid, due_issue4.iid, due_issue2.iid]
- end
+ context 'when ascending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { 'RELATIVE_POSITION_ASC' }
+ let(:first_param) { 2 }
+ let(:expected_results) { [relative_issue5.iid, relative_issue3.iid, relative_issue1.iid, relative_issue4.iid, relative_issue2.iid] }
end
end
end
- context 'when sorting by relative position' do
- let(:sort_project) { create(:project, :public) }
-
- let!(:relative_issue1) { create(:issue, project: sort_project, relative_position: 2000) }
- let!(:relative_issue2) { create(:issue, project: sort_project, relative_position: nil) }
- let!(:relative_issue3) { create(:issue, project: sort_project, relative_position: 1000) }
- let!(:relative_issue4) { create(:issue, project: sort_project, relative_position: nil) }
- let!(:relative_issue5) { create(:issue, project: sort_project, relative_position: 500) }
-
- let(:params) { 'sort: RELATIVE_POSITION_ASC' }
-
- def query(issue_params = params)
- graphql_query_for(
- 'project',
- { 'fullPath' => sort_project.full_path },
- "issues(#{issue_params}) { pageInfo { endCursor} edges { node { iid dueDate } } }"
- )
+ context 'when sorting by priority' do
+ let_it_be(:sort_project) { create(:project, :public) }
+ let_it_be(:early_milestone) { create(:milestone, project: sort_project, due_date: 10.days.from_now) }
+ let_it_be(:late_milestone) { create(:milestone, project: sort_project, due_date: 30.days.from_now) }
+ let_it_be(:priority_label1) { create(:label, project: sort_project, priority: 1) }
+ let_it_be(:priority_label2) { create(:label, project: sort_project, priority: 5) }
+ let_it_be(:priority_issue1) { create(:issue, project: sort_project, labels: [priority_label1], milestone: late_milestone) }
+ let_it_be(:priority_issue2) { create(:issue, project: sort_project, labels: [priority_label2]) }
+ let_it_be(:priority_issue3) { create(:issue, project: sort_project, milestone: early_milestone) }
+ let_it_be(:priority_issue4) { create(:issue, project: sort_project) }
+
+ context 'when ascending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { 'PRIORITY_ASC' }
+ let(:first_param) { 2 }
+ let(:expected_results) { [priority_issue3.iid, priority_issue1.iid, priority_issue2.iid, priority_issue4.iid] }
+ end
end
- before do
- post_graphql(query, current_user: current_user)
+ context 'when descending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { 'PRIORITY_DESC' }
+ let(:first_param) { 2 }
+ let(:expected_results) { [priority_issue1.iid, priority_issue3.iid, priority_issue2.iid, priority_issue4.iid] }
+ end
end
+ end
- it_behaves_like 'a working graphql query'
+ context 'when sorting by label priority' do
+ let_it_be(:sort_project) { create(:project, :public) }
+ let_it_be(:label1) { create(:label, project: sort_project, priority: 1) }
+ let_it_be(:label2) { create(:label, project: sort_project, priority: 5) }
+ let_it_be(:label3) { create(:label, project: sort_project, priority: 10) }
+ let_it_be(:label_issue1) { create(:issue, project: sort_project, labels: [label1]) }
+ let_it_be(:label_issue2) { create(:issue, project: sort_project, labels: [label2]) }
+ let_it_be(:label_issue3) { create(:issue, project: sort_project, labels: [label1, label3]) }
+ let_it_be(:label_issue4) { create(:issue, project: sort_project) }
context 'when ascending' do
- it 'sorts issues' do
- expect(grab_iids).to eq [relative_issue5.iid, relative_issue3.iid, relative_issue1.iid, relative_issue4.iid, relative_issue2.iid]
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { 'LABEL_PRIORITY_ASC' }
+ let(:first_param) { 2 }
+ let(:expected_results) { [label_issue3.iid, label_issue1.iid, label_issue2.iid, label_issue4.iid] }
end
+ end
- context 'when paginating' do
- let(:params) { 'sort: RELATIVE_POSITION_ASC, first: 2' }
+ context 'when descending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { 'LABEL_PRIORITY_DESC' }
+ let(:first_param) { 2 }
+ let(:expected_results) { [label_issue2.iid, label_issue3.iid, label_issue1.iid, label_issue4.iid] }
+ end
+ end
+ end
- it 'sorts issues' do
- expect(grab_iids).to eq [relative_issue5.iid, relative_issue3.iid]
+ context 'when sorting by milestone due date' do
+ let_it_be(:sort_project) { create(:project, :public) }
+ let_it_be(:early_milestone) { create(:milestone, project: sort_project, due_date: 10.days.from_now) }
+ let_it_be(:late_milestone) { create(:milestone, project: sort_project, due_date: 30.days.from_now) }
+ let_it_be(:milestone_issue1) { create(:issue, project: sort_project) }
+ let_it_be(:milestone_issue2) { create(:issue, project: sort_project, milestone: early_milestone) }
+ let_it_be(:milestone_issue3) { create(:issue, project: sort_project, milestone: late_milestone) }
- cursored_query = query("sort: RELATIVE_POSITION_ASC, after: \"#{end_cursor}\"")
- post_graphql(cursored_query, current_user: current_user)
- response_data = JSON.parse(response.body)['data']['project']['issues']['edges']
+ context 'when ascending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { 'MILESTONE_DUE_ASC' }
+ let(:first_param) { 2 }
+ let(:expected_results) { [milestone_issue2.iid, milestone_issue3.iid, milestone_issue1.iid] }
+ end
+ end
- expect(grab_iids(response_data)).to eq [relative_issue1.iid, relative_issue4.iid, relative_issue2.iid]
- end
+ context 'when descending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { 'MILESTONE_DUE_DESC' }
+ let(:first_param) { 2 }
+ let(:expected_results) { [milestone_issue3.iid, milestone_issue2.iid, milestone_issue1.iid] }
end
end
end
diff --git a/spec/requests/api/graphql/project/jira_import_spec.rb b/spec/requests/api/graphql/project/jira_import_spec.rb
index 43e1bb13342..e063068eb1a 100644
--- a/spec/requests/api/graphql/project/jira_import_spec.rb
+++ b/spec/requests/api/graphql/project/jira_import_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe 'query jira import data' do
+describe 'query Jira import data' do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
@@ -18,6 +18,7 @@ describe 'query jira import data' do
jiraImports {
nodes {
jiraProjectKey
+ createdAt
scheduledAt
scheduledBy {
username