cuki 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +5 -4
- data/VERSION +1 -1
- data/cuki.gemspec +2 -3
- data/cuki.yaml.sample +3 -3
- data/features/pull/error_handling.feature +23 -1
- data/features/pull/pull.feature +56 -55
- data/features/pull/pull_single.feature +14 -9
- data/features/pull/splitting.feature +7 -6
- data/features/pull/tables.feature +9 -6
- data/features/pull/tags.feature +12 -7
- data/features/pull/textile.feature +9 -6
- data/lib/confluence_page.rb +25 -1
- data/lib/cuki.rb +76 -165
- data/lib/link_builder.rb +4 -2
- metadata +4 -5
- data/lib/cleaner.rb +0 -32
data/README.rdoc
CHANGED
@@ -2,13 +2,11 @@
|
|
2
2
|
|
3
3
|
Cuki provides an easy way to import acceptance criteria from a Confluence wiki into Cucumber feature files
|
4
4
|
|
5
|
-
- Supports a mapping between Confluence pages and feature files
|
6
5
|
- Converts Confluence tables to Cucumber tables
|
7
6
|
- Strips out unnecessary Confluence formatting (headers, etc.)
|
8
7
|
- Includes a link back to the original Confluence page
|
9
8
|
- Formats the feature using Cucumber's auto-formatter (optional)
|
10
9
|
- Support client SSL certificates for use within an organisation's secure intranet
|
11
|
-
- Assign tags to a feature based on the wiki page content
|
12
10
|
|
13
11
|
It can be used as part of a CI process or just for ad-hoc imports.
|
14
12
|
|
@@ -45,6 +43,11 @@ You can also pull a single feature:
|
|
45
43
|
|
46
44
|
cuki pull features/add_product.feature
|
47
45
|
|
46
|
+
== Tags
|
47
|
+
|
48
|
+
You can add tags to a feature based on the wiki page content. See the sample configuration file.
|
49
|
+
|
50
|
+
|
48
51
|
== Options
|
49
52
|
|
50
53
|
- --skip--autoformat to avoid reformatting features (runs over the whole features directory)
|
@@ -60,8 +63,6 @@ If your Confluence installation requires a client certificate, you can supply th
|
|
60
63
|
|
61
64
|
- Will only work with Confluence setups which have no password, or use client certificates for authentication
|
62
65
|
- Only provides one-way sync, i.e. you can't edit a file locally and push it to Confluence
|
63
|
-
- Fails if the AC block is the last h1. section of the page
|
64
|
-
- Should fail if no features found in container block
|
65
66
|
|
66
67
|
== TODO
|
67
68
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.11
|
data/cuki.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "cuki"
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.11"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Andy Waite"]
|
12
|
-
s.date = "2011-10-
|
12
|
+
s.date = "2011-10-23"
|
13
13
|
s.description = ""
|
14
14
|
s.email = "andy@andywaite.com"
|
15
15
|
s.executables = ["cuki"]
|
@@ -44,7 +44,6 @@ Gem::Specification.new do |s|
|
|
44
44
|
"features/step_defs/pull_steps.rb",
|
45
45
|
"features/step_defs/push_steps.rb",
|
46
46
|
"features/support/env.rb",
|
47
|
-
"lib/cleaner.rb",
|
48
47
|
"lib/confluence_page.rb",
|
49
48
|
"lib/cuki.rb",
|
50
49
|
"lib/feature_file.rb",
|
data/cuki.yaml.sample
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
# the mappings associate the page IDs on Confluence with local feature files
|
3
3
|
---
|
4
4
|
host: http://mywiki
|
5
|
+
container: "h1. Acceptance Criteria"
|
5
6
|
tags:
|
6
7
|
draft: "{info:title=Draft version}"
|
7
8
|
signed_off: "{info:title=Signed-off}"
|
8
9
|
mappings:
|
9
|
-
123: features/products
|
10
|
-
124: features/
|
11
|
-
125: features/admin # folder mapping
|
10
|
+
123: features/products
|
11
|
+
124: features/admin
|
@@ -13,4 +13,26 @@ Feature: Error Handling
|
|
13
13
|
Then it should fail with:
|
14
14
|
"""
|
15
15
|
No config file found at config/cuki.yaml
|
16
|
-
"""
|
16
|
+
"""
|
17
|
+
|
18
|
+
Scenario: Missing scenarios
|
19
|
+
Given a file named "config/cuki.yaml" with:
|
20
|
+
"""
|
21
|
+
---
|
22
|
+
host: http://example.com
|
23
|
+
mappings:
|
24
|
+
123: features/products
|
25
|
+
"""
|
26
|
+
And a Confluence page on "example.com" with id 123:
|
27
|
+
"""
|
28
|
+
<input id="content-title" value="Add Product">
|
29
|
+
<div id="markupTextarea">
|
30
|
+
h1. Acceptance Criteria
|
31
|
+
</div>
|
32
|
+
"""
|
33
|
+
When I run `cuki pull --skip-autoformat`
|
34
|
+
Then it should fail with:
|
35
|
+
"""
|
36
|
+
No scenarios found in doc 123
|
37
|
+
"""
|
38
|
+
|
data/features/pull/pull.feature
CHANGED
@@ -1,55 +1,56 @@
|
|
1
|
-
Feature: Pull
|
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
|
-
|
1
|
+
# Feature: Pull
|
2
|
+
#
|
3
|
+
# @focus @announc
|
4
|
+
# Scenario: Pull all features
|
5
|
+
# Given a file named "config/cuki.yaml" with:
|
6
|
+
# """
|
7
|
+
# ---
|
8
|
+
# host: http://example.com
|
9
|
+
# mappings:
|
10
|
+
# 123: features/products/add_product.feature
|
11
|
+
# 456: features/products/remove_product.feature
|
12
|
+
# """
|
13
|
+
# And a Confluence page on "example.com" with id 123:
|
14
|
+
# """
|
15
|
+
# <input id="content-title" value="Add Product">
|
16
|
+
# <div id="markupTextarea">
|
17
|
+
# This feature describes adding a product
|
18
|
+
#
|
19
|
+
# # Some comment
|
20
|
+
#
|
21
|
+
# Blah
|
22
|
+
# </div>
|
23
|
+
# """
|
24
|
+
# And a Confluence page on "example.com" with id 456:
|
25
|
+
# """
|
26
|
+
# <input id="content-title" value="Remove Product">
|
27
|
+
# <div id="markupTextarea">
|
28
|
+
# This feature describes removing a product
|
29
|
+
# </div>
|
30
|
+
# """
|
31
|
+
# When I run `cuki pull --skip-autoformat`
|
32
|
+
# Then the file "features/products/add_product.feature" should contain exactly:
|
33
|
+
# """
|
34
|
+
# Feature: Add Product
|
35
|
+
#
|
36
|
+
# http://example.com/pages/viewpage.action?pageId=123
|
37
|
+
#
|
38
|
+
#
|
39
|
+
# This feature describes adding a product
|
40
|
+
#
|
41
|
+
# - Some comment
|
42
|
+
#
|
43
|
+
# Blah
|
44
|
+
#
|
45
|
+
# """
|
46
|
+
# And the file "features/products/remove_product.feature" should contain exactly:
|
47
|
+
# """
|
48
|
+
# Feature: Remove Product
|
49
|
+
#
|
50
|
+
# http://example.com/pages/viewpage.action?pageId=456
|
51
|
+
#
|
52
|
+
#
|
53
|
+
# This feature describes removing a product
|
54
|
+
#
|
55
|
+
# """
|
56
|
+
#
|
@@ -1,29 +1,34 @@
|
|
1
1
|
Feature: Pull single
|
2
2
|
|
3
|
-
|
3
|
+
@focus @announce
|
4
|
+
Scenario: Pull single feature
|
4
5
|
Given a file named "config/cuki.yaml" with:
|
5
6
|
"""
|
6
7
|
---
|
7
8
|
host: http://example.com
|
8
9
|
mappings:
|
9
|
-
123: features/products
|
10
|
-
456: features/
|
10
|
+
123: features/products
|
11
|
+
456: features/admin
|
11
12
|
"""
|
12
13
|
And a Confluence page on "example.com" with id 123:
|
13
14
|
"""
|
14
|
-
<input id="content-title" value="
|
15
|
+
<input id="content-title" value="Products">
|
15
16
|
<div id="markupTextarea">
|
16
|
-
|
17
|
+
h1. Acceptance Criteria
|
18
|
+
h2. Add Product
|
19
|
+
h6. Scenario
|
17
20
|
</div>
|
18
21
|
"""
|
19
22
|
And a Confluence page on "example.com" with id 456:
|
20
23
|
"""
|
21
|
-
<input id="content-title" value="
|
24
|
+
<input id="content-title" value="Admin">
|
22
25
|
<div id="markupTextarea">
|
23
|
-
|
26
|
+
h1. Acceptance Criteria
|
27
|
+
h2. Edit User
|
28
|
+
h6. Scenario
|
24
29
|
</div>
|
25
30
|
"""
|
26
|
-
When I run `cuki pull features/products
|
31
|
+
When I run `cuki pull features/products --skip-autoformat`
|
27
32
|
Then a file named "features/products/add_product.feature" should exist
|
28
|
-
But the file "features/
|
33
|
+
But the file "features/admin/edit_user.feature" should not exist
|
29
34
|
|
@@ -36,7 +36,7 @@ Feature: Splitting
|
|
36
36
|
h1. Next Section
|
37
37
|
</div>
|
38
38
|
"""
|
39
|
-
When I run `cuki pull --skip-autoformat
|
39
|
+
When I run `cuki pull --skip-autoformat`
|
40
40
|
Then the file "features/products/add_product.feature" should contain exactly:
|
41
41
|
"""
|
42
42
|
Feature: Add Product
|
@@ -62,7 +62,7 @@ Feature: Splitting
|
|
62
62
|
|
63
63
|
"""
|
64
64
|
|
65
|
-
@announce
|
65
|
+
@announce
|
66
66
|
Scenario: Pull all features (specified container)
|
67
67
|
Given a file named "config/cuki.yaml" with:
|
68
68
|
"""
|
@@ -89,7 +89,7 @@ Feature: Splitting
|
|
89
89
|
h1. Next Section
|
90
90
|
</div>
|
91
91
|
"""
|
92
|
-
When I run `cuki pull --skip-autoformat
|
92
|
+
When I run `cuki pull --skip-autoformat`
|
93
93
|
Then the file "features/products/add_product.feature" should contain exactly:
|
94
94
|
"""
|
95
95
|
Feature: Add Product
|
@@ -104,7 +104,7 @@ Feature: Splitting
|
|
104
104
|
"""
|
105
105
|
|
106
106
|
@announce
|
107
|
-
Scenario:Special Chars
|
107
|
+
Scenario: Special Chars
|
108
108
|
Given a file named "config/cuki.yaml" with:
|
109
109
|
"""
|
110
110
|
---
|
@@ -129,7 +129,7 @@ Feature: Splitting
|
|
129
129
|
h1. Next Section
|
130
130
|
</div>
|
131
131
|
"""
|
132
|
-
When I run `cuki pull --skip-autoformat
|
132
|
+
When I run `cuki pull --skip-autoformat`
|
133
133
|
Then the file "features/products/add_remove_product.feature" should contain exactly:
|
134
134
|
"""
|
135
135
|
Feature: Add/Remove Product
|
@@ -141,4 +141,5 @@ Feature: Splitting
|
|
141
141
|
Scenario: Scenario A
|
142
142
|
|
143
143
|
|
144
|
-
"""
|
144
|
+
"""
|
145
|
+
|
@@ -7,13 +7,17 @@ Feature: Tables
|
|
7
7
|
---
|
8
8
|
host: http://example.com
|
9
9
|
mappings:
|
10
|
-
123: features/products
|
10
|
+
123: features/products
|
11
11
|
"""
|
12
12
|
And a Confluence page on "example.com" with id 123:
|
13
13
|
"""
|
14
|
-
<input id="content-title" value="
|
14
|
+
<input id="content-title" value="Products">
|
15
15
|
<div id="markupTextarea">
|
16
|
-
|
16
|
+
h1. Acceptance Criteria
|
17
|
+
|
18
|
+
h2. Add Product
|
19
|
+
|
20
|
+
h6. Scenario: Foo
|
17
21
|
|
18
22
|
Given this:
|
19
23
|
|| foo || bar ||
|
@@ -21,13 +25,12 @@ Feature: Tables
|
|
21
25
|
| b | 2 |
|
22
26
|
</div>
|
23
27
|
"""
|
24
|
-
When I run `cuki pull --skip-autoformat
|
28
|
+
When I run `cuki pull --skip-autoformat`
|
25
29
|
Then the file "features/products/add_product.feature" should contain exactly:
|
26
30
|
"""
|
27
31
|
Feature: Add Product
|
28
32
|
|
29
|
-
http://example.com/pages/viewpage.action?pageId=123
|
30
|
-
|
33
|
+
http://example.com/pages/viewpage.action?pageId=123#Products-AddProduct
|
31
34
|
|
32
35
|
Scenario: Foo
|
33
36
|
|
data/features/pull/tags.feature
CHANGED
@@ -9,23 +9,28 @@ Feature: Tags
|
|
9
9
|
draft: "{info:title=Draft version}"
|
10
10
|
signed_off: "{info:title=Signed-off}"
|
11
11
|
mappings:
|
12
|
-
123: features/products
|
12
|
+
123: features/products
|
13
13
|
"""
|
14
14
|
And a Confluence page on "example.com" with id 123:
|
15
15
|
"""
|
16
|
-
<input id="content-title" value="
|
17
|
-
<div id="markupTextarea">
|
18
|
-
|
16
|
+
<input id="content-title" value="Products">
|
17
|
+
<div id="markupTextarea">
|
18
|
+
h1. Acceptance Criteria
|
19
|
+
|
20
|
+
{info:title=Draft version}
|
21
|
+
|
22
|
+
h2. Add Product
|
23
|
+
|
24
|
+
h6. Scenario: Foo
|
19
25
|
</div>
|
20
26
|
"""
|
21
|
-
When I run `cuki pull --skip-autoformat
|
27
|
+
When I run `cuki pull --skip-autoformat`
|
22
28
|
Then the file "features/products/add_product.feature" should contain exactly:
|
23
29
|
"""
|
24
30
|
@draft
|
25
31
|
Feature: Add Product
|
26
32
|
|
27
|
-
http://example.com/pages/viewpage.action?pageId=123
|
28
|
-
|
33
|
+
http://example.com/pages/viewpage.action?pageId=123#Products-AddProduct
|
29
34
|
|
30
35
|
Scenario: Foo
|
31
36
|
|
@@ -6,24 +6,27 @@ Feature: Textile
|
|
6
6
|
---
|
7
7
|
host: http://example.com
|
8
8
|
mappings:
|
9
|
-
123: features/products
|
9
|
+
123: features/products
|
10
10
|
"""
|
11
11
|
And a Confluence page on "example.com" with id 123:
|
12
12
|
"""
|
13
|
-
<input id="content-title" value="
|
13
|
+
<input id="content-title" value="Products">
|
14
14
|
<div id="markupTextarea">
|
15
|
-
|
15
|
+
h1. Acceptance Criteria
|
16
|
+
|
17
|
+
h2. Add Product
|
18
|
+
|
19
|
+
h6. Scenario: Foo
|
16
20
|
|
17
21
|
h6. Scenario Outline: Bar
|
18
22
|
</div>
|
19
23
|
"""
|
20
|
-
When I run `cuki pull --skip-autoformat
|
24
|
+
When I run `cuki pull --skip-autoformat`
|
21
25
|
Then the file "features/products/add_product.feature" should contain exactly:
|
22
26
|
"""
|
23
27
|
Feature: Add Product
|
24
28
|
|
25
|
-
http://example.com/pages/viewpage.action?pageId=123
|
26
|
-
|
29
|
+
http://example.com/pages/viewpage.action?pageId=123#Products-AddProduct
|
27
30
|
|
28
31
|
Scenario: Foo
|
29
32
|
|
data/lib/confluence_page.rb
CHANGED
@@ -11,7 +11,31 @@ class ConfluencePage
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def content
|
14
|
-
CGI.unescapeHTML @doc.css('#markupTextarea').text
|
14
|
+
content = CGI.unescapeHTML @doc.css('#markupTextarea').text
|
15
|
+
|
16
|
+
content.gsub!(' ', '')
|
17
|
+
|
18
|
+
# remove the double pipes used for table headers in Confluence
|
19
|
+
content.gsub!('||', '|')
|
20
|
+
|
21
|
+
# remove other noise
|
22
|
+
content.gsub!("\r\n", "\n")
|
23
|
+
content.gsub!("\\\\\n", '')
|
24
|
+
content.gsub!('\\', '')
|
25
|
+
|
26
|
+
# remove any unwanted headers
|
27
|
+
content.gsub!(/h\d\. (Scenario: .*)/, '\1')
|
28
|
+
content.gsub!(/h\d\. (Scenario Outline: .*)/, '\1')
|
29
|
+
content.gsub!(/h\d\. (Background: .*)/, '\1')
|
30
|
+
|
31
|
+
#Remove fancy quotes
|
32
|
+
content.gsub!('’', "'")
|
33
|
+
content.gsub!('‘', "'")
|
34
|
+
content.gsub!('“', '"')
|
35
|
+
content.gsub!('”', '"')
|
36
|
+
|
37
|
+
content.gsub!(/^#(.*)/, '-' + '\1')
|
38
|
+
content
|
15
39
|
end
|
16
40
|
|
17
41
|
end
|
data/lib/cuki.rb
CHANGED
@@ -6,7 +6,6 @@ require 'json'
|
|
6
6
|
require 'parallel'
|
7
7
|
require File.dirname(__FILE__) + '/string_utils'
|
8
8
|
require File.dirname(__FILE__) + '/link_builder'
|
9
|
-
require File.dirname(__FILE__) + '/cleaner'
|
10
9
|
require File.dirname(__FILE__) + '/confluence_page'
|
11
10
|
require File.dirname(__FILE__) + '/feature_file'
|
12
11
|
require File.dirname(__FILE__) + '/test_bits'
|
@@ -14,226 +13,138 @@ require File.dirname(__FILE__) + '/test_bits'
|
|
14
13
|
class Cuki
|
15
14
|
|
16
15
|
CONFIG_PATH = 'config/cuki.yaml'
|
16
|
+
DEFAULT_CONTAINER = /h1\. Acceptance Criteria/
|
17
|
+
PRIMARY_HEADER = "h1\."
|
18
|
+
FEATURE_HEADER = "h2\."
|
19
|
+
SCENARIO_HEADER = "h6\."
|
20
|
+
|
21
|
+
FLAGS = {:skip_autoformat => '--skip-autoformat'}
|
17
22
|
|
18
23
|
def self.invoke(args)
|
19
24
|
new(args)
|
20
25
|
end
|
21
26
|
|
22
27
|
def initialize(args)
|
23
|
-
|
28
|
+
@args = args
|
29
|
+
validate_args
|
24
30
|
read_config
|
25
31
|
configure_http_client
|
26
|
-
|
32
|
+
@link_builder = LinkBuilder.new(@config['host'])
|
27
33
|
action = args.first
|
28
|
-
if args.include?(
|
29
|
-
args.delete_if { |arg|
|
34
|
+
if args.include?(FLAGS[:skip_autoformat])
|
35
|
+
args.delete_if { |arg| FLAGS[:skip_autoformat] == arg }
|
30
36
|
@skip_autoformat = true
|
31
37
|
end
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
file = args[1]
|
40
|
-
if file
|
41
|
-
id = @config['mappings'].invert[file]
|
42
|
-
raise "could not get id for #{file}" unless id
|
43
|
-
pull_feature id, file
|
44
|
-
else
|
45
|
-
Parallel.map(@config['mappings'], :in_processes => 4) do |id, filepath|
|
46
|
-
pull_feature id, filepath
|
47
|
-
end
|
48
|
-
end
|
49
|
-
autoformat
|
38
|
+
configure_pull_stubs
|
39
|
+
verify_project
|
40
|
+
file = args[1]
|
41
|
+
if file
|
42
|
+
id = mappings.invert[file]
|
43
|
+
terminate "could not get id for #{file}" unless id
|
44
|
+
pull_feature id, file
|
50
45
|
else
|
51
|
-
|
52
|
-
|
46
|
+
Parallel.map(mappings, :in_processes => 4) do |id, filepath|
|
47
|
+
pull_feature id, filepath
|
48
|
+
end
|
53
49
|
end
|
50
|
+
autoformat!
|
54
51
|
end
|
55
52
|
|
56
53
|
private
|
57
|
-
|
54
|
+
|
58
55
|
def verify_project
|
59
|
-
|
60
|
-
|
61
|
-
autoformat
|
56
|
+
terminate "features folder not found" unless File.exists?('features')
|
57
|
+
`cucumber --dry-run -P`
|
62
58
|
end
|
63
|
-
|
59
|
+
|
64
60
|
def read_config
|
65
|
-
unless File.exist?(CONFIG_PATH)
|
66
|
-
puts "No config file found at #{CONFIG_PATH}"
|
67
|
-
exit(1)
|
68
|
-
end
|
61
|
+
terminate "No config file found at #{CONFIG_PATH}" unless File.exist?(CONFIG_PATH)
|
69
62
|
@config = YAML::load( File.open( CONFIG_PATH ) )
|
70
|
-
unless @config["host"]
|
71
|
-
|
72
|
-
exit(1)
|
73
|
-
end
|
74
|
-
unless @config["mappings"]
|
75
|
-
puts "Mappings not found in #{CONFIG_PATH}"
|
76
|
-
exit(1)
|
77
|
-
end
|
63
|
+
terminate "Host not found in #{CONFIG_PATH}" unless @config["host"]
|
64
|
+
terminate "Mappings not found in #{CONFIG_PATH}" unless @config["mappings"]
|
78
65
|
end
|
79
|
-
|
66
|
+
|
80
67
|
def configure_http_client
|
81
68
|
@client = HTTPClient.new
|
82
69
|
@client.ssl_config.set_trust_ca(ENV['CER']) if ENV['CER']
|
83
70
|
@client.ssl_config.set_client_cert_file(ENV['PEM'], ENV['PEM']) if ENV['PEM']
|
84
71
|
end
|
85
|
-
|
72
|
+
|
86
73
|
def pull_feature(id, filepath)
|
87
|
-
|
88
|
-
|
89
|
-
link_builder = LinkBuilder.new(@config['host'])
|
90
|
-
|
91
|
-
wiki_edit_link = link_builder.edit(id)
|
92
|
-
wiki_view_link = link_builder.view(id)
|
93
|
-
|
74
|
+
wiki_edit_link = @link_builder.edit(id)
|
94
75
|
puts "Downloading #{wiki_edit_link}"
|
95
76
|
response = @client.get wiki_edit_link
|
96
|
-
|
97
|
-
confluence_page = ConfluencePage.new(response.body)
|
98
|
-
|
99
|
-
unless confluence_page.content
|
100
|
-
puts "Not a valid confluence page:"
|
101
|
-
puts response.body
|
102
|
-
exit(1)
|
103
|
-
end
|
104
|
-
|
105
|
-
unless filepath.include?('.feature')
|
106
|
-
|
107
|
-
@config['container'] ||= /h1\. Acceptance Criteria/
|
108
|
-
|
109
|
-
handle_multi response.body, id
|
110
|
-
else
|
111
|
-
|
112
|
-
feature_file = FeatureFile.new
|
113
|
-
feature_file.title = confluence_page.title
|
114
|
-
feature_file.link = wiki_view_link
|
115
|
-
feature_file.content = confluence_page.content
|
116
|
-
|
117
|
-
content = Cleaner.clean(feature_file.to_s)
|
118
|
-
|
119
|
-
content = process_tags content
|
120
|
-
|
121
|
-
content = generated_by + content unless @skip_header
|
122
|
-
|
123
|
-
save_file content, filepath
|
124
|
-
end
|
77
|
+
handle_multi response.body, id
|
125
78
|
end
|
126
|
-
|
127
|
-
def
|
128
|
-
|
79
|
+
|
80
|
+
def container
|
81
|
+
@config['container'] ||= DEFAULT_CONTAINER
|
129
82
|
end
|
130
|
-
|
131
|
-
def autoformat
|
83
|
+
|
84
|
+
def autoformat!
|
132
85
|
`cucumber -a . --dry-run -P` unless @skip_autoformat
|
133
86
|
end
|
134
|
-
|
87
|
+
|
135
88
|
def handle_multi response_body, id
|
136
89
|
confluence_page = ConfluencePage.new(response_body)
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
puts "Could not find acceptance criteria container"
|
146
|
-
exit(1)
|
147
|
-
end
|
148
|
-
acceptance_criteria_block = @content.split(@config['container']).last
|
149
|
-
if acceptance_criteria_block.match(/h1\./)
|
150
|
-
acceptance_criteria_block = acceptance_criteria_block.split(/h1\./).first
|
151
|
-
end
|
152
|
-
unless acceptance_criteria_block
|
153
|
-
puts "Could not match #{@config['container']} in #{id}"
|
154
|
-
exit(1)
|
90
|
+
|
91
|
+
content = confluence_page.content
|
92
|
+
|
93
|
+
terminate "Could not find acceptance criteria container" unless content.match(container)
|
94
|
+
|
95
|
+
acceptance_criteria = content.split(container).last
|
96
|
+
if acceptance_criteria.include?(PRIMARY_HEADER)
|
97
|
+
acceptance_criteria = acceptance_criteria.split(/#{PRIMARY_HEADER}/).first
|
155
98
|
end
|
156
|
-
|
157
|
-
|
158
|
-
|
99
|
+
|
100
|
+
terminate "Could not match #{container} in #{id}" unless acceptance_criteria
|
101
|
+
|
102
|
+
scenario_titles = acceptance_criteria.scan(/#{FEATURE_HEADER} (.*)/).flatten
|
103
|
+
scenario_blocks = acceptance_criteria.split(/#{FEATURE_HEADER} .*/)
|
159
104
|
scenario_blocks.shift
|
160
105
|
|
161
106
|
combined = {}
|
162
107
|
found = 0
|
163
108
|
scenario_titles.each_with_index do |title, index|
|
164
|
-
combined[title] = scenario_blocks[index].gsub(
|
109
|
+
combined[title] = scenario_blocks[index].gsub(/#{SCENARIO_HEADER} (.*)/, '\1')
|
165
110
|
found += 1
|
166
111
|
end
|
167
|
-
if 0 == found
|
168
|
-
puts "No scenarios found in doc #{id}"
|
169
|
-
exit(1)
|
170
|
-
end
|
171
|
-
combined.each do |title, content|
|
172
|
-
|
173
|
-
tags = []
|
174
|
-
if @config['tags']
|
175
|
-
@config['tags'].each_pair do |tag, snippet|
|
176
|
-
tags << "@#{tag}" if @content.include?(snippet)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
unless tags.empty?
|
180
|
-
content = tags.join(' ') + "\n" + content
|
181
|
-
# tags.each do |tag|
|
182
|
-
# content.gsub!(@config['tags'][tag.gsub('@', '')], '')
|
183
|
-
# end
|
184
|
-
end
|
185
|
-
|
186
|
-
scenario_title_compressed = title.anchorize
|
187
|
-
feature_filename = title.parameterize
|
188
112
|
|
189
|
-
|
113
|
+
terminate "No scenarios found in doc #{id}" if 0 == found
|
114
|
+
combined.each do |title, content|
|
190
115
|
|
116
|
+
feature_filename = title.parameterize
|
117
|
+
dirpath = mappings[id]
|
191
118
|
FileUtils.mkdir_p(dirpath)
|
192
|
-
|
119
|
+
|
193
120
|
fname = "#{dirpath}/#{feature_filename.gsub("\r", '').parameterize}.feature"
|
121
|
+
puts "Writing #{fname}"
|
194
122
|
File.open(fname, 'w') do |f|
|
195
|
-
|
196
|
-
|
123
|
+
if @config['tags']
|
124
|
+
@config['tags'].each do |tag, token|
|
125
|
+
f.write "@#{tag}\n" if acceptance_criteria.include?(token)
|
126
|
+
end
|
127
|
+
end
|
197
128
|
f.write "Feature: #{title}\n\n"
|
198
|
-
|
199
|
-
f.write link
|
129
|
+
f.write @link_builder.view(id, confluence_page.title, title)
|
200
130
|
f.write content
|
201
131
|
end
|
202
132
|
end
|
203
133
|
end
|
204
|
-
|
205
|
-
def process_tags(content)
|
206
|
-
tags = []
|
207
|
-
if @config['tags']
|
208
|
-
@config['tags'].each_pair do |tag, snippet|
|
209
|
-
tags << "@#{tag}" if content.include?(snippet)
|
210
|
-
end
|
211
|
-
end
|
212
|
-
unless tags.empty?
|
213
|
-
content = tags.join(' ') + "\n" + content
|
214
|
-
tags.each do |tag|
|
215
|
-
content.gsub!(@config['tags'][tag.gsub('@', '')], '')
|
216
|
-
end
|
217
|
-
end
|
218
|
-
content
|
219
|
-
end
|
220
|
-
|
221
|
-
def save_file(content, filepath)
|
222
|
-
dir_path = File.dirname(filepath)
|
223
134
|
|
224
|
-
|
135
|
+
def validate_args
|
136
|
+
terminate "No action given" if @args.empty?
|
137
|
+
command = @args.first
|
138
|
+
terminate "Unknown action '#{@args.first}'" unless 'pull' == command
|
139
|
+
end
|
225
140
|
|
226
|
-
|
227
|
-
|
228
|
-
f.puts content
|
229
|
-
end
|
141
|
+
def mappings
|
142
|
+
@config['mappings']
|
230
143
|
end
|
231
144
|
|
232
|
-
def
|
233
|
-
|
234
|
-
|
235
|
-
exit(1)
|
236
|
-
end
|
145
|
+
def terminate(message)
|
146
|
+
puts message
|
147
|
+
exit(1)
|
237
148
|
end
|
238
|
-
|
149
|
+
|
239
150
|
end
|
data/lib/link_builder.rb
CHANGED
@@ -8,8 +8,10 @@ class LinkBuilder
|
|
8
8
|
@host + '/pages/editpage.action?pageId=' + id.to_s
|
9
9
|
end
|
10
10
|
|
11
|
-
def view(id)
|
12
|
-
@host + '/pages/viewpage.action?pageId=' + id.to_s
|
11
|
+
def view(id, feature=nil, scenario=nil)
|
12
|
+
link = @host + '/pages/viewpage.action?pageId=' + id.to_s
|
13
|
+
link += '#' + feature.anchorize if feature
|
14
|
+
link += '-' + scenario.anchorize if scenario
|
13
15
|
end
|
14
16
|
|
15
17
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cuki
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 9
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 11
|
10
|
+
version: 0.0.11
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Andy Waite
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-10-
|
18
|
+
date: 2011-10-23 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
requirement: &id001 !ruby/object:Gem::Requirement
|
@@ -169,7 +169,6 @@ files:
|
|
169
169
|
- features/step_defs/pull_steps.rb
|
170
170
|
- features/step_defs/push_steps.rb
|
171
171
|
- features/support/env.rb
|
172
|
-
- lib/cleaner.rb
|
173
172
|
- lib/confluence_page.rb
|
174
173
|
- lib/cuki.rb
|
175
174
|
- lib/feature_file.rb
|
data/lib/cleaner.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
class Cleaner
|
2
|
-
|
3
|
-
def self.clean(content)
|
4
|
-
|
5
|
-
content.gsub!(' ', '')
|
6
|
-
|
7
|
-
# remove the double pipes used for table headers in Confluence
|
8
|
-
content.gsub!('||', '|')
|
9
|
-
|
10
|
-
# remove other noise
|
11
|
-
content.gsub!("\r\n", "\n")
|
12
|
-
content.gsub!("\\\\\n", '')
|
13
|
-
content.gsub!('\\', '')
|
14
|
-
|
15
|
-
# remove any unwanted headers
|
16
|
-
content.gsub!(/h\d\. (Scenario: .*)/, '\1')
|
17
|
-
content.gsub!(/h\d\. (Scenario Outline: .*)/, '\1')
|
18
|
-
content.gsub!(/h\d\. (Background: .*)/, '\1')
|
19
|
-
|
20
|
-
#Remove fancy quotes
|
21
|
-
content.gsub!('’', "'")
|
22
|
-
content.gsub!('‘', "'")
|
23
|
-
content.gsub!('“', '"')
|
24
|
-
content.gsub!('”', '"')
|
25
|
-
|
26
|
-
content.gsub!(/^#(.*)/, '-' + '\1')
|
27
|
-
|
28
|
-
content
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|