howitzer 1.0.2 → 1.1.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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +3 -1
  4. data/.ruby-gemset +1 -0
  5. data/.travis.yml +4 -1
  6. data/.yardopts +5 -0
  7. data/CHANGELOG.md +23 -1
  8. data/CONTRIBUTING.md +14 -0
  9. data/GETTING_STARTED.md +283 -183
  10. data/Gemfile +3 -2
  11. data/LICENSE +1 -1
  12. data/README.md +93 -39
  13. data/Rakefile +4 -0
  14. data/bin/howitzer +34 -5
  15. data/features/cli_help.feature +3 -2
  16. data/features/cli_new.feature +1 -1
  17. data/features/cli_unknown.feature +1 -1
  18. data/features/cli_update.feature +84 -0
  19. data/features/step_definitions/common_steps.rb +9 -1
  20. data/generators/base_generator.rb +30 -15
  21. data/generators/config/config_generator.rb +7 -7
  22. data/generators/config/templates/custom.yml +1 -0
  23. data/generators/config/templates/default.yml +35 -5
  24. data/generators/cucumber/templates/env.rb +2 -2
  25. data/generators/cucumber/templates/transformers.rb +3 -1
  26. data/generators/root/templates/Gemfile +5 -3
  27. data/generators/root/templates/Rakefile +2 -0
  28. data/generators/rspec/templates/example_spec.rb +3 -3
  29. data/generators/rspec/templates/spec_helper.rb +6 -7
  30. data/howitzer.gemspec +15 -15
  31. data/lib/howitzer/capybara/settings.rb +125 -49
  32. data/lib/howitzer/helpers.rb +161 -94
  33. data/lib/howitzer/mailgun/client.rb +1 -1
  34. data/lib/howitzer/tasks/framework.rake +3 -0
  35. data/lib/howitzer/utils.rb +1 -1
  36. data/lib/howitzer/utils/locator_store.rb +1 -1
  37. data/lib/howitzer/utils/log.rb +1 -1
  38. data/lib/howitzer/utils/page_validator.rb +1 -1
  39. data/lib/howitzer/version.rb +1 -1
  40. data/lib/howitzer/web_page.rb +11 -11
  41. data/spec/spec_helper.rb +25 -22
  42. data/spec/support/generator_helper.rb +8 -1
  43. data/spec/unit/generators/base_generator_spec.rb +242 -0
  44. data/spec/unit/generators/config_generator_spec.rb +34 -0
  45. data/spec/unit/generators/cucumber_generator_spec.rb +45 -0
  46. data/spec/unit/generators/emails_generator_spec.rb +31 -0
  47. data/spec/unit/generators/pages_generator_spec.rb +33 -0
  48. data/spec/unit/generators/root_generator_spec.rb +35 -0
  49. data/spec/unit/generators/rspec_generator_spec.rb +36 -0
  50. data/spec/unit/generators/tasks_generator_spec.rb +31 -0
  51. data/spec/unit/lib/capybara/dsl_ex_spec.rb +11 -11
  52. data/spec/unit/lib/capybara/settings_spec.rb +336 -58
  53. data/spec/unit/lib/email_spec.rb +17 -17
  54. data/spec/unit/lib/helpers_spec.rb +699 -315
  55. data/spec/unit/lib/mailgun/client_spec.rb +9 -9
  56. data/spec/unit/lib/mailgun/connector_spec.rb +20 -20
  57. data/spec/unit/lib/mailgun/response_spec.rb +9 -9
  58. data/spec/unit/lib/settings_spec.rb +6 -6
  59. data/spec/unit/lib/utils/data_generator/data_storage_spec.rb +31 -31
  60. data/spec/unit/lib/utils/data_generator/gen_spec.rb +20 -20
  61. data/spec/unit/lib/utils/locator_store_spec.rb +39 -39
  62. data/spec/unit/lib/utils/log_spec.rb +42 -42
  63. data/spec/unit/lib/utils/page_validator_spec.rb +69 -70
  64. data/spec/unit/lib/web_page_spec.rb +91 -69
  65. data/spec/unit/version_spec.rb +3 -3
  66. metadata +100 -78
  67. data/spec/unit/generators/generators_spec.rb +0 -175
@@ -1,3 +1,6 @@
1
+ require 'fileutils'
2
+ require_relative '../../generators/base_generator'
3
+
1
4
  module GeneratorHelper
2
5
  def file_tree_info(root)
3
6
  Dir["#{root}/**/*"].sort_by{|name| name.sub(root, '') }.map do |name|
@@ -10,4 +13,8 @@ module GeneratorHelper
10
13
  def template_file_size(root_directory, file)
11
14
  File.size(File.join(generators_path, root_directory, 'templates', file))
12
15
  end
13
- end
16
+ end
17
+
18
+ Howitzer::BaseGenerator.logger = StringIO.new
19
+ Howitzer::BaseGenerator.destination = Dir.mktmpdir
20
+ Dir[File.join(generators_path, '**', '*_generator.rb')].each{ |f| require f }
@@ -0,0 +1,242 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Howitzer::BaseGenerator do
4
+ let(:output) { StringIO.new }
5
+ let(:destination) { Dir.mktmpdir }
6
+
7
+ describe '.logger' do
8
+ subject { described_class.logger }
9
+ before { described_class.instance_variable_set('@logger', output) }
10
+ it { is_expected.to eq(output) }
11
+ end
12
+
13
+ describe '.logger=' do
14
+ subject { described_class.instance_variable_get('@logger') }
15
+ before { described_class.logger = output }
16
+ it { is_expected.to eq(output) }
17
+ end
18
+
19
+ describe '.destination' do
20
+ subject { described_class.destination }
21
+ before { described_class.instance_variable_set('@destination', destination) }
22
+ it { is_expected.to eq(destination) }
23
+ end
24
+
25
+ describe '.destination=' do
26
+ subject { described_class.instance_variable_get('@destination') }
27
+ before { described_class.destination = destination }
28
+ it { is_expected.to eq(destination) }
29
+ end
30
+
31
+ describe 'constructor' do
32
+ let(:list1) { double(:list1) }
33
+ let(:list2) { double(:list2) }
34
+ subject { described_class.new }
35
+ before do
36
+ expect_any_instance_of(described_class).to receive(:print_banner).with(no_args).once
37
+ allow_any_instance_of(described_class).to receive(:manifest) do
38
+ {
39
+ files: list1,
40
+ templates: list2,
41
+ unknown: nil
42
+ }
43
+ end
44
+ end
45
+ it do
46
+ expect_any_instance_of(described_class).to receive(:copy_files).with(list1).once
47
+ expect_any_instance_of(described_class).to receive(:copy_templates).with(list2).once
48
+ subject
49
+ end
50
+ end
51
+
52
+ describe '#manifest' do
53
+ subject { described_class.new.manifest }
54
+ before { allow_any_instance_of(described_class).to receive(:initialize) { nil } }
55
+ it { is_expected.to be_nil }
56
+ end
57
+
58
+ describe '#banner' do
59
+ subject { described_class.new.send(:banner) }
60
+ before { allow_any_instance_of(described_class).to receive(:initialize) { nil } }
61
+ it { is_expected.to be_nil }
62
+ end
63
+
64
+ describe '#logger' do
65
+ subject { described_class.new.send(:logger) }
66
+ before { allow_any_instance_of(described_class).to receive(:initialize) { nil } }
67
+ context 'when not specified' do
68
+ before { described_class.instance_variable_set('@logger', nil) }
69
+ it { is_expected.to eq($stdout) }
70
+ end
71
+ context 'when custom' do
72
+ let(:output) { StringIO.new }
73
+ before { described_class.instance_variable_set('@logger', output) }
74
+ it { is_expected.to eq(output) }
75
+ end
76
+ end
77
+
78
+ describe '#destination' do
79
+ subject { described_class.new.send(:destination) }
80
+ before do
81
+ allow_any_instance_of(described_class).to receive(:initialize) { nil }
82
+ allow(described_class).to receive(:destination) { '/' }
83
+ end
84
+ it { is_expected.to eq('/') }
85
+ end
86
+
87
+ describe '#copy_files' do
88
+ let(:list) { [ {source: 'example.txt'} ] }
89
+ let(:source_path) { '/example_path/example.txt' }
90
+ let(:generator) { described_class.new }
91
+ subject { generator.send(:copy_files, list) }
92
+ before do
93
+ allow_any_instance_of(described_class).to receive(:initialize) { nil }
94
+ allow(generator).to receive(:source_path).with(list.first[:source]) { source_path }
95
+ end
96
+ after { subject }
97
+ context 'when source_file exists' do
98
+ before { allow(File).to receive(:exists?).with(source_path) { true } }
99
+ it { expect(generator).to receive(:copy_with_path).with(list.first).once }
100
+ end
101
+ context 'when source_file missing' do
102
+ before { allow(File).to receive(:exists?).with(source_path) { false } }
103
+ it { expect(generator).to receive(:puts_error).with("File '/example_path/example.txt' was not found.").once }
104
+ end
105
+ end
106
+
107
+ describe '#copy_templates' do
108
+ subject { described_class.new.send(:copy_templates, nil) }
109
+ before { allow_any_instance_of(described_class).to receive(:initialize) { nil } }
110
+ it { is_expected.to be_nil }
111
+ end
112
+
113
+ describe '#print_banner' do
114
+ let(:generator) { described_class.new }
115
+ subject { generator.send(:print_banner) }
116
+ before do
117
+ allow_any_instance_of(described_class).to receive(:initialize) { nil }
118
+ allow(generator).to receive(:banner) { banner }
119
+ end
120
+ after { subject }
121
+ context 'when banner present' do
122
+ let(:banner) { 'banner' }
123
+ it { expect(described_class.logger).to receive(:puts).with(banner).once }
124
+ end
125
+ context 'when banner blank' do
126
+ let(:banner) { '' }
127
+ it { expect(described_class.logger).not_to receive(:puts) }
128
+ end
129
+ end
130
+
131
+ describe '#print_info' do
132
+ subject { described_class.new.send(:print_info, 'data') }
133
+ before { allow_any_instance_of(described_class).to receive(:initialize) { nil } }
134
+ after { subject }
135
+ it { expect(described_class.logger).to receive(:print).with(' data')}
136
+ end
137
+
138
+ describe '#puts_info' do
139
+ subject { described_class.new.send(:puts_info, 'data') }
140
+ before { allow_any_instance_of(described_class).to receive(:initialize) { nil } }
141
+ after { subject }
142
+ it { expect(described_class.logger).to receive(:puts).with(' data')}
143
+ end
144
+
145
+ describe '#puts_error' do
146
+ subject { described_class.new.send(:puts_error, 'data') }
147
+ before { allow_any_instance_of(described_class).to receive(:initialize) { nil } }
148
+ after { subject }
149
+ it { expect(described_class.logger).to receive(:puts).with(' ERROR: data')}
150
+ end
151
+
152
+ describe '#source_path' do
153
+ subject { described_class.new.send(:source_path, 'example.txt') }
154
+ before do
155
+ allow_any_instance_of(described_class).to receive(:initialize) { nil }
156
+ allow(File).to receive(:dirname) { '/' }
157
+ end
158
+ it { is_expected.to eq('/base/templates/example.txt') }
159
+ end
160
+
161
+ describe '#dest_path' do
162
+ subject { described_class.new.send(:dest_path, 'example.txt') }
163
+ before { allow_any_instance_of(described_class).to receive(:initialize) { nil } }
164
+ it { is_expected.to include('/example.txt') }
165
+ end
166
+
167
+ describe '#copy_with_path' do
168
+ let(:generator) { described_class.new }
169
+ let(:data) { {source: 's.txt', destination: 'd.txt'} }
170
+ let(:src) { '/path/to/s.txt' }
171
+ let(:dst) { '/path/to/d.txt' }
172
+ subject { generator.send(:copy_with_path, data) }
173
+ before do
174
+ allow_any_instance_of(described_class).to receive(:initialize) { nil }
175
+ allow(generator).to receive(:source_path).with('s.txt') { src }
176
+ allow(generator).to receive(:dest_path).with('d.txt') { dst }
177
+ allow(FileUtils).to receive(:mkdir_p).with('/path/to') { true }
178
+ end
179
+ after { subject }
180
+ context 'when destination file present' do
181
+ before { allow(File).to receive(:exists?).with(dst) { true } }
182
+ context 'when identical with source file' do
183
+ before { allow(FileUtils).to receive(:identical?).with(src, dst) { true } }
184
+ it { expect(generator).to receive(:puts_info).with("Identical 'd.txt' file").once }
185
+ end
186
+ context 'when not identical with source file' do
187
+ before do
188
+ allow(FileUtils).to receive(:identical?).with(src, dst) { false }
189
+ expect(generator).to receive(:puts_info).with("Conflict with 'd.txt' file")
190
+ expect(generator).to receive(:print_info).with(" Overwrite 'd.txt' file? [Yn]:")
191
+ end
192
+ context 'when user typed Y' do
193
+ before { allow(generator).to receive(:gets) { 'Y' } }
194
+ it do
195
+ expect(FileUtils).to receive(:cp).with(src, dst) {nil}.once
196
+ expect(generator).to receive(:puts_info).with(" Forced 'd.txt' file")
197
+ end
198
+ end
199
+ context 'when user typed y' do
200
+ before { allow(generator).to receive(:gets) { 'y' } }
201
+ it do
202
+ expect(FileUtils).to receive(:cp).with(src, dst) {nil}.once
203
+ expect(generator).to receive(:puts_info).with(" Forced 'd.txt' file")
204
+ end
205
+ end
206
+ context 'when user typed N' do
207
+ before { allow(generator).to receive(:gets) { 'N' } }
208
+ it do
209
+ expect(generator).to receive(:puts_info).with(" Skipped 'd.txt' file")
210
+ expect(FileUtils).not_to receive(:cp)
211
+ end
212
+ end
213
+ context 'when user typed n' do
214
+ before { allow(generator).to receive(:gets) { 'n' } }
215
+ it do
216
+ expect(generator).to receive(:puts_info).with(" Skipped 'd.txt' file")
217
+ expect(FileUtils).not_to receive(:cp)
218
+ end
219
+ end
220
+ context 'when user typed hello' do
221
+ before { allow(generator).to receive(:gets) { 'hello' } }
222
+ it do
223
+ expect(generator).not_to receive(:puts_info)
224
+ expect(FileUtils).not_to receive(:cp)
225
+ end
226
+ end
227
+ end
228
+ end
229
+ context 'when destination file missing' do
230
+ before { allow(File).to receive(:exists?).with(dst) { false } }
231
+ it do
232
+ expect(generator).to receive(:puts_info).with("Added 'd.txt' file")
233
+ expect(FileUtils).to receive(:cp).with(src, dst).once
234
+ end
235
+ end
236
+ context 'when exception happened' do
237
+ before { allow(FileUtils).to receive(:mkdir_p).and_raise(StandardError.new('Some error')) }
238
+ it { expect(generator).to receive(:puts_error).with("Impossible to create 'd.txt' file. Reason: Some error")}
239
+ end
240
+ end
241
+
242
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Generators' do
4
+ let(:destination) { Howitzer::BaseGenerator.destination }
5
+ let(:output) { StringIO.new }
6
+ subject { file_tree_info(destination) }
7
+ before do
8
+ Howitzer::BaseGenerator.logger = output
9
+ generator_name.new
10
+ end
11
+ after { FileUtils.rm_r(destination) }
12
+
13
+ describe 'ConfigGenerator' do
14
+ let(:generator_name) { Howitzer::ConfigGenerator }
15
+ let(:expected_result) do
16
+ [
17
+ {:name=> '/config', :is_directory=>true},
18
+ {:name=> '/config/custom.yml', :is_directory=>false, :size=>template_file_size('config', 'custom.yml')},
19
+ {:name=> '/config/default.yml', :is_directory=>false, :size=>template_file_size('config', 'default.yml')}
20
+ ]
21
+ end
22
+
23
+ it { is_expected.to eql(expected_result) }
24
+ describe 'output' do
25
+ let(:expected_output) do
26
+ " * Config files generation ...
27
+ Added 'config/custom.yml' file
28
+ Added 'config/default.yml' file\n"
29
+ end
30
+ subject { output.string }
31
+ it { is_expected.to eql(expected_output) }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Generators' do
4
+ let(:destination) { Howitzer::BaseGenerator.destination }
5
+ let(:output) { StringIO.new }
6
+ subject { file_tree_info(destination) }
7
+ before do
8
+ Howitzer::BaseGenerator.logger = output
9
+ generator_name.new
10
+ end
11
+ after { FileUtils.rm_r(destination) }
12
+
13
+ describe 'CucumberGenerator' do
14
+ let(:generator_name) { Howitzer::CucumberGenerator }
15
+ let(:expected_result) do
16
+ [
17
+ {:name=> '/config', :is_directory=>true},
18
+ {:name=> '/config/cucumber.yml', :is_directory=>false, :size=>template_file_size('cucumber', 'cucumber.yml')},
19
+ {:name=> '/features', :is_directory=>true},
20
+ {:name=> '/features/example.feature', :is_directory=>false, :size=>template_file_size('cucumber', 'example.feature')},
21
+ {:name=> '/features/step_definitions', :is_directory=>true},
22
+ {:name=> '/features/step_definitions/common_steps.rb', :is_directory=>false, :size=>template_file_size('cucumber', 'common_steps.rb')},
23
+ {:name=> '/features/support', :is_directory=>true},
24
+ {:name=> '/features/support/env.rb', :is_directory=>false, :size=>template_file_size('cucumber', 'env.rb')},
25
+ {:name=> '/features/support/transformers.rb', :is_directory=>false, :size=>template_file_size('cucumber', 'transformers.rb')},
26
+ {:name=> '/tasks', :is_directory=>true},
27
+ {:name=> '/tasks/cucumber.rake', :is_directory=>false, :size=>template_file_size('cucumber', 'cucumber.rake')}
28
+ ]
29
+ end
30
+ it { is_expected.to eql(expected_result) }
31
+ describe 'output' do
32
+ let(:expected_output) do
33
+ " * Cucumber integration to the framework ...
34
+ Added 'features/step_definitions/common_steps.rb' file
35
+ Added 'features/support/env.rb' file
36
+ Added 'features/support/transformers.rb' file
37
+ Added 'features/example.feature' file
38
+ Added 'tasks/cucumber.rake' file
39
+ Added 'config/cucumber.yml' file\n"
40
+ end
41
+ subject { output.string }
42
+ it { is_expected.to eql(expected_output) }
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Generators' do
4
+ let(:destination) { Howitzer::BaseGenerator.destination }
5
+ let(:output) { StringIO.new }
6
+ subject { file_tree_info(destination) }
7
+ before do
8
+ Howitzer::BaseGenerator.logger = output
9
+ generator_name.new
10
+ end
11
+ after { FileUtils.rm_r(destination) }
12
+
13
+ describe 'EmailsGenerator' do
14
+ let(:generator_name) { Howitzer::EmailsGenerator }
15
+ let(:expected_result) do
16
+ [
17
+ {:name=> '/emails', :is_directory=>true},
18
+ {:name=> '/emails/example_email.rb', :is_directory=>false, :size=>template_file_size('emails', 'example_email.rb')}
19
+ ]
20
+ end
21
+ it { is_expected.to eql(expected_result) }
22
+ describe 'output' do
23
+ let(:expected_output) do
24
+ " * Email example generation ...
25
+ Added '/emails/example_email.rb' file\n"
26
+ end
27
+ subject { output.string }
28
+ it { is_expected.to eql(expected_output) }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Generators' do
4
+ let(:destination) { Howitzer::BaseGenerator.destination }
5
+ let(:output) { StringIO.new }
6
+ subject { file_tree_info(destination) }
7
+ before do
8
+ Howitzer::BaseGenerator.logger = output
9
+ generator_name.new
10
+ end
11
+ after { FileUtils.rm_r(destination) }
12
+
13
+ describe 'PagesGenerator' do
14
+ let(:generator_name) { Howitzer::PagesGenerator }
15
+ let(:expected_result) do
16
+ [
17
+ {:name=> '/pages', :is_directory=>true},
18
+ {:name=> '/pages/example_menu.rb', :is_directory=>false, :size=>template_file_size('pages', 'example_menu.rb')},
19
+ {:name=> '/pages/example_page.rb', :is_directory=>false, :size=>template_file_size('pages', 'example_page.rb')}
20
+ ]
21
+ end
22
+ it { is_expected.to eql(expected_result) }
23
+ describe 'output' do
24
+ let(:expected_output) do
25
+ " * PageOriented pattern structure generation ...
26
+ Added 'pages/example_page.rb' file
27
+ Added 'pages/example_menu.rb' file\n"
28
+ end
29
+ subject { output.string }
30
+ it { is_expected.to eql(expected_output) }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Generators' do
4
+ let(:destination) { Howitzer::BaseGenerator.destination }
5
+ let(:output) { StringIO.new }
6
+ subject { file_tree_info(destination) }
7
+ before do
8
+ Howitzer::BaseGenerator.logger = output
9
+ generator_name.new
10
+ end
11
+ after { FileUtils.rm_r(destination) }
12
+
13
+ describe 'RootGenerator' do
14
+ let(:generator_name) { Howitzer::RootGenerator }
15
+ let(:expected_result) do
16
+ [
17
+ {:name=> '/Gemfile', :is_directory=>false, :size=>template_file_size('root', 'Gemfile')},
18
+ {:name=> '/Rakefile', :is_directory=>false, :size=>template_file_size('root', 'Rakefile')},
19
+ {:name=> '/boot.rb', :is_directory=>false, :size=>template_file_size('root', 'boot.rb')}
20
+ ]
21
+ end
22
+ it { is_expected.to eql(expected_result) }
23
+ describe 'output' do
24
+ let(:expected_output) do
25
+ " * Root files generation ...
26
+ Added '.gitignore' file
27
+ Added 'Gemfile' file
28
+ Added 'Rakefile' file
29
+ Added 'boot.rb' file\n"
30
+ end
31
+ subject { output.string }
32
+ it { is_expected.to eql(expected_output) }
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Generators' do
4
+ let(:destination) { Howitzer::BaseGenerator.destination }
5
+ let(:output) { StringIO.new }
6
+ subject { file_tree_info(destination) }
7
+ before do
8
+ Howitzer::BaseGenerator.logger = output
9
+ generator_name.new
10
+ end
11
+ after { FileUtils.rm_r(destination) }
12
+
13
+ describe 'RSpecGenerator' do
14
+ let(:generator_name) { Howitzer::RspecGenerator }
15
+ let(:expected_result) do
16
+ [
17
+ {:name=> '/spec', :is_directory=>true},
18
+ {:name=> '/spec/example_spec.rb', :is_directory=>false, :size=>template_file_size('rspec', 'example_spec.rb')},
19
+ {:name=> '/spec/spec_helper.rb', :is_directory=>false, :size=>template_file_size('rspec', 'spec_helper.rb')},
20
+ {:name=> '/tasks', :is_directory=>true},
21
+ {:name=> '/tasks/rspec.rake', :is_directory=>false, :size=>template_file_size('rspec', 'rspec.rake')}
22
+ ]
23
+ end
24
+ it { is_expected.to eql(expected_result) }
25
+ describe 'output' do
26
+ let(:expected_output) do
27
+ " * RSpec integration to the framework ...
28
+ Added 'spec/spec_helper.rb' file
29
+ Added 'spec/example_spec.rb' file
30
+ Added 'tasks/rspec.rake' file\n"
31
+ end
32
+ subject { output.string }
33
+ it { is_expected.to eql(expected_output) }
34
+ end
35
+ end
36
+ end