1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
# frozen_string_literal: true
# Use this for testing how a GraphQL query handles sorting and pagination.
# This is particularly important when using keyset pagination connection,
# which is the default for ActiveRecord relations, as certain sort keys
# might not be supportable.
#
# sort_param: the value to specify the sort
# data_path: the keys necessary to dig into the return GraphQL data to get the
# returned results
# first_param: number of items expected (like a page size)
# all_records: array of comparison data of all items sorted correctly
# pagination_query: method that specifies the GraphQL query
# pagination_results_data: method that extracts the sorted data used to compare against
# the expected results
#
# Example:
# describe 'sorting and pagination' do
# let_it_be(:sort_project) { create(:project, :public) }
# let(:data_path) { [:project, :issues] }
#
# def pagination_query(arguments)
# graphql_query_for(:project, { full_path: sort_project.full_path },
# query_nodes(:issues, :iid, include_pagination_info: true, args: arguments)
# )
# end
#
# # A method transforming nodes to data to match against
# # default: the identity function
# def pagination_results_data(issues)
# issues.map { |issue| issue['iid].to_i }
# end
#
# context 'when sorting by weight' do
# let_it_be(:issues) { make_some_issues_with_weights }
#
# context 'when ascending' do
# let(:ordered_issues) { issues.sort_by(&:weight) }
#
# it_behaves_like 'sorted paginated query' do
# let(:sort_param) { :WEIGHT_ASC }
# let(:first_param) { 2 }
# let(:all_records) { ordered_issues.map(&:iid) }
# end
# end
#
RSpec.shared_examples 'sorted paginated query' do |conditions = {}|
# Provided as a convenience when constructing queries using string concatenation
let(:page_info) { 'pageInfo { startCursor endCursor }' }
# Convenience for using default implementation of pagination_results_data
let(:node_path) { ['id'] }
it_behaves_like 'requires variables' do
let(:required_variables) { [:sort_param, :first_param, :all_records, :data_path, :current_user] }
end
describe do
let(:sort_argument) { graphql_args(sort: sort_param) }
let(:params) { sort_argument }
# Convenience helper for the large number of queries defined as a projection
# from some root value indexed by full_path to a collection of objects with IID
def nested_internal_id_query(root_field, parent, field, args, selection: :iid)
graphql_query_for(root_field, { full_path: parent.full_path },
query_nodes(field, selection, args: args, include_pagination_info: true)
)
end
def pagination_query(params)
raise('pagination_query(params) must be defined in the test, see example in comment') unless defined?(super)
super
end
def pagination_results_data(nodes)
if defined?(super)
super(nodes)
else
nodes.map { |n| n.dig(*node_path) }
end
end
def results
nodes = graphql_dig_at(graphql_data(fresh_response_data), *data_path, :nodes)
pagination_results_data(nodes)
end
def end_cursor
graphql_dig_at(graphql_data(fresh_response_data), *data_path, :page_info, :end_cursor)
end
def start_cursor
graphql_dig_at(graphql_data(fresh_response_data), *data_path, :page_info, :start_cursor)
end
let(:query) { pagination_query(params) }
before do
post_graphql(query, current_user: current_user)
end
context 'when sorting' do
it 'sorts correctly' do
expect(results).to eq all_records
end
context 'when paginating' do
let(:params) { sort_argument.merge(first: first_param) }
let(:first_page) { all_records.first(first_param) }
let(:rest) { all_records.drop(first_param) }
it 'paginates correctly' do
expect(results).to eq first_page
fwds = pagination_query(sort_argument.merge(after: end_cursor))
post_graphql(fwds, current_user: current_user)
expect(results).to eq rest
bwds = pagination_query(sort_argument.merge(before: start_cursor))
post_graphql(bwds, current_user: current_user)
expect(results).to eq first_page
end
end
context 'when last and sort params are present', if: conditions[:is_reversible] do
let(:params) { sort_argument.merge(last: 1) }
it 'fetches last elements without error' do
post_graphql(pagination_query(params), current_user: current_user)
expect(results.first).to eq(all_records.last)
end
end
end
end
end
|