appmap 0.102.2 → 0.103.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/.rubocop.yml +6 -0
- data/.standard.yml +1 -0
- data/.travis.yml +2 -2
- data/CHANGELOG.md +17 -0
- data/appmap.gemspec +35 -36
- data/lib/appmap/agent.rb +28 -24
- data/lib/appmap/class_map.rb +36 -31
- data/lib/appmap/config.rb +132 -110
- data/lib/appmap/cucumber.rb +25 -25
- data/lib/appmap/detect_enabled.rb +30 -28
- data/lib/appmap/gem_hooks/activejob.yml +0 -1
- data/lib/appmap/hook/method/ruby2.rb +19 -8
- data/lib/appmap/hook/method/ruby3.rb +20 -13
- data/lib/appmap/hook/method.rb +27 -27
- data/lib/appmap/hook/record_around.rb +77 -0
- data/lib/appmap/hook.rb +74 -44
- data/lib/appmap/metadata.rb +6 -18
- data/lib/appmap/middleware/remote_recording.rb +26 -27
- data/lib/appmap/minitest.rb +27 -27
- data/lib/appmap/rspec.rb +37 -37
- data/lib/appmap/trace.rb +14 -12
- data/lib/appmap/util.rb +71 -61
- data/lib/appmap/version.rb +1 -1
- metadata +5 -16
data/lib/appmap/config.rb
CHANGED
@@ -1,17 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require_relative
|
3
|
+
require "pathname"
|
4
|
+
require "set"
|
5
|
+
require "yaml"
|
6
|
+
require "appmap/util"
|
7
|
+
require "appmap/handler"
|
8
|
+
require "appmap/service/guesser"
|
9
|
+
require "appmap/swagger/configuration"
|
10
|
+
require "appmap/depends/configuration"
|
11
|
+
require_relative "hook_log"
|
12
|
+
|
13
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
14
|
+
# rubocop:disable Metrics/AbcSize
|
15
|
+
# rubocop:disable Metrics/MethodLength
|
16
|
+
# rubocop:disable Metrics/ClassLength
|
17
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
18
|
+
# rubocop:disable Style/Documentation
|
19
|
+
# rubocop:disable Layout/IndentationWidth
|
12
20
|
|
13
21
|
module AppMap
|
14
22
|
class Config
|
23
|
+
RECORD_AROUND_LABELS = %w[job.perform cli.command message.handle].freeze
|
24
|
+
|
15
25
|
# Specifies a logical code package be mapped.
|
16
26
|
# This can be a project source folder, a Gem, or a builtin.
|
17
27
|
#
|
@@ -32,8 +42,12 @@ module AppMap
|
|
32
42
|
# Specifies the class that will convert code events into event objects.
|
33
43
|
attr_writer :handler_class
|
34
44
|
|
45
|
+
def record_around?
|
46
|
+
RECORD_AROUND_LABELS.find { |label| labels&.member?(label) }
|
47
|
+
end
|
48
|
+
|
35
49
|
def handler_class
|
36
|
-
require
|
50
|
+
require "appmap/handler/function_handler"
|
37
51
|
@handler_class || AppMap::Handler::FunctionHandler
|
38
52
|
end
|
39
53
|
|
@@ -51,7 +65,7 @@ module AppMap
|
|
51
65
|
def subpackage(location, config)
|
52
66
|
return self if gem
|
53
67
|
|
54
|
-
path = location.split(
|
68
|
+
path = location.split("/")[0...-1].join("/")
|
55
69
|
clone.tap do |pkg|
|
56
70
|
pkg.name = path
|
57
71
|
pkg.path = path
|
@@ -106,7 +120,7 @@ module AppMap
|
|
106
120
|
handler_class: handler_class ? handler_class.name : nil,
|
107
121
|
exclude: Util.blank?(exclude) ? nil : exclude,
|
108
122
|
labels: Util.blank?(labels) ? nil : labels,
|
109
|
-
shallow: shallow.nil? ? nil : shallow
|
123
|
+
shallow: shallow.nil? ? nil : shallow
|
110
124
|
}.compact
|
111
125
|
end
|
112
126
|
end
|
@@ -131,7 +145,7 @@ module AppMap
|
|
131
145
|
}
|
132
146
|
end
|
133
147
|
|
134
|
-
|
148
|
+
alias_method :as_json, :to_h
|
135
149
|
end
|
136
150
|
private_constant :TargetMethods
|
137
151
|
|
@@ -158,7 +172,7 @@ module AppMap
|
|
158
172
|
|
159
173
|
MethodHook = Struct.new(:cls, :method_names, :labels) # :nodoc:
|
160
174
|
private_constant :MethodHook
|
161
|
-
|
175
|
+
|
162
176
|
class << self
|
163
177
|
def package_hooks(methods, path: nil, gem: nil, force: false, builtin: false, handler_class: nil, require_name: nil)
|
164
178
|
Array(methods).map do |method|
|
@@ -182,52 +196,52 @@ module AppMap
|
|
182
196
|
|
183
197
|
def declare_hook(hook_decl)
|
184
198
|
hook_decl = YAML.load(hook_decl) if hook_decl.is_a?(String)
|
185
|
-
|
186
|
-
methods_decl = hook_decl[
|
199
|
+
|
200
|
+
methods_decl = hook_decl["methods"] || hook_decl["method"]
|
187
201
|
methods_decl = Array(methods_decl) unless methods_decl.is_a?(Hash)
|
188
|
-
labels_decl = Array(hook_decl[
|
202
|
+
labels_decl = Array(hook_decl["labels"] || hook_decl["label"])
|
189
203
|
|
190
204
|
methods = methods_decl.map do |name|
|
191
|
-
class_name, method_name, static = name.include?(
|
192
|
-
method_hook class_name, [
|
205
|
+
class_name, method_name, static = name.include?(".") ? name.split(".", 2) + [true] : name.split("#", 2) + [false]
|
206
|
+
method_hook class_name, [method_name], labels_decl
|
193
207
|
end
|
194
208
|
|
195
|
-
require_name = hook_decl[
|
196
|
-
gem_name = hook_decl[
|
197
|
-
path = hook_decl[
|
198
|
-
builtin = hook_decl[
|
209
|
+
require_name = hook_decl["require_name"]
|
210
|
+
gem_name = hook_decl["gem"]
|
211
|
+
path = hook_decl["path"]
|
212
|
+
builtin = hook_decl["builtin"]
|
199
213
|
|
200
214
|
options = {
|
201
215
|
builtin: builtin,
|
202
216
|
gem: gem_name,
|
203
217
|
path: path,
|
204
218
|
require_name: require_name || gem_name || path,
|
205
|
-
force: hook_decl[
|
219
|
+
force: hook_decl["force"]
|
206
220
|
}.compact
|
207
221
|
|
208
|
-
handler_class = hook_decl[
|
222
|
+
handler_class = hook_decl["handler_class"]
|
209
223
|
options[:handler_class] = Handler.find(handler_class) if handler_class
|
210
224
|
|
211
225
|
package_hooks(methods, **options)
|
212
226
|
end
|
213
227
|
|
214
228
|
def declare_hook_deprecated(hook_decl)
|
215
|
-
function_name = hook_decl[
|
229
|
+
function_name = hook_decl["name"]
|
216
230
|
package, cls, functions = []
|
217
231
|
if function_name
|
218
232
|
package, cls, _, function = Util.parse_function_name(function_name)
|
219
233
|
functions = Array(function)
|
220
234
|
else
|
221
|
-
package = hook_decl[
|
222
|
-
cls = hook_decl[
|
223
|
-
functions = hook_decl[
|
224
|
-
raise
|
235
|
+
package = hook_decl["package"]
|
236
|
+
cls = hook_decl["class"]
|
237
|
+
functions = hook_decl["function"] || hook_decl["functions"]
|
238
|
+
raise "AppMap config 'function' element should specify 'package', 'class' and 'function' or 'functions'" unless package && cls && functions
|
225
239
|
end
|
226
240
|
|
227
241
|
functions = Array(functions).map(&:to_sym)
|
228
|
-
labels = hook_decl[
|
229
|
-
req = hook_decl[
|
230
|
-
builtin = hook_decl[
|
242
|
+
labels = hook_decl["label"] || hook_decl["labels"]
|
243
|
+
req = hook_decl["require"]
|
244
|
+
builtin = hook_decl["builtin"]
|
231
245
|
|
232
246
|
package_options = {}
|
233
247
|
package_options[:labels] = Array(labels).map(&:to_s) if labels
|
@@ -238,20 +252,20 @@ module AppMap
|
|
238
252
|
end
|
239
253
|
|
240
254
|
def builtin_hooks_path
|
241
|
-
[
|
255
|
+
[[__dir__, "builtin_hooks"].join("/")] + (ENV["APPMAP_BUILTIN_HOOKS_PATH"] || "").split(/[;:]/)
|
242
256
|
end
|
243
257
|
|
244
258
|
def gem_hooks_path
|
245
|
-
[
|
259
|
+
[[__dir__, "gem_hooks"].join("/")] + (ENV["APPMAP_GEM_HOOKS_PATH"] || "").split(/[;:]/)
|
246
260
|
end
|
247
261
|
|
248
262
|
def load_hooks
|
249
263
|
loader = lambda do |dir, &block|
|
250
|
-
basename = dir.split(
|
264
|
+
basename = dir.split("/").compact.join("/")
|
251
265
|
[].tap do |hooks|
|
252
|
-
Dir.glob(Pathname.new(dir).join(
|
266
|
+
Dir.glob(Pathname.new(dir).join("**").join("*.yml").to_s).each do |yaml_file|
|
253
267
|
path = yaml_file[basename.length + 1...-4]
|
254
|
-
YAML.
|
268
|
+
YAML.load_file(yaml_file).map do |config|
|
255
269
|
block.call path, config
|
256
270
|
config
|
257
271
|
end.each do |config|
|
@@ -262,16 +276,16 @@ module AppMap
|
|
262
276
|
end
|
263
277
|
|
264
278
|
builtin_hooks = builtin_hooks_path.map do |path|
|
265
|
-
loader.(path) do |path, config|
|
266
|
-
config[
|
267
|
-
config[
|
279
|
+
loader.call(path) do |path, config|
|
280
|
+
config["path"] = path
|
281
|
+
config["builtin"] = true
|
268
282
|
end
|
269
283
|
end
|
270
284
|
|
271
285
|
gem_hooks = gem_hooks_path.map do |path|
|
272
|
-
loader.(path) do |path, config|
|
273
|
-
config[
|
274
|
-
config[
|
286
|
+
loader.call(path) do |path, config|
|
287
|
+
config["gem"] = path
|
288
|
+
config["builtin"] = false
|
275
289
|
end
|
276
290
|
end
|
277
291
|
|
@@ -296,10 +310,10 @@ module AppMap
|
|
296
310
|
@exclude = exclude
|
297
311
|
@functions = functions
|
298
312
|
|
299
|
-
@builtin_hooks = Hash.new { |h,k| h[k] = [] }
|
300
|
-
@gem_hooks = Hash.new { |h,k| h[k] = [] }
|
301
|
-
|
302
|
-
(functions + self.class.load_hooks).each_with_object(Hash.new { |h,k| h[k] = [] }) do |cls_target_methods, gem_hooks|
|
313
|
+
@builtin_hooks = Hash.new { |h, k| h[k] = [] }
|
314
|
+
@gem_hooks = Hash.new { |h, k| h[k] = [] }
|
315
|
+
|
316
|
+
(functions + self.class.load_hooks).each_with_object(Hash.new { |h, k| h[k] = [] }) do |cls_target_methods, gem_hooks|
|
303
317
|
hooks = if cls_target_methods.target_methods.package.builtin
|
304
318
|
@builtin_hooks
|
305
319
|
else
|
@@ -318,11 +332,11 @@ module AppMap
|
|
318
332
|
def load_from_file(config_file_name)
|
319
333
|
logo = lambda do
|
320
334
|
Util.color(<<~LOGO, :magenta)
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
335
|
+
___ __ ___
|
336
|
+
/ _ | ___ ___ / |/ /__ ____
|
337
|
+
/ __ |/ _ \\/ _ \\/ /|_/ / _ `/ _ \\
|
338
|
+
/_/ |_/ .__/ .__/_/ /_/\\_,_/ .__/
|
339
|
+
/_/ /_/ /_/
|
326
340
|
LOGO
|
327
341
|
end
|
328
342
|
|
@@ -331,43 +345,43 @@ module AppMap
|
|
331
345
|
config_data = if config_present
|
332
346
|
YAML.safe_load(::File.read(config_file_name))
|
333
347
|
else
|
334
|
-
warn logo.
|
335
|
-
warn
|
336
|
-
warn Util.color(%
|
337
|
-
warn
|
348
|
+
warn logo.call
|
349
|
+
warn ""
|
350
|
+
warn Util.color(%(NOTICE: The AppMap config file #{config_file_name} was not found!), :magenta, bold: true)
|
351
|
+
warn ""
|
338
352
|
warn Util.color(<<~MISSING_FILE_MSG, :magenta)
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
353
|
+
AppMap uses this file to customize its behavior. For example, you can use
|
354
|
+
the 'packages' setting to indicate which local file paths and dependency
|
355
|
+
gems you want to include in the AppMap. Since you haven't provided specific
|
356
|
+
settings, the appmap gem will use these default options:
|
343
357
|
MISSING_FILE_MSG
|
344
358
|
{}
|
345
359
|
end
|
346
360
|
|
347
361
|
load(config_data).tap do |config|
|
348
362
|
{
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
{
|
363
|
+
"name" => config.name,
|
364
|
+
"language" => "ruby",
|
365
|
+
"appmap_dir" => AppMap::DEFAULT_APPMAP_DIR,
|
366
|
+
"packages" => config.packages.select { |p| p.path }.map do |pkg|
|
367
|
+
{"path" => pkg.path}
|
354
368
|
end,
|
355
|
-
|
369
|
+
"exclude" => []
|
356
370
|
}.compact.tap do |config_yaml|
|
357
371
|
unless config_present
|
358
372
|
warn Util.color(YAML.dump(config_yaml), :magenta)
|
359
373
|
dirname = Pathname.new(config_file_name).dirname.expand_path
|
360
374
|
if Dir.exist?(dirname) && File.writable?(dirname)
|
361
375
|
warn Util.color(<<~CONFIG_FILE_MSG, :magenta)
|
362
|
-
|
363
|
-
|
376
|
+
This file will be saved to #{Pathname.new(config_file_name).expand_path},
|
377
|
+
where you can customize it.
|
364
378
|
CONFIG_FILE_MSG
|
365
379
|
File.write(config_file_name, YAML.dump(config_yaml))
|
366
380
|
end
|
367
381
|
warn Util.color(<<~CONFIG_FILE_MSG, :magenta)
|
368
|
-
|
382
|
+
For more information, see https://appmap.io/docs/reference/appmap-ruby.html#configuration
|
369
383
|
CONFIG_FILE_MSG
|
370
|
-
warn logo.
|
384
|
+
warn logo.call
|
371
385
|
end
|
372
386
|
end
|
373
387
|
end
|
@@ -375,14 +389,14 @@ module AppMap
|
|
375
389
|
|
376
390
|
# Loads configuration from a Hash.
|
377
391
|
def load(config_data)
|
378
|
-
name = config_data[
|
392
|
+
name = config_data["name"] || Service::Guesser.guess_name
|
379
393
|
config_params = {
|
380
|
-
exclude: config_data[
|
394
|
+
exclude: config_data["exclude"]
|
381
395
|
}.compact
|
382
396
|
|
383
|
-
if config_data[
|
384
|
-
config_params[:functions] = config_data[
|
385
|
-
if hook_decl[
|
397
|
+
if config_data["functions"]
|
398
|
+
config_params[:functions] = config_data["functions"].map do |hook_decl|
|
399
|
+
if hook_decl["name"] || hook_decl["package"]
|
386
400
|
declare_hook_deprecated(hook_decl)
|
387
401
|
else
|
388
402
|
# Support the same syntax within the 'functions' that's used for externalized
|
@@ -393,24 +407,24 @@ module AppMap
|
|
393
407
|
end
|
394
408
|
|
395
409
|
config_params[:packages] = \
|
396
|
-
if config_data[
|
397
|
-
config_data[
|
398
|
-
gem = package[
|
399
|
-
path = package[
|
400
|
-
raise
|
401
|
-
raise
|
410
|
+
if config_data["packages"]
|
411
|
+
config_data["packages"].map do |package|
|
412
|
+
gem = package["gem"]
|
413
|
+
path = package["path"]
|
414
|
+
raise "AppMap config 'package' element should specify 'gem' or 'path', not both" if gem && path
|
415
|
+
raise "AppMap config 'package' element should specify 'gem' or 'path'" unless gem || path
|
402
416
|
|
403
417
|
if gem
|
404
|
-
shallow = package[
|
418
|
+
shallow = package["shallow"]
|
405
419
|
# shallow is true by default for gems
|
406
420
|
shallow = true if shallow.nil?
|
407
421
|
|
408
422
|
require_name = \
|
409
|
-
package[
|
410
|
-
package[
|
411
|
-
Package.build_from_gem(gem, require_name: require_name, exclude: package[
|
423
|
+
package["package"] || # deprecated
|
424
|
+
package["require_name"]
|
425
|
+
Package.build_from_gem(gem, require_name: require_name, exclude: package["exclude"] || [], shallow: shallow)
|
412
426
|
else
|
413
|
-
Package.build_from_path(path, exclude: package[
|
427
|
+
Package.build_from_path(path, exclude: package["exclude"] || [], shallow: package["shallow"])
|
414
428
|
end
|
415
429
|
end.compact
|
416
430
|
else
|
@@ -419,12 +433,12 @@ module AppMap
|
|
419
433
|
end
|
420
434
|
end
|
421
435
|
|
422
|
-
if config_data[
|
423
|
-
swagger_config = Swagger::Configuration.load(config_data[
|
436
|
+
if config_data["swagger"]
|
437
|
+
swagger_config = Swagger::Configuration.load(config_data["swagger"])
|
424
438
|
config_params[:swagger_config] = swagger_config
|
425
439
|
end
|
426
|
-
if config_data[
|
427
|
-
depends_config = Depends::Configuration.load(config_data[
|
440
|
+
if config_data["depends"]
|
441
|
+
depends_config = Depends::Configuration.load(config_data["depends"])
|
428
442
|
config_params[:depends_config] = depends_config
|
429
443
|
end
|
430
444
|
|
@@ -447,27 +461,35 @@ module AppMap
|
|
447
461
|
@hook_paths.find { |hook_path| path.index(hook_path) == 0 }
|
448
462
|
end
|
449
463
|
|
464
|
+
HookConfig = Struct.new(:package, :labels) do
|
465
|
+
end
|
466
|
+
|
450
467
|
# Looks up a class and method in the config, to find the matching Package configuration.
|
451
|
-
# This class is only used after +path_enabled?+ has returned `true`.
|
452
|
-
|
453
|
-
def
|
468
|
+
# This class is only used after +path_enabled?+ has returned `true`.
|
469
|
+
LookupHookConfig = Struct.new(:config, :cls, :method) do
|
470
|
+
def hook_config
|
454
471
|
# Global "excludes" configuration can be used to ignore any class/method.
|
455
472
|
return if config.never_hook?(cls, method)
|
456
473
|
|
457
|
-
package_for_code_object || package_for_location
|
474
|
+
pkg = package_for_code_object || package_for_location
|
475
|
+
return unless pkg
|
476
|
+
|
477
|
+
comment = method.comment
|
478
|
+
labels = (pkg.labels || []) + ClassMap.parse_labels(comment)
|
479
|
+
HookConfig.new(pkg, labels)
|
458
480
|
end
|
459
481
|
|
460
482
|
# Hook a method which is specified by class and method name.
|
461
483
|
def package_for_code_object
|
462
484
|
class_name = begin
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
485
|
+
(cls.to_s.index("#<Class:") == 0) ? cls.to_s["#<Class:".length...-1] : cls.name
|
486
|
+
rescue
|
487
|
+
# Calling #to_s on some Rails classes
|
488
|
+
# (e.g. those generated to represent
|
489
|
+
# associations) will raise an exception. Fall
|
490
|
+
# back to using the class name.
|
491
|
+
cls.name
|
492
|
+
end
|
471
493
|
Array(config.gem_hooks[class_name])
|
472
494
|
.find { |hook| hook.include_method?(method.name) }
|
473
495
|
&.package
|
@@ -482,20 +504,20 @@ module AppMap
|
|
482
504
|
location_file = AppMap::Util.normalize_path(location_file)
|
483
505
|
|
484
506
|
pkg = config
|
485
|
-
|
486
|
-
|
487
|
-
|
507
|
+
.packages
|
508
|
+
.select { |pkg| pkg.path }
|
509
|
+
.select do |pkg|
|
488
510
|
(location_file.index(pkg.path) == 0) &&
|
489
511
|
!pkg.exclude.find { |p| location_file.index(p) }
|
490
512
|
end
|
491
|
-
|
513
|
+
.min { |a, b| b.path <=> a.path } # Longest matching package first
|
492
514
|
|
493
515
|
pkg.subpackage(location_file, config) if pkg
|
494
516
|
end
|
495
517
|
end
|
496
518
|
|
497
|
-
def
|
498
|
-
|
519
|
+
def lookup_hook_config(cls, method)
|
520
|
+
LookupHookConfig.new(self, cls, method).hook_config
|
499
521
|
end
|
500
522
|
|
501
523
|
def never_hook?(cls, method)
|
@@ -505,7 +527,7 @@ module AppMap
|
|
505
527
|
end
|
506
528
|
|
507
529
|
_, separator, = ::AppMap::Hook.qualify_method_name(method)
|
508
|
-
if exclude.member?(cls.name) || exclude.member?([
|
530
|
+
if exclude.member?(cls.name) || exclude.member?([cls.name, separator, method.name].join)
|
509
531
|
HookLog.log "Hooking of #{method} disabled by configuration" if HookLog.enabled?
|
510
532
|
return true
|
511
533
|
end
|
data/lib/appmap/cucumber.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require
|
3
|
+
require_relative "../appmap"
|
4
|
+
require_relative "util"
|
5
|
+
require_relative "detect_enabled"
|
6
|
+
require "fileutils"
|
7
7
|
|
8
8
|
module AppMap
|
9
9
|
module Cucumber
|
10
|
-
APPMAP_OUTPUT_DIR =
|
10
|
+
APPMAP_OUTPUT_DIR = File.join(AppMap.output_dir, "cucumber")
|
11
11
|
|
12
12
|
ScenarioAttributes = Struct.new(:name, :feature, :feature_group)
|
13
13
|
|
14
14
|
ProviderStruct = Struct.new(:scenario) do
|
15
15
|
def feature_group
|
16
16
|
# e.g. <Cucumber::Core::Ast::Location::Precise: cucumber/api/features/authenticate.feature:1>
|
17
|
-
feature_path.split(
|
17
|
+
feature_path.split("/").last.split(".")[0]
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -34,7 +34,7 @@ module AppMap
|
|
34
34
|
# versions 4.0 and later.
|
35
35
|
class Provider4 < ProviderStruct
|
36
36
|
def attributes
|
37
|
-
ScenarioAttributes.new(scenario.name, scenario.name.split(
|
37
|
+
ScenarioAttributes.new(scenario.name, scenario.name.split(" ")[0..1].join(" "), feature_group)
|
38
38
|
end
|
39
39
|
|
40
40
|
def feature_path
|
@@ -46,14 +46,14 @@ module AppMap
|
|
46
46
|
def init
|
47
47
|
AppMap::DetectEnabled.discourage_conflicting_recording_methods :cucumber
|
48
48
|
|
49
|
-
warn
|
49
|
+
warn "Configuring AppMap recorder for Cucumber"
|
50
50
|
|
51
51
|
FileUtils.mkdir_p APPMAP_OUTPUT_DIR
|
52
52
|
end
|
53
53
|
|
54
54
|
def write_scenario(scenario, appmap)
|
55
|
-
appmap[
|
56
|
-
scenario_filename = AppMap::Util.scenario_filename(appmap[
|
55
|
+
appmap["metadata"] = update_metadata(scenario, appmap["metadata"])
|
56
|
+
scenario_filename = AppMap::Util.scenario_filename(appmap["metadata"]["name"])
|
57
57
|
|
58
58
|
AppMap::Util.write_appmap(File.join(APPMAP_OUTPUT_DIR, scenario_filename), appmap)
|
59
59
|
end
|
@@ -69,11 +69,11 @@ module AppMap
|
|
69
69
|
protected
|
70
70
|
|
71
71
|
def cucumber_version
|
72
|
-
Gem.loaded_specs[
|
72
|
+
Gem.loaded_specs["cucumber"]&.version&.to_s
|
73
73
|
end
|
74
74
|
|
75
75
|
def provider(scenario)
|
76
|
-
major, = cucumber_version.split(
|
76
|
+
major, = cucumber_version.split(".").map(&:to_i)
|
77
77
|
if major < 4
|
78
78
|
ProviderBefore4
|
79
79
|
else
|
@@ -85,19 +85,19 @@ module AppMap
|
|
85
85
|
attributes = provider(scenario).attributes
|
86
86
|
|
87
87
|
base_metadata.tap do |m|
|
88
|
-
m[
|
89
|
-
m[
|
90
|
-
m[
|
91
|
-
m[
|
92
|
-
m[
|
93
|
-
m[
|
94
|
-
m[
|
95
|
-
|
96
|
-
|
88
|
+
m["name"] = attributes.name
|
89
|
+
m["feature"] = attributes.feature
|
90
|
+
m["feature_group"] = attributes.feature_group
|
91
|
+
m["labels"] ||= []
|
92
|
+
m["labels"] += (scenario.tags&.map(&:name) || [])
|
93
|
+
m["frameworks"] ||= []
|
94
|
+
m["frameworks"] << {
|
95
|
+
"name" => "cucumber",
|
96
|
+
"version" => Gem.loaded_specs["cucumber"]&.version&.to_s
|
97
97
|
}
|
98
|
-
m[
|
99
|
-
|
100
|
-
|
98
|
+
m["recorder"] = {
|
99
|
+
"name" => "cucumber",
|
100
|
+
"type" => "tests"
|
101
101
|
}
|
102
102
|
end
|
103
103
|
end
|
@@ -106,7 +106,7 @@ module AppMap
|
|
106
106
|
end
|
107
107
|
|
108
108
|
if AppMap::Cucumber.enabled?
|
109
|
-
require
|
109
|
+
require "appmap"
|
110
110
|
|
111
111
|
AppMap::Cucumber.run
|
112
112
|
end
|