cuke-steps 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/confluence_step_outputter.rb +64 -0
- data/lib/cuke-steps.rb +96 -0
- data/lib/cuke-tags.rb +49 -0
- data/lib/html_step_outputter.rb +119 -0
- data/lib/step_parser.rb +75 -0
- data/lib/tag_parser.rb +80 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: daf09884012622405d6940dc98da89ee1de820aa
|
4
|
+
data.tar.gz: 2bbbf2c368ee08721e5ade74bfccb53851f60b9f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 398a85ade3e4d2cc46a5253fbd41af919987b4dd34500c9055ca7a7e2839f9cf5af4556ea0578f3664379e8581e6172b0ca8121745a005d356eea54bf620a021
|
7
|
+
data.tar.gz: 1be067cd1c81a005dff578017b7f9728af8f1c0b30ec5672d70424a0926c6153955e12ae389382c353f05f0207205a0a5a1000072e4d205c6fa1d1fb3881d60c
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# Outputter that generates Confluence markup
|
2
|
+
|
3
|
+
require 'cgi'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
class ConfluenceStepOutputter
|
7
|
+
def initialize(file)
|
8
|
+
@file = File.open(file, 'w')
|
9
|
+
@previous_type = ""
|
10
|
+
end
|
11
|
+
|
12
|
+
def header
|
13
|
+
# No-op
|
14
|
+
end
|
15
|
+
def footer
|
16
|
+
@file.puts %(<p> </p>)
|
17
|
+
@file.puts %(<p><em>Documentation generated #{Time.now}</em></p>)
|
18
|
+
end
|
19
|
+
|
20
|
+
def close
|
21
|
+
@file.close
|
22
|
+
end
|
23
|
+
|
24
|
+
def start_directory(dir)
|
25
|
+
@file.puts %(<p> </p>)
|
26
|
+
@file.puts "<h2>#{dir}</h2>"
|
27
|
+
@previous_type = ""
|
28
|
+
end
|
29
|
+
|
30
|
+
def end_directory
|
31
|
+
# No-op
|
32
|
+
end
|
33
|
+
|
34
|
+
def start_all
|
35
|
+
@file.puts %(<p> </p>)
|
36
|
+
@file.puts "<h2>All definitions alphabetically</h2>"
|
37
|
+
@previous_type = ""
|
38
|
+
end
|
39
|
+
|
40
|
+
def end_all
|
41
|
+
# No-op
|
42
|
+
end
|
43
|
+
|
44
|
+
def step(step)
|
45
|
+
if @previous_type != step[:type]
|
46
|
+
@file.puts %(<h3>#{step[:type]} definitions</h3>)
|
47
|
+
@previous_type = step[:type]
|
48
|
+
end
|
49
|
+
@file.puts %(<ac:macro ac:name="expand">)
|
50
|
+
@file.puts %( <ac:parameter ac:name="title">#{CGI.escapeHTML(step[:name])}</ac:parameter>)
|
51
|
+
@file.puts %( <ac:rich-text-body>)
|
52
|
+
# TODO: Add link to source repo or Jenkins workspace
|
53
|
+
# <p><a href=".../#{CGI.escapeHTML(step[:filename])}" style="color: #888;">#{CGI.escapeHTML(step[:filename])}:#{step[:line_number]}</a></p>
|
54
|
+
@file.puts %( <p style="color: #888;">#{CGI.escapeHTML(step[:filename])}:#{step[:line_number]}</p>)
|
55
|
+
@file.puts %( <pre style="background-color: #ddd; padding-top: 1.2em;">)
|
56
|
+
step[:code].each do |line|
|
57
|
+
@file.puts %( #{CGI.escapeHTML(line)})
|
58
|
+
end
|
59
|
+
@file.puts %( </pre>)
|
60
|
+
@file.puts %( </ac:rich-text-body>)
|
61
|
+
@file.puts %(</ac:macro>)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/lib/cuke-steps.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#-*- encoding: utf-8 -*-
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
require_relative 'step_parser'
|
7
|
+
require_relative 'confluence_step_outputter'
|
8
|
+
require_relative 'html_step_outputter'
|
9
|
+
|
10
|
+
|
11
|
+
# Parse command line
|
12
|
+
options = {}
|
13
|
+
opts = OptionParser.new do |opts|
|
14
|
+
opts.banner = "Usage: cuke-steps.rb [options] <directories...>"
|
15
|
+
|
16
|
+
opts.on("-o", "--output FILE", "Output to FILE") do |file|
|
17
|
+
options[:file] = file
|
18
|
+
end
|
19
|
+
opts.on("-f", "--format FMT", "Select output format: cf, html") do |format|
|
20
|
+
options[:format] = format
|
21
|
+
end
|
22
|
+
end
|
23
|
+
opts.parse!(ARGV)
|
24
|
+
|
25
|
+
# Default output options
|
26
|
+
if options[:file] && !options[:format]
|
27
|
+
options[:format] = options[:file].sub(/^.*\./, "")
|
28
|
+
end
|
29
|
+
if !options[:format]
|
30
|
+
options[:format] = "html"
|
31
|
+
end
|
32
|
+
if options[:format] && !options[:file]
|
33
|
+
options[:file] = "steps.#{options[:format]}"
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# All other arguments are treated as input directories
|
38
|
+
dirs = ARGV
|
39
|
+
if dirs.size == 0
|
40
|
+
puts "No source directories provided, use -h for help"
|
41
|
+
exit 1
|
42
|
+
end
|
43
|
+
|
44
|
+
# Setup output
|
45
|
+
case options[:format]
|
46
|
+
when 'cf'
|
47
|
+
output = ConfluenceStepOutputter.new(options[:file])
|
48
|
+
when 'html'
|
49
|
+
output = HtmlStepOutputter.new(options[:file])
|
50
|
+
else
|
51
|
+
puts "Unknown output format: #{options[:format]}"
|
52
|
+
exit 1
|
53
|
+
end
|
54
|
+
puts "Writing output to file '#{options[:file]}'"
|
55
|
+
|
56
|
+
|
57
|
+
# Sort primarily by step type, secondarily by step definition
|
58
|
+
sorter = lambda do |a,b|
|
59
|
+
if a[:type] == b[:type]
|
60
|
+
a[:name].downcase <=> b[:name].downcase
|
61
|
+
else
|
62
|
+
weight = { "Given" => 1, "When" => 2, "Then" => 3, "Transform" => 4, "Before" => 5, "After" => 6, "AfterStep" => 7 }
|
63
|
+
wa = weight[a[:type]]
|
64
|
+
wb = weight[b[:type]]
|
65
|
+
wa <=> wb
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# Read files and output
|
71
|
+
all_steps = []
|
72
|
+
output.header
|
73
|
+
dirs.each do |dir|
|
74
|
+
dir = dir.sub(/\/+$/, "")
|
75
|
+
s = StepParser.new
|
76
|
+
Dir.glob("#{dir}/**/*.rb") do |file|
|
77
|
+
s.read(file)
|
78
|
+
end
|
79
|
+
steps = s.steps
|
80
|
+
all_steps += steps
|
81
|
+
|
82
|
+
output.start_directory(dir)
|
83
|
+
steps.sort!(&sorter)
|
84
|
+
steps.each { |s| output.step(s) }
|
85
|
+
output.end_directory
|
86
|
+
end
|
87
|
+
|
88
|
+
if dirs.size > 1
|
89
|
+
output.start_all
|
90
|
+
all_steps.sort!(&sorter)
|
91
|
+
all_steps.each { |s| output.step(s) }
|
92
|
+
output.end_all
|
93
|
+
end
|
94
|
+
|
95
|
+
output.footer
|
96
|
+
output.close
|
data/lib/cuke-tags.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#-*- encoding: utf-8 -*-
|
3
|
+
require 'json'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
require_relative 'step_parser'
|
7
|
+
require_relative 'tag_parser'
|
8
|
+
require_relative 'confluence_step_outputter'
|
9
|
+
require_relative 'html_step_outputter'
|
10
|
+
|
11
|
+
# Parse command line
|
12
|
+
options = {}
|
13
|
+
opts = OptionParser.new do |opts|
|
14
|
+
opts.banner = "Usage: cuke-tags.rb [options] <directories...>"
|
15
|
+
|
16
|
+
opts.on("-o", "--output FILE", "Output to FILE") do |file|
|
17
|
+
options[:file] = file
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
opts.parse!(ARGV)
|
22
|
+
|
23
|
+
# Default output options
|
24
|
+
if !options[:file]
|
25
|
+
options[:file] = "tags.json"
|
26
|
+
end
|
27
|
+
|
28
|
+
# All other arguments are treated as input directories
|
29
|
+
dirs = ARGV
|
30
|
+
if dirs.size == 0
|
31
|
+
puts "No source directory provided, use -h for help"
|
32
|
+
exit 1
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
puts "Writing output to file '#{options[:file]}'"
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
# Read files and output
|
41
|
+
all_tags = []
|
42
|
+
dir = Dir.new(dirs[0]).to_path
|
43
|
+
t = TagParser.new
|
44
|
+
Dir.glob("#{dir}/*.feature") do |file|
|
45
|
+
t.read(file)
|
46
|
+
tags = t.tags
|
47
|
+
all_tags += tags
|
48
|
+
end
|
49
|
+
File.open(options[:file], 'w') {|f| f.write(all_tags.to_json) }
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# Outputter that generates HTML markup
|
2
|
+
# Feel free to customize the below code for your needs
|
3
|
+
|
4
|
+
require 'cgi'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
class HtmlStepOutputter
|
8
|
+
|
9
|
+
def initialize(file)
|
10
|
+
@file = File.open(file, 'w')
|
11
|
+
@previous_type = ""
|
12
|
+
@id_number = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
# HTML file header - customize as needed
|
16
|
+
def header
|
17
|
+
@file.puts <<-eos
|
18
|
+
<!DOCTYPE html>
|
19
|
+
<html>
|
20
|
+
<head>
|
21
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
22
|
+
<title>Cucumber step documentation</title>
|
23
|
+
<style>
|
24
|
+
.stepdefs {
|
25
|
+
font-size: smaller;
|
26
|
+
}
|
27
|
+
.stepdefs li {
|
28
|
+
margin-bottom: 0.25em;
|
29
|
+
list-style-type: none;
|
30
|
+
}
|
31
|
+
.stepdefs li:before {
|
32
|
+
content: "\u00BB ";
|
33
|
+
font-size: larger;
|
34
|
+
padding-right: 0.3em;
|
35
|
+
}
|
36
|
+
.stepdef {
|
37
|
+
color: #111;
|
38
|
+
text-decoration: none;
|
39
|
+
}
|
40
|
+
.extrainfo {
|
41
|
+
display: none;
|
42
|
+
overflow: hidden; /* Fixes jumping issue in jQuery slideToggle() */
|
43
|
+
}
|
44
|
+
</style>
|
45
|
+
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
|
46
|
+
</head>
|
47
|
+
<body>
|
48
|
+
eos
|
49
|
+
end
|
50
|
+
|
51
|
+
def footer
|
52
|
+
@file.puts <<-eos
|
53
|
+
</ul>
|
54
|
+
<p> </p>
|
55
|
+
<p><em>Documentation generated #{Time.now}</em></p>
|
56
|
+
</body>
|
57
|
+
</html>
|
58
|
+
eos
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def close
|
63
|
+
@file.close
|
64
|
+
end
|
65
|
+
|
66
|
+
def start_directory(dir)
|
67
|
+
@file.puts %(</ul>) if @previous_type != ""
|
68
|
+
@file.puts %(<p> </p>)
|
69
|
+
@file.puts %(<h2>Step definitions in #{dir}/</h2>)
|
70
|
+
@previous_type = ""
|
71
|
+
end
|
72
|
+
|
73
|
+
def end_directory
|
74
|
+
# No-op
|
75
|
+
end
|
76
|
+
|
77
|
+
def start_all
|
78
|
+
@file.puts %(</ul>)
|
79
|
+
@file.puts %(<p> </p>)
|
80
|
+
@file.puts %(<h2>All definitions alphabetically</h2>)
|
81
|
+
@previous_type = ""
|
82
|
+
end
|
83
|
+
|
84
|
+
def end_all
|
85
|
+
# No-op
|
86
|
+
end
|
87
|
+
|
88
|
+
def step(step)
|
89
|
+
if @previous_type != step[:type]
|
90
|
+
@file.puts %(</ul>) if @previous_type != ""
|
91
|
+
@file.puts %(<h3>#{step[:type]} definitions</h3>)
|
92
|
+
@file.puts %(<ul class="stepdefs">)
|
93
|
+
@previous_type = step[:type]
|
94
|
+
end
|
95
|
+
|
96
|
+
id = new_id
|
97
|
+
@file.puts %(<li>)
|
98
|
+
@file.puts %( <a href="#" onclick="$('##{id}').slideToggle(); return false;" class="stepdef">#{CGI.escapeHTML(step[:name])}</a>)
|
99
|
+
@file.puts %( <div id="#{id}" class="extrainfo">)
|
100
|
+
# TODO: Add link to source repo or Jenkins workspace
|
101
|
+
# <p><a href=".../#{CGI.escapeHTML(step[:filename])}" style="color: #888;">#{CGI.escapeHTML(step[:filename])}:#{step[:line_number]}</a></p>
|
102
|
+
@file.puts %( <p style="color: #888;">#{CGI.escapeHTML(step[:filename])}:#{step[:line_number]}</p>)
|
103
|
+
@file.puts %( <pre style="background-color: #ddd; padding-top: 1.2em;">)
|
104
|
+
step[:code].each do |line|
|
105
|
+
@file.puts %( #{CGI.escapeHTML(line)})
|
106
|
+
end
|
107
|
+
@file.puts %( </pre>)
|
108
|
+
@file.puts %( </div>)
|
109
|
+
@file.puts %(</li>)
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def new_id
|
115
|
+
@id_number += 1
|
116
|
+
"id#{@id_number}"
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
data/lib/step_parser.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# Class that parses step definitions from Ruby files
|
2
|
+
|
3
|
+
class StepParser
|
4
|
+
|
5
|
+
attr_reader :steps
|
6
|
+
def initialize
|
7
|
+
@steps = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def read(file)
|
11
|
+
@current_file = file
|
12
|
+
@line_number = 0
|
13
|
+
@lines = IO.read(file).split(/\r?\n/)
|
14
|
+
parse_lines
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def next_line
|
21
|
+
@line_number += 1
|
22
|
+
@lines.shift
|
23
|
+
end
|
24
|
+
|
25
|
+
def unread(line)
|
26
|
+
@line_number -= 1
|
27
|
+
@lines.unshift(line)
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_lines
|
31
|
+
@comments = []
|
32
|
+
while not @lines.empty?
|
33
|
+
|
34
|
+
line = next_line
|
35
|
+
case line
|
36
|
+
when /^ *#/
|
37
|
+
@comments << line
|
38
|
+
when /^(Given|When|Then|Before|After|AfterStep|Transform) /
|
39
|
+
unread(line)
|
40
|
+
parse_step
|
41
|
+
@comments = []
|
42
|
+
when /^\s+(Given|When|Then|Before|After|AfterStep|Transform) /
|
43
|
+
puts "WARNING: Indented step definition in file #{@current_file}: #{line}"
|
44
|
+
@comments = []
|
45
|
+
else
|
46
|
+
@comments = []
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_step
|
53
|
+
type = parse_step_type(@lines.first)
|
54
|
+
name = parse_step_name(@lines.first)
|
55
|
+
line_number = @line_number + 1
|
56
|
+
code = @comments
|
57
|
+
line = ""
|
58
|
+
while !@lines.empty? && !(line =~ /^end\s*$/)
|
59
|
+
line = next_line
|
60
|
+
code << line
|
61
|
+
end
|
62
|
+
@steps << { :type => type, :name => name, :filename => @current_file, :code => code, :line_number => line_number }
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_step_type(line)
|
66
|
+
line.sub(/^([A-Za-z]+).*/, '\1')
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_step_name(line)
|
70
|
+
line = line.sub(/^(Given|When|Then|Transform) +\/\^?(.*?)\$?\/.*/, '\1 \2')
|
71
|
+
line = line.gsub('\ ', ' ')
|
72
|
+
line
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
data/lib/tag_parser.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# Class that parses step definitions from Ruby files
|
2
|
+
|
3
|
+
class TagParser
|
4
|
+
|
5
|
+
attr_reader :tags
|
6
|
+
def initialize
|
7
|
+
@tags = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def read(file)
|
11
|
+
@current_file = file
|
12
|
+
@line_number = 0
|
13
|
+
@lines = IO.read(file).split(/\r?\n/)
|
14
|
+
parse_lines
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def next_line
|
20
|
+
@line_number += 1
|
21
|
+
@lines.shift
|
22
|
+
end
|
23
|
+
|
24
|
+
def unread(line)
|
25
|
+
@line_number -= 1
|
26
|
+
@lines.unshift(line)
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_lines
|
30
|
+
@comments = []
|
31
|
+
while not @lines.empty?
|
32
|
+
line = next_line
|
33
|
+
case line
|
34
|
+
when /^ *#/
|
35
|
+
@comments << line
|
36
|
+
when /^\s+@.*/
|
37
|
+
unread(line)
|
38
|
+
parse_tag
|
39
|
+
@comments = []
|
40
|
+
when /^@.*/
|
41
|
+
unread(line)
|
42
|
+
parse_tag
|
43
|
+
else
|
44
|
+
@comments = []
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_tag
|
51
|
+
line = next_line
|
52
|
+
tag_names = parse_tag_names(line)
|
53
|
+
line_number =@line_number
|
54
|
+
code = ""
|
55
|
+
line = ""
|
56
|
+
scenario = ""
|
57
|
+
while !@lines.empty? && !(line =~ /^\s+@.*/)
|
58
|
+
line = next_line
|
59
|
+
case line
|
60
|
+
|
61
|
+
when /^\s+Scenario:.*/
|
62
|
+
scenario = line
|
63
|
+
else
|
64
|
+
code << line
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
@tags << { :names => tag_names, :filename => @current_file, :comments => @comments,:code => code, :scenario=>scenario, :line_number => line_number }
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_step_type(line)
|
72
|
+
line.sub(/^([A-Za-z]+).*/, '\1')
|
73
|
+
end
|
74
|
+
|
75
|
+
def parse_tag_names(line)
|
76
|
+
tags = line.split(' ')
|
77
|
+
tags
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cuke-steps
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ali Khan
|
8
|
+
- plaa
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-05-02 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Cucumber Steps and Tags documentation generator.
|
15
|
+
email: alikhan@himindz.ie
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/confluence_step_outputter.rb
|
21
|
+
- lib/cuke-steps.rb
|
22
|
+
- lib/cuke-tags.rb
|
23
|
+
- lib/html_step_outputter.rb
|
24
|
+
- lib/step_parser.rb
|
25
|
+
- lib/tag_parser.rb
|
26
|
+
homepage: https://github.com/himindz/cuke-steps
|
27
|
+
licenses:
|
28
|
+
- BSD
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 2.2.1
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: Cucumber Steps and Tags documentation generator
|
50
|
+
test_files: []
|