plan_my_stuff 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +595 -0
- data/CONFIGURATION.md +487 -0
- data/README.md +612 -88
- data/app/controllers/plan_my_stuff/application_controller.rb +27 -5
- data/app/controllers/plan_my_stuff/comments_controller.rb +50 -19
- data/app/controllers/plan_my_stuff/issues/approvals_controller.rb +127 -0
- data/app/controllers/plan_my_stuff/issues/closures_controller.rb +53 -0
- data/app/controllers/plan_my_stuff/issues/links_controller.rb +129 -0
- data/app/controllers/plan_my_stuff/issues/takes_controller.rb +161 -0
- data/app/controllers/plan_my_stuff/issues/testings_controller.rb +82 -0
- data/app/controllers/plan_my_stuff/issues/viewers_controller.rb +62 -0
- data/app/controllers/plan_my_stuff/issues/waitings_controller.rb +55 -0
- data/app/controllers/plan_my_stuff/issues_controller.rb +53 -70
- data/app/controllers/plan_my_stuff/labels_controller.rb +32 -10
- data/app/controllers/plan_my_stuff/project_items/assignments_controller.rb +88 -0
- data/app/controllers/plan_my_stuff/project_items/statuses_controller.rb +44 -0
- data/app/controllers/plan_my_stuff/project_items_controller.rb +32 -69
- data/app/controllers/plan_my_stuff/projects_controller.rb +81 -3
- data/app/controllers/plan_my_stuff/testing_project_items/results_controller.rb +67 -0
- data/app/controllers/plan_my_stuff/testing_project_items_controller.rb +49 -0
- data/app/controllers/plan_my_stuff/testing_projects_controller.rb +121 -0
- data/app/controllers/plan_my_stuff/webhooks/aws_controller.rb +202 -0
- data/app/controllers/plan_my_stuff/webhooks/github_controller.rb +371 -0
- data/app/jobs/plan_my_stuff/application_job.rb +8 -0
- data/app/jobs/plan_my_stuff/reminders_sweep_job.rb +75 -0
- data/app/views/plan_my_stuff/comments/edit.html.erb +1 -3
- data/app/views/plan_my_stuff/comments/partials/_form.html.erb +8 -0
- data/app/views/plan_my_stuff/issues/edit.html.erb +2 -4
- data/app/views/plan_my_stuff/issues/index.html.erb +5 -5
- data/app/views/plan_my_stuff/issues/new.html.erb +2 -4
- data/app/views/plan_my_stuff/issues/partials/_approvals.html.erb +108 -0
- data/app/views/plan_my_stuff/issues/partials/_form.html.erb +11 -6
- data/app/views/plan_my_stuff/issues/partials/_labels.html.erb +4 -3
- data/app/views/plan_my_stuff/issues/partials/_links.html.erb +113 -0
- data/app/views/plan_my_stuff/issues/partials/_viewers.html.erb +4 -3
- data/app/views/plan_my_stuff/issues/show.html.erb +67 -6
- data/app/views/plan_my_stuff/partials/_flash.html.erb +3 -0
- data/app/views/plan_my_stuff/projects/edit.html.erb +5 -0
- data/app/views/plan_my_stuff/projects/index.html.erb +18 -2
- data/app/views/plan_my_stuff/projects/new.html.erb +5 -0
- data/app/views/plan_my_stuff/projects/partials/_form.html.erb +30 -0
- data/app/views/plan_my_stuff/projects/show.html.erb +30 -11
- data/app/views/plan_my_stuff/testing_project_items/new.html.erb +10 -0
- data/app/views/plan_my_stuff/testing_project_items/results/new.html.erb +20 -0
- data/app/views/plan_my_stuff/testing_projects/edit.html.erb +5 -0
- data/app/views/plan_my_stuff/testing_projects/new.html.erb +5 -0
- data/app/views/plan_my_stuff/testing_projects/partials/_form.html.erb +40 -0
- data/app/views/plan_my_stuff/testing_projects/partials/_item.html.erb +52 -0
- data/app/views/plan_my_stuff/testing_projects/partials/items/_form.html.erb +36 -0
- data/app/views/plan_my_stuff/testing_projects/show.html.erb +65 -0
- data/config/routes.rb +43 -15
- data/lib/generators/plan_my_stuff/install/templates/initializer.rb +302 -20
- data/lib/plan_my_stuff/application_record.rb +158 -1
- data/lib/plan_my_stuff/approval.rb +88 -0
- data/lib/plan_my_stuff/archive/sweep.rb +85 -0
- data/lib/plan_my_stuff/archive.rb +12 -0
- data/lib/plan_my_stuff/attachment.rb +83 -0
- data/lib/plan_my_stuff/attachment_uploader.rb +245 -0
- data/lib/plan_my_stuff/aws_sns_simulator.rb +116 -0
- data/lib/plan_my_stuff/base_metadata.rb +25 -28
- data/lib/plan_my_stuff/base_project.rb +502 -0
- data/lib/plan_my_stuff/base_project_extractions/graphql_hydration.rb +186 -0
- data/lib/plan_my_stuff/base_project_item.rb +588 -0
- data/lib/plan_my_stuff/base_project_metadata.rb +16 -0
- data/lib/plan_my_stuff/cache.rb +197 -0
- data/lib/plan_my_stuff/client.rb +139 -64
- data/lib/plan_my_stuff/comment.rb +225 -100
- data/lib/plan_my_stuff/comment_metadata.rb +68 -5
- data/lib/plan_my_stuff/configuration.rb +459 -28
- data/lib/plan_my_stuff/custom_fields.rb +96 -12
- data/lib/plan_my_stuff/engine.rb +14 -2
- data/lib/plan_my_stuff/errors.rb +65 -5
- data/lib/plan_my_stuff/graphql/queries.rb +454 -0
- data/lib/plan_my_stuff/issue.rb +1097 -166
- data/lib/plan_my_stuff/issue_extractions/approvals.rb +370 -0
- data/lib/plan_my_stuff/issue_extractions/links.rb +525 -0
- data/lib/plan_my_stuff/issue_extractions/viewers.rb +75 -0
- data/lib/plan_my_stuff/issue_extractions/waiting.rb +171 -0
- data/lib/plan_my_stuff/issue_field.rb +126 -0
- data/lib/plan_my_stuff/issue_field_translation.rb +67 -0
- data/lib/plan_my_stuff/issue_field_value_set.rb +68 -0
- data/lib/plan_my_stuff/issue_metadata.rb +132 -21
- data/lib/plan_my_stuff/label.rb +100 -13
- data/lib/plan_my_stuff/link.rb +144 -0
- data/lib/plan_my_stuff/markdown.rb +13 -7
- data/lib/plan_my_stuff/metadata_parser.rb +51 -12
- data/lib/plan_my_stuff/notifications.rb +148 -0
- data/lib/plan_my_stuff/pipeline/completed_sweep.rb +46 -0
- data/lib/plan_my_stuff/pipeline/issue_linker.rb +62 -0
- data/lib/plan_my_stuff/pipeline/status.rb +40 -0
- data/lib/plan_my_stuff/pipeline/testing.rb +23 -0
- data/lib/plan_my_stuff/pipeline.rb +310 -0
- data/lib/plan_my_stuff/project.rb +63 -465
- data/lib/plan_my_stuff/project_item.rb +3 -409
- data/lib/plan_my_stuff/project_item_metadata.rb +55 -0
- data/lib/plan_my_stuff/project_metadata.rb +47 -0
- data/lib/plan_my_stuff/reminders/closer.rb +70 -0
- data/lib/plan_my_stuff/reminders/fire.rb +129 -0
- data/lib/plan_my_stuff/reminders/sweep.rb +54 -0
- data/lib/plan_my_stuff/reminders.rb +12 -0
- data/lib/plan_my_stuff/repo.rb +145 -0
- data/lib/plan_my_stuff/test_helpers.rb +265 -25
- data/lib/plan_my_stuff/testing_project.rb +292 -0
- data/lib/plan_my_stuff/testing_project_item.rb +218 -0
- data/lib/plan_my_stuff/testing_project_metadata.rb +94 -0
- data/lib/plan_my_stuff/user_resolver.rb +24 -3
- data/lib/plan_my_stuff/verifier.rb +10 -0
- data/lib/plan_my_stuff/version.rb +2 -2
- data/lib/plan_my_stuff/webhook_replayer.rb +292 -0
- data/lib/plan_my_stuff.rb +55 -20
- data/lib/tasks/plan_my_stuff.rake +331 -0
- metadata +99 -4
|
@@ -3,17 +3,25 @@
|
|
|
3
3
|
require 'plan_my_stuff'
|
|
4
4
|
|
|
5
5
|
module PlanMyStuff
|
|
6
|
-
# Test support for
|
|
6
|
+
# Test support for apps that *consume* PlanMyStuff. Provides:
|
|
7
7
|
# - `PlanMyStuff.test_mode!` to stub all API calls
|
|
8
8
|
# - Factory-style builders: `build_issue`, `build_comment`, `build_project`
|
|
9
9
|
# - RSpec matchers: `expect_pms_issue_created`, `expect_pms_comment_created`, `expect_pms_item_moved`
|
|
10
10
|
#
|
|
11
|
-
# Usage:
|
|
11
|
+
# Usage (in a consuming Rails app):
|
|
12
12
|
# require 'plan_my_stuff/test_helpers'
|
|
13
13
|
#
|
|
14
14
|
# RSpec.configure do |config|
|
|
15
15
|
# config.include PlanMyStuff::TestHelpers
|
|
16
16
|
# end
|
|
17
|
+
#
|
|
18
|
+
# Not the harness for PlanMyStuff's own unit specs. Gem-internal specs go
|
|
19
|
+
# through VCR cassettes against a real GitHub sandbox (see `spec/support/vcr.rb`
|
|
20
|
+
# and the `:pms_vcr_configured` shared context). Calling `PlanMyStuff.test_mode!`
|
|
21
|
+
# inside the gem would monkey-patch the very class methods the cassettes were
|
|
22
|
+
# recorded against, defeating the VCR coverage. The builder utilities
|
|
23
|
+
# (`build_issue`, `build_comment`, `build_project`, `stub_approvals`) are safe
|
|
24
|
+
# to use inside the gem as plain factories - they don't replace any methods.
|
|
17
25
|
module TestHelpers
|
|
18
26
|
# Recorded actions during test mode, keyed by type:
|
|
19
27
|
# :issue_created, :comment_created, :item_moved
|
|
@@ -58,8 +66,8 @@ module PlanMyStuff
|
|
|
58
66
|
custom_fields: {},
|
|
59
67
|
}.merge(metadata))
|
|
60
68
|
|
|
61
|
-
issue.
|
|
62
|
-
issue.
|
|
69
|
+
issue.metadata = issue_metadata
|
|
70
|
+
issue.__send__(:persisted!)
|
|
63
71
|
|
|
64
72
|
body_comment = build_comment(
|
|
65
73
|
body: body,
|
|
@@ -98,8 +106,8 @@ module PlanMyStuff
|
|
|
98
106
|
custom_fields: {},
|
|
99
107
|
}.merge(metadata))
|
|
100
108
|
|
|
101
|
-
comment.
|
|
102
|
-
comment.
|
|
109
|
+
comment.metadata = comment_metadata
|
|
110
|
+
comment.__send__(:persisted!)
|
|
103
111
|
comment
|
|
104
112
|
end
|
|
105
113
|
|
|
@@ -124,7 +132,7 @@ module PlanMyStuff
|
|
|
124
132
|
statuses: status_options,
|
|
125
133
|
fields: [{ id: 'field_status', name: 'Status', options: status_options }],
|
|
126
134
|
)
|
|
127
|
-
project.
|
|
135
|
+
project.__send__(:persisted!)
|
|
128
136
|
|
|
129
137
|
project.items = items.map do |item_hash|
|
|
130
138
|
PlanMyStuff::ProjectItem.build(
|
|
@@ -143,6 +151,54 @@ module PlanMyStuff
|
|
|
143
151
|
|
|
144
152
|
project
|
|
145
153
|
end
|
|
154
|
+
|
|
155
|
+
# Sets approvals on an in-memory +Issue+ without hitting the API.
|
|
156
|
+
# Accepts user objects or integer user_ids.
|
|
157
|
+
#
|
|
158
|
+
# @param issue [PlanMyStuff::Issue]
|
|
159
|
+
# @param approved [Array<Object, Integer>] approvers who have approved
|
|
160
|
+
# @param pending [Array<Object, Integer>] approvers who have not yet approved
|
|
161
|
+
#
|
|
162
|
+
# @return [Array<PlanMyStuff::Approval>] the records written to +issue.metadata.approvals+
|
|
163
|
+
#
|
|
164
|
+
def stub_approvals(issue, approved: [], pending: [])
|
|
165
|
+
records =
|
|
166
|
+
pending.map { |u| PlanMyStuff::Approval.new(user_id: extract_user_id(u), status: 'pending') } +
|
|
167
|
+
approved.map do |u|
|
|
168
|
+
PlanMyStuff::Approval.new(user_id: extract_user_id(u), status: 'approved', approved_at: Time.current)
|
|
169
|
+
end
|
|
170
|
+
issue.metadata.approvals = records
|
|
171
|
+
records
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# @return [Integer]
|
|
175
|
+
def extract_user_id(user)
|
|
176
|
+
user.is_a?(Integer) ? user : PlanMyStuff::UserResolver.user_id(user)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Captures +*.plan_my_stuff+ +ActiveSupport::Notifications+ events fired
|
|
181
|
+
# inside the given block. Lets consuming apps assert that their code
|
|
182
|
+
# triggers gem events without threading subscriptions through every spec.
|
|
183
|
+
#
|
|
184
|
+
# Usage:
|
|
185
|
+
#
|
|
186
|
+
# events = PlanMyStuff::TestHelpers::Notifications.capture do
|
|
187
|
+
# PlanMyStuff::Issue.create!(...)
|
|
188
|
+
# end
|
|
189
|
+
# events.first[:name] # => "issue_created.plan_my_stuff"
|
|
190
|
+
# events.first[:payload] # => { issue:, user:, timestamp:, ... }
|
|
191
|
+
#
|
|
192
|
+
module Notifications
|
|
193
|
+
module_function
|
|
194
|
+
|
|
195
|
+
# @return [Array<Hash>] events as +{ name: String, payload: Hash }+
|
|
196
|
+
def capture(&)
|
|
197
|
+
events = []
|
|
198
|
+
callback = -> (name, _start, _finish, _id, payload) { events << { name: name, payload: payload } }
|
|
199
|
+
ActiveSupport::Notifications.subscribed(callback, /\.plan_my_stuff\z/, &)
|
|
200
|
+
events
|
|
201
|
+
end
|
|
146
202
|
end
|
|
147
203
|
|
|
148
204
|
# RSpec matchers included when the module is included in a test config.
|
|
@@ -218,6 +274,36 @@ module PlanMyStuff
|
|
|
218
274
|
expect_pms_action(:item_assigned, 'item to be assigned', **filters)
|
|
219
275
|
end
|
|
220
276
|
|
|
277
|
+
# @param filters [Hash] attribute filters (e.g. item_id:)
|
|
278
|
+
def expect_pms_pipeline_taken(**filters)
|
|
279
|
+
expect_pms_action(:pipeline_started, 'pipeline item to be taken', **filters)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# @param filters [Hash] attribute filters (e.g. item_id:)
|
|
283
|
+
def expect_pms_pipeline_in_review(**filters)
|
|
284
|
+
expect_pms_action(:pipeline_in_review, 'pipeline item marked in review', **filters)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# @param filters [Hash] attribute filters (e.g. item_id:)
|
|
288
|
+
def expect_pms_pipeline_testing(**filters)
|
|
289
|
+
expect_pms_action(:pipeline_testing, 'pipeline item moved to testing', **filters)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# @param filters [Hash] attribute filters (e.g. item_id:)
|
|
293
|
+
def expect_pms_pipeline_ready_for_release(**filters)
|
|
294
|
+
expect_pms_action(:pipeline_ready_for_release, 'pipeline item marked ready for release', **filters)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# @param filters [Hash] attribute filters (e.g. project_number:, commit_sha:)
|
|
298
|
+
def expect_pms_pipeline_deployment_started(**filters)
|
|
299
|
+
expect_pms_action(:pipeline_deployment_started, 'pipeline deployment started', **filters)
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# @param filters [Hash] attribute filters (e.g. item_id:, deployment_id:)
|
|
303
|
+
def expect_pms_pipeline_deployment_completed(**filters)
|
|
304
|
+
expect_pms_action(:pipeline_deployment_completed, 'pipeline deployment completed', **filters)
|
|
305
|
+
end
|
|
306
|
+
|
|
221
307
|
private
|
|
222
308
|
|
|
223
309
|
# @return [void]
|
|
@@ -228,7 +314,7 @@ module PlanMyStuff
|
|
|
228
314
|
|
|
229
315
|
expect(match).to(
|
|
230
316
|
be_truthy,
|
|
231
|
-
"Expected
|
|
317
|
+
"Expected PlanMyStuff #{description} with #{filters.inspect}, " \
|
|
232
318
|
"but recorded actions were: #{format_actions(type)}",
|
|
233
319
|
)
|
|
234
320
|
end
|
|
@@ -257,7 +343,7 @@ module PlanMyStuff
|
|
|
257
343
|
# @return [void]
|
|
258
344
|
#
|
|
259
345
|
def test_mode!
|
|
260
|
-
TestHelpers.recorded_actions = []
|
|
346
|
+
PlanMyStuff::TestHelpers.recorded_actions = []
|
|
261
347
|
return if @_test_mode
|
|
262
348
|
|
|
263
349
|
@_test_mode_originals = {}
|
|
@@ -265,10 +351,11 @@ module PlanMyStuff
|
|
|
265
351
|
stub_comment_class_methods!
|
|
266
352
|
stub_project_class_methods!
|
|
267
353
|
stub_project_item_class_methods!
|
|
354
|
+
stub_pipeline_methods!
|
|
268
355
|
@_test_mode = true
|
|
269
356
|
end
|
|
270
357
|
|
|
271
|
-
# Restores original class methods overwritten by test_mode!
|
|
358
|
+
# Restores original class / instance methods overwritten by test_mode!
|
|
272
359
|
#
|
|
273
360
|
# @return [void]
|
|
274
361
|
#
|
|
@@ -276,19 +363,24 @@ module PlanMyStuff
|
|
|
276
363
|
return unless @_test_mode
|
|
277
364
|
|
|
278
365
|
(@_test_mode_originals || {}).each do |klass, methods|
|
|
279
|
-
methods.each do |
|
|
280
|
-
|
|
366
|
+
methods.each do |key, original|
|
|
367
|
+
kind, name = key
|
|
368
|
+
if kind == :instance
|
|
369
|
+
klass.define_method(name, original)
|
|
370
|
+
else
|
|
371
|
+
klass.define_singleton_method(name, original)
|
|
372
|
+
end
|
|
281
373
|
end
|
|
282
374
|
end
|
|
283
375
|
|
|
284
376
|
@_test_mode_originals = nil
|
|
285
377
|
@_test_mode = false
|
|
286
|
-
TestHelpers.recorded_actions = []
|
|
378
|
+
PlanMyStuff::TestHelpers.recorded_actions = []
|
|
287
379
|
end
|
|
288
380
|
|
|
289
381
|
private
|
|
290
382
|
|
|
291
|
-
# Saves the original method so it can be restored by exit_test_mode!
|
|
383
|
+
# Saves the original class method so it can be restored by exit_test_mode!
|
|
292
384
|
#
|
|
293
385
|
# @param klass [Class]
|
|
294
386
|
# @param method_name [Symbol]
|
|
@@ -297,13 +389,26 @@ module PlanMyStuff
|
|
|
297
389
|
#
|
|
298
390
|
def save_original(klass, method_name)
|
|
299
391
|
@_test_mode_originals[klass] ||= {}
|
|
300
|
-
@_test_mode_originals[klass][method_name] = klass.method(method_name)
|
|
392
|
+
@_test_mode_originals[klass][[:class, method_name]] = klass.method(method_name)
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# Saves the original instance method so it can be restored by exit_test_mode!
|
|
396
|
+
#
|
|
397
|
+
# @param klass [Class]
|
|
398
|
+
# @param method_name [Symbol]
|
|
399
|
+
#
|
|
400
|
+
# @return [void]
|
|
401
|
+
#
|
|
402
|
+
def save_instance_original(klass, method_name)
|
|
403
|
+
@_test_mode_originals[klass] ||= {}
|
|
404
|
+
@_test_mode_originals[klass][[:instance, method_name]] = klass.instance_method(method_name)
|
|
301
405
|
end
|
|
302
406
|
|
|
303
407
|
# @return [void]
|
|
304
408
|
def stub_issue_class_methods!
|
|
305
409
|
issue_mod = PlanMyStuff::Issue
|
|
306
|
-
%i[create! find list update!
|
|
410
|
+
%i[create! find list update!].each { |m| save_original(issue_mod, m) }
|
|
411
|
+
%i[add_viewers! remove_viewers!].each { |m| save_instance_original(issue_mod, m) }
|
|
307
412
|
|
|
308
413
|
issue_mod.define_singleton_method(:create!) do |**params|
|
|
309
414
|
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
@@ -316,10 +421,17 @@ module PlanMyStuff
|
|
|
316
421
|
},
|
|
317
422
|
}
|
|
318
423
|
|
|
424
|
+
resolved_repo =
|
|
425
|
+
if params[:repo]
|
|
426
|
+
PlanMyStuff::Repo.resolve!(params[:repo]).full_name
|
|
427
|
+
else
|
|
428
|
+
'TestOrg/TestRepo'
|
|
429
|
+
end
|
|
430
|
+
|
|
319
431
|
PlanMyStuff::TestHelpers.build_issue(
|
|
320
432
|
title: params[:title],
|
|
321
433
|
body: params[:body],
|
|
322
|
-
repo:
|
|
434
|
+
repo: resolved_repo,
|
|
323
435
|
labels: params[:labels] || [],
|
|
324
436
|
)
|
|
325
437
|
end
|
|
@@ -330,7 +442,8 @@ module PlanMyStuff
|
|
|
330
442
|
params: { number: number, repo: repo },
|
|
331
443
|
}
|
|
332
444
|
|
|
333
|
-
PlanMyStuff::
|
|
445
|
+
resolved_repo = repo ? PlanMyStuff::Repo.resolve!(repo).full_name : 'TestOrg/TestRepo'
|
|
446
|
+
PlanMyStuff::TestHelpers.build_issue(number: number, repo: resolved_repo)
|
|
334
447
|
end
|
|
335
448
|
|
|
336
449
|
issue_mod.define_singleton_method(:list) do |**params|
|
|
@@ -351,19 +464,19 @@ module PlanMyStuff
|
|
|
351
464
|
nil
|
|
352
465
|
end
|
|
353
466
|
|
|
354
|
-
issue_mod.
|
|
467
|
+
issue_mod.define_method(:add_viewers!) do |user_ids:, user: nil|
|
|
355
468
|
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
356
469
|
type: :viewers_added,
|
|
357
|
-
params:
|
|
470
|
+
params: { number: number, repo: repo&.to_s, user_ids: Array.wrap(user_ids), user: user },
|
|
358
471
|
}
|
|
359
472
|
|
|
360
473
|
nil
|
|
361
474
|
end
|
|
362
475
|
|
|
363
|
-
issue_mod.
|
|
476
|
+
issue_mod.define_method(:remove_viewers!) do |user_ids:, user: nil|
|
|
364
477
|
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
365
478
|
type: :viewers_removed,
|
|
366
|
-
params:
|
|
479
|
+
params: { number: number, repo: repo&.to_s, user_ids: Array.wrap(user_ids), user: user },
|
|
367
480
|
}
|
|
368
481
|
|
|
369
482
|
nil
|
|
@@ -443,9 +556,9 @@ module PlanMyStuff
|
|
|
443
556
|
# @return [void]
|
|
444
557
|
def stub_project_item_class_methods!
|
|
445
558
|
item_mod = PlanMyStuff::ProjectItem
|
|
446
|
-
%i[move_item create! assign].each { |m| save_original(item_mod, m) }
|
|
559
|
+
%i[move_item! create! assign!].each { |m| save_original(item_mod, m) }
|
|
447
560
|
|
|
448
|
-
item_mod.define_singleton_method(:move_item) do |**params|
|
|
561
|
+
item_mod.define_singleton_method(:move_item!) do |**params|
|
|
449
562
|
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
450
563
|
type: :item_moved,
|
|
451
564
|
params: {
|
|
@@ -484,7 +597,7 @@ module PlanMyStuff
|
|
|
484
597
|
)
|
|
485
598
|
end
|
|
486
599
|
|
|
487
|
-
item_mod.define_singleton_method(:assign) do |**params|
|
|
600
|
+
item_mod.define_singleton_method(:assign!) do |**params|
|
|
488
601
|
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
489
602
|
type: :item_assigned,
|
|
490
603
|
params: {
|
|
@@ -497,5 +610,132 @@ module PlanMyStuff
|
|
|
497
610
|
nil
|
|
498
611
|
end
|
|
499
612
|
end
|
|
613
|
+
|
|
614
|
+
# @return [void]
|
|
615
|
+
def stub_pipeline_methods!
|
|
616
|
+
pipeline_mod = PlanMyStuff::Pipeline
|
|
617
|
+
%i[
|
|
618
|
+
take! mark_in_review! request_testing! mark_ready_for_release! start_deployment! complete_deployment!
|
|
619
|
+
].each { |m| save_original(pipeline_mod, m) }
|
|
620
|
+
|
|
621
|
+
pipeline_mod.define_singleton_method(:take!) do |project_item, user: nil|
|
|
622
|
+
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
623
|
+
type: :pipeline_started,
|
|
624
|
+
params: {
|
|
625
|
+
item_id: project_item.respond_to?(:id) ? project_item.id : nil,
|
|
626
|
+
issue_number: project_item.respond_to?(:number) ? project_item.number : nil,
|
|
627
|
+
user: user,
|
|
628
|
+
},
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
nil
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
pipeline_mod.define_singleton_method(:mark_in_review!) do |project_item|
|
|
635
|
+
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
636
|
+
type: :pipeline_in_review,
|
|
637
|
+
params: {
|
|
638
|
+
item_id: project_item.respond_to?(:id) ? project_item.id : nil,
|
|
639
|
+
issue_number: project_item.respond_to?(:number) ? project_item.number : nil,
|
|
640
|
+
},
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
nil
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
pipeline_mod.define_singleton_method(:request_testing!) do |project_item, user: nil|
|
|
647
|
+
field_name = PlanMyStuff.configuration.pipeline_testing_field_name
|
|
648
|
+
value = PlanMyStuff.configuration.pipeline_testing_values.fetch(:active)
|
|
649
|
+
|
|
650
|
+
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
651
|
+
type: :pipeline_testing,
|
|
652
|
+
params: {
|
|
653
|
+
item_id: project_item.respond_to?(:id) ? project_item.id : nil,
|
|
654
|
+
issue_number: project_item.respond_to?(:number) ? project_item.number : nil,
|
|
655
|
+
field_name: field_name,
|
|
656
|
+
value: value,
|
|
657
|
+
user: user,
|
|
658
|
+
},
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if project_item.respond_to?(:field_values) && project_item.field_values.is_a?(Hash)
|
|
662
|
+
project_item.field_values[field_name] = value
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
nil
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
pipeline_mod.define_singleton_method(:mark_ready_for_release!) do |project_item|
|
|
669
|
+
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
670
|
+
type: :pipeline_ready_for_release,
|
|
671
|
+
params: {
|
|
672
|
+
item_id: project_item.respond_to?(:id) ? project_item.id : nil,
|
|
673
|
+
issue_number: project_item.respond_to?(:number) ? project_item.number : nil,
|
|
674
|
+
},
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
nil
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
pipeline_mod.define_singleton_method(:start_deployment!) do |project_number: nil, commit_sha: nil|
|
|
681
|
+
number =
|
|
682
|
+
project_number || PlanMyStuff.configuration.pipeline_project_number ||
|
|
683
|
+
PlanMyStuff.configuration.default_project_number
|
|
684
|
+
|
|
685
|
+
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
686
|
+
type: :pipeline_deployment_started,
|
|
687
|
+
params: {
|
|
688
|
+
project_number: number,
|
|
689
|
+
commit_sha: commit_sha,
|
|
690
|
+
},
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
[]
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
pipeline_mod.define_singleton_method(:complete_deployment!) do |project_item, deployment_id: nil|
|
|
697
|
+
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
698
|
+
type: :pipeline_deployment_completed,
|
|
699
|
+
params: {
|
|
700
|
+
item_id: project_item.respond_to?(:id) ? project_item.id : nil,
|
|
701
|
+
issue_number: project_item.respond_to?(:number) ? project_item.number : nil,
|
|
702
|
+
deployment_id: deployment_id,
|
|
703
|
+
},
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
nil
|
|
707
|
+
end
|
|
708
|
+
end
|
|
709
|
+
end
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
if defined?(RSpec::Matchers)
|
|
713
|
+
# Asserts that a +<name>.plan_my_stuff+ event was fired inside the block.
|
|
714
|
+
# Chain +.with(key: value, ...)+ to match on a payload subset.
|
|
715
|
+
#
|
|
716
|
+
# expect { Issue.create!(...) }.to(have_fired_event('issue_created.plan_my_stuff'))
|
|
717
|
+
# expect { Issue.create!(...) }.to(have_fired_event('issue_created.plan_my_stuff').with(user: alice))
|
|
718
|
+
#
|
|
719
|
+
RSpec::Matchers.define(:have_fired_event) do |event_name|
|
|
720
|
+
supports_block_expectations
|
|
721
|
+
|
|
722
|
+
chain(:with) { |expected_payload| @expected_payload = expected_payload }
|
|
723
|
+
|
|
724
|
+
match do |block|
|
|
725
|
+
events = PlanMyStuff::TestHelpers::Notifications.capture(&block)
|
|
726
|
+
matching = events.select { |e| e[:name] == event_name }
|
|
727
|
+
@fired_events = events
|
|
728
|
+
next false if matching.empty?
|
|
729
|
+
next true if @expected_payload.nil?
|
|
730
|
+
|
|
731
|
+
matching.any? { |e| @expected_payload.all? { |k, v| e[:payload][k] == v } }
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
failure_message do
|
|
735
|
+
fired_names = @fired_events.pluck(:name)
|
|
736
|
+
base = "expected block to fire #{event_name.inspect}"
|
|
737
|
+
base += " with payload including #{@expected_payload.inspect}" if @expected_payload
|
|
738
|
+
"#{base}, but fired: #{fired_names.empty? ? '(none)' : fired_names.inspect}"
|
|
739
|
+
end
|
|
500
740
|
end
|
|
501
741
|
end
|