appmap 0.102.2 → 0.103.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|