antwort 0.0.12
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 +7 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.rubocop.yml +19 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +249 -0
- data/Gemfile +3 -0
- data/Guardfile +14 -0
- data/README.md +108 -0
- data/Rakefile +14 -0
- data/antwort.gemspec +45 -0
- data/bin/antwort +5 -0
- data/lib/antwort.rb +13 -0
- data/lib/antwort/builder.rb +8 -0
- data/lib/antwort/builder/builder.rb +104 -0
- data/lib/antwort/builder/email.rb +61 -0
- data/lib/antwort/builder/flattener.rb +37 -0
- data/lib/antwort/builder/helpers/logic.rb +82 -0
- data/lib/antwort/builder/helpers/sanitizers.rb +29 -0
- data/lib/antwort/builder/partial.rb +80 -0
- data/lib/antwort/builder/style.rb +59 -0
- data/lib/antwort/cli.rb +7 -0
- data/lib/antwort/cli/cli.rb +275 -0
- data/lib/antwort/cli/helpers.rb +44 -0
- data/lib/antwort/cli/send.rb +79 -0
- data/lib/antwort/cli/upload.rb +89 -0
- data/lib/antwort/helpers.rb +19 -0
- data/lib/antwort/server.rb +70 -0
- data/lib/antwort/server/assets.rb +23 -0
- data/lib/antwort/server/helpers.rb +67 -0
- data/lib/antwort/server/markup.rb +39 -0
- data/lib/antwort/version.rb +3 -0
- data/spec/builder/builder_spec.rb +30 -0
- data/spec/builder/email_spec.rb +21 -0
- data/spec/builder/flattener_spec.rb +64 -0
- data/spec/builder/helpers_logic_spec.rb +244 -0
- data/spec/builder/partial_spec.rb +87 -0
- data/spec/builder/style_spec.rb +66 -0
- data/spec/cli/helpers_spec.rb +60 -0
- data/spec/cli/upload_spec.rb +47 -0
- data/spec/cli_spec.rb +46 -0
- data/spec/fixtures/assets/images/1-demo/placeholder.png +0 -0
- data/spec/fixtures/assets/images/newsletter/placeholder.png +0 -0
- data/spec/fixtures/assets/images/shared/placeholder-grey.png +0 -0
- data/spec/fixtures/assets/images/shared/placeholder-white.png +0 -0
- data/spec/fixtures/build/demo-123456/build.html +7 -0
- data/spec/fixtures/build/demo-123457/build.html +7 -0
- data/spec/fixtures/build/demo-bar-123/build.html +7 -0
- data/spec/fixtures/build/foo-1/build.html +7 -0
- data/spec/fixtures/data/1-demo.yml +3 -0
- data/spec/fixtures/emails/1-demo/index.html.erb +9 -0
- data/spec/fixtures/emails/2-no-layout/index.html.erb +6 -0
- data/spec/fixtures/emails/3-no-title/index.html.erb +1 -0
- data/spec/fixtures/views/404.html.erb +1 -0
- data/spec/fixtures/views/index.html.erb +14 -0
- data/spec/fixtures/views/layout.erb +8 -0
- data/spec/fixtures/views/server.erb +5 -0
- data/spec/server_spec.rb +54 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/capture.rb +17 -0
- data/template/email/css/include.scss +5 -0
- data/template/email/css/inline.scss +5 -0
- data/template/email/email.html.erb +11 -0
- data/template/email/images/.empty_directory +0 -0
- data/template/project/.env.sample +21 -0
- data/template/project/.gitignore.tt +17 -0
- data/template/project/.ruby-version +1 -0
- data/template/project/Gemfile.tt +9 -0
- data/template/project/Guardfile +9 -0
- data/template/project/assets/css/demo/include.scss +3 -0
- data/template/project/assets/css/demo/inline.scss +33 -0
- data/template/project/assets/css/server.scss +167 -0
- data/template/project/assets/css/shared/_base.scss +64 -0
- data/template/project/assets/css/shared/_mixins.scss +25 -0
- data/template/project/assets/css/shared/_reset.scss +59 -0
- data/template/project/assets/css/shared/_vars.scss +12 -0
- data/template/project/assets/css/shared/include.scss +23 -0
- data/template/project/assets/css/shared/inline.scss +9 -0
- data/template/project/assets/images/.gitkeep +0 -0
- data/template/project/assets/images/shared/placeholder.png +0 -0
- data/template/project/build/.empty_directory +0 -0
- data/template/project/data/.empty_directory +0 -0
- data/template/project/data/config.yml +3 -0
- data/template/project/data/demo.yml +4 -0
- data/template/project/emails/demo/_partial.html.erb +9 -0
- data/template/project/emails/demo/index.html.erb +54 -0
- data/template/project/views/404.html.erb +8 -0
- data/template/project/views/index.html.erb +18 -0
- data/template/project/views/layout.erb +38 -0
- data/template/project/views/markup/_button.html.erb +5 -0
- data/template/project/views/markup/_image_tag.html.erb +10 -0
- data/template/project/views/server.erb +32 -0
- metadata +443 -0
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Antwort::PartialBuilder do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
Dir.chdir(fixtures_root)
|
7
|
+
end
|
8
|
+
|
9
|
+
before :each do
|
10
|
+
allow($stdout).to receive(:write) # Ignore warnings
|
11
|
+
@builder = Antwort::PartialBuilder.new({email: '1-demo', id: '123', 'css-style': 'expanded'})
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "Builds" do
|
15
|
+
it "builds HTML"
|
16
|
+
it "inlines CSS"
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "Helpers" do
|
20
|
+
describe "adjusts filename as necssary (make sure it ends with .html)" do
|
21
|
+
|
22
|
+
it "always starts with _" do
|
23
|
+
expect(@builder.adjust_filename('foo.erb')).to eq('_foo.html')
|
24
|
+
expect(@builder.adjust_filename('foo.html')).to eq('_foo.html')
|
25
|
+
expect(@builder.adjust_filename('foo.html.erb')).to eq('_foo.html')
|
26
|
+
end
|
27
|
+
|
28
|
+
it "always ends with .html" do
|
29
|
+
expect(@builder.adjust_filename('_foo.html.erb')).to eq('_foo.html')
|
30
|
+
expect(@builder.adjust_filename('_foo.erb')).to eq('_foo.html')
|
31
|
+
expect(@builder.adjust_filename('_foo.html')).to eq('_foo.html')
|
32
|
+
end
|
33
|
+
|
34
|
+
it "uses template name instead of _index" do
|
35
|
+
expect(@builder.adjust_filename('index.html.erb')).to eq('_1-demo.html')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "save from Nokogiri" do
|
40
|
+
let (:start) { '<div id="valid-dom-tree">' }
|
41
|
+
let (:ende) {'</div><!--/#valid-dom-tree-->' }
|
42
|
+
|
43
|
+
it "can add nokogiri wrapper" do
|
44
|
+
expect(@builder.send(:add_nokogiri_wrapper, 'foo')).to eq(start + 'foo' + ende)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "can remove nokogiri wrapper" do
|
48
|
+
expect(@builder.send(:remove_nokogiri_wrapper, start + 'foo' + ende)).to eq('foo')
|
49
|
+
expect(@builder.send(:remove_nokogiri_wrapper, start + 'foo' + "</div> <!--/#valid-dom-tree-->")).to eq('foo')
|
50
|
+
expect(@builder.send(:remove_nokogiri_wrapper, start + 'foo' + "</div>\n<!--/#valid-dom-tree-->")).to eq('foo')
|
51
|
+
end
|
52
|
+
|
53
|
+
# TODO: These specs don't work because PartialBuilder cannot find matching CSS
|
54
|
+
|
55
|
+
it "adds wrapper before inlining", skip: true do
|
56
|
+
@builder.inline('foo')
|
57
|
+
expect(@builder).to receive(:add_nokogiri_wrapper)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "removes wrapper after inlining", skip: true do
|
61
|
+
@builder.inline('foo')
|
62
|
+
expect(@builder).to receive(:remove_nokogiri_wrapper)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "Clean up" do
|
67
|
+
it "can convert logic html entities back to operators" do
|
68
|
+
h = {
|
69
|
+
"{% <= %}" => "{% <= %}", # only convert operators within logic
|
70
|
+
"{{ <= }}" => "{{ <= }}", # leave operators in content
|
71
|
+
"{% >= %}" => "{% >= %}",
|
72
|
+
"{{ >= }}" => "{{ >= }}",
|
73
|
+
" && " => " && "
|
74
|
+
}
|
75
|
+
h.each do |key, value|
|
76
|
+
expect(@builder.cleanup_logic(key)).to eq(value)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it "can restore variables in links" do
|
81
|
+
a = '<a href="%7B%7B%20hello%20%7D%7D">world</a>'
|
82
|
+
b = '<a href="{{ hello }}">world</a>'
|
83
|
+
expect(@builder.restore_variables_in_links(a)).to eq(b)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Antwort::Style do
|
4
|
+
|
5
|
+
describe "on Initialize" do
|
6
|
+
let (:style) { Antwort::Style.new('font-weight:bold;font-size:24px;line-height:28px;font-family:Helvetica, Arial, sans-serif;color:#111111;color:#2CA4D8') }
|
7
|
+
|
8
|
+
it "remembers all keys" do
|
9
|
+
expect(style.keys).to eq(['font-weight','font-size','line-height','font-family','color'])
|
10
|
+
end
|
11
|
+
it "remembers duplicate keys" do
|
12
|
+
expect(style.duplicate_keys).to eq(['color'])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "Readable Attributes" do
|
17
|
+
let (:style) { Antwort::Style.new('font-size:24px;color:black;color:red') }
|
18
|
+
|
19
|
+
context "not flattened" do
|
20
|
+
it "can be accessed as a hash" do
|
21
|
+
expect(style.original).to eq([{'font-size' => '24px'}, {'color'=>'black'}, {'color'=>'red'}])
|
22
|
+
end
|
23
|
+
it "can be accessed as a string" do
|
24
|
+
expect(style.original_str).to eq('font-size:24px;color:black;color:red')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "flattened" do
|
29
|
+
it "can be accessed as a hash" do
|
30
|
+
expect(style.flattened).to eq({
|
31
|
+
'font-size' => '24px',
|
32
|
+
'color' => 'red'
|
33
|
+
})
|
34
|
+
end
|
35
|
+
it "can be accessed as a string" do
|
36
|
+
expect(style.flattened_str).to eq('font-size:24px;color:red')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "has a duplicates? method" do
|
41
|
+
expect(Antwort::Style.new('color:red;color:black').duplicates?).to eq(true)
|
42
|
+
expect(Antwort::Style.new('color:red;font-size:12px').duplicates?).to eq(false)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "Code Formatting" do
|
47
|
+
it "trims leading and trailing white space from styles" do
|
48
|
+
style = Antwort::Style.new('color:red; color: black;border :none;')
|
49
|
+
expect(style.flattened).to eq({
|
50
|
+
'color' => 'black',
|
51
|
+
'border' => 'none'})
|
52
|
+
expect(style.flattened).not_to eq({
|
53
|
+
' color' => ' black',
|
54
|
+
'border ' => 'none'})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "Internal Helpers" do
|
59
|
+
it "can push styles hash back into sting" do
|
60
|
+
hash = { 'font-size' => '14px', 'font-family' => 'Helvetica' }
|
61
|
+
style = Antwort::Style.new
|
62
|
+
expect(style.send(:hash_to_str, hash)).to eq('font-size:14px;font-family:Helvetica')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Dummy
|
4
|
+
end
|
5
|
+
|
6
|
+
describe Antwort::CLIHelpers do
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
@helper = Dummy.new
|
10
|
+
@helper.extend(Antwort::CLIHelpers)
|
11
|
+
end
|
12
|
+
|
13
|
+
before :all do
|
14
|
+
Dir.chdir(fixtures_root)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'CLI Helpers' do
|
18
|
+
|
19
|
+
it '#built_emails' do
|
20
|
+
b = @helper.built_emails
|
21
|
+
expect(b).to include('demo-123456')
|
22
|
+
expect(b).not_to include('2-no-layout')
|
23
|
+
end
|
24
|
+
|
25
|
+
it '#available_emails - lists all available emails' do
|
26
|
+
a = @helper.available_emails
|
27
|
+
expect(a).to include('1-demo')
|
28
|
+
expect(a).to include('2-no-layout')
|
29
|
+
expect(a).to include('3-no-title')
|
30
|
+
end
|
31
|
+
|
32
|
+
it '#images_dir - returns images asset path by EMAIL_ID' do
|
33
|
+
expect(@helper.images_dir('foo')).to eq './assets/images/foo'
|
34
|
+
end
|
35
|
+
|
36
|
+
it '#count_files' do
|
37
|
+
expect(@helper.count_files './assets/images/shared').to be(2)
|
38
|
+
expect(@helper.count_files './assets/images/1-demo').to be(1)
|
39
|
+
end
|
40
|
+
|
41
|
+
it '#last_build_by_id' do
|
42
|
+
expect(@helper.last_build_by_id 'foo').to eq('foo-1')
|
43
|
+
expect(@helper.last_build_by_id 'demo').to eq('demo-123457')
|
44
|
+
expect(@helper.last_build_by_id 'demo').not_to eq('demo-123456')
|
45
|
+
expect(@helper.last_build_by_id 'demo').not_to eq('demo-bar-123')
|
46
|
+
end
|
47
|
+
|
48
|
+
it '#list_folders' do
|
49
|
+
a = @helper.list_folders '.'
|
50
|
+
expect(a).to include('assets')
|
51
|
+
expect(a).to include('build')
|
52
|
+
expect(a).to include('data')
|
53
|
+
expect(a).to include('emails')
|
54
|
+
expect(a).to include('views')
|
55
|
+
expect(a).not_to include('support')
|
56
|
+
expect(a).not_to include('lib')
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Antwort::CLI::Upload do
|
4
|
+
|
5
|
+
subject { described_class.new('newsletter') }
|
6
|
+
|
7
|
+
before :all do
|
8
|
+
Dir.chdir(fixtures_root)
|
9
|
+
end
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
allow($stdout).to receive(:write)
|
13
|
+
Fog.mock!
|
14
|
+
allow_any_instance_of(Thor::Actions).to receive(:yes?).and_return(true)
|
15
|
+
end
|
16
|
+
|
17
|
+
after(:each) { Fog.unmock! }
|
18
|
+
|
19
|
+
describe '#upload' do
|
20
|
+
before :each do
|
21
|
+
allow(Dir).to receive(:foreach)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'cleans S3 directory' do
|
25
|
+
expect(subject).to receive(:clean_directory!)
|
26
|
+
subject.upload
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#connection' do
|
31
|
+
it 'returns S3 connection' do
|
32
|
+
expect(subject.connection).to be_a(Fog::Storage::AWS::Mock)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#directory' do
|
37
|
+
it 'responds to #directory' do
|
38
|
+
expect(subject).to respond_to(:directory)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#clean_directory!' do
|
43
|
+
it 'responds to #clean_directory!' do
|
44
|
+
expect(subject).to respond_to(:clean_directory!)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/spec/cli_spec.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Antwort::CLI do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
Dir.chdir(fixtures_root)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#upload' do
|
10
|
+
let(:output) { capture(:stdout) { subject.upload('newsletter') } }
|
11
|
+
|
12
|
+
before :each do
|
13
|
+
allow($stdout).to receive(:write)
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'user confirms upload' do
|
17
|
+
before :each do
|
18
|
+
allow_any_instance_of(Thor::Actions).to receive(:yes?).and_return(true)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'uploads mail' do
|
22
|
+
expect(subject).to receive(:upload)
|
23
|
+
output
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'messages success' do
|
27
|
+
expect(output).to include('create')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'user denies upload' do
|
32
|
+
before :each do
|
33
|
+
allow_any_instance_of(Thor::Actions).to receive(:yes?).and_return(false)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'does not upload mail' do
|
37
|
+
expect(subject).not_to receive(:upload_mail)
|
38
|
+
output
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'messages cancelation' do
|
42
|
+
expect(output).to include('aborted')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
<h1>Hello Three</h1>
|
@@ -0,0 +1 @@
|
|
1
|
+
404 error, template not found
|
data/spec/server_spec.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Antwort Server' do
|
4
|
+
|
5
|
+
def app
|
6
|
+
Antwort::Server
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "Index page" do
|
10
|
+
it "is valid" do
|
11
|
+
get '/'
|
12
|
+
expect(last_response.status).to eq(200)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "lists available templates by title, includes link" do
|
16
|
+
get '/'
|
17
|
+
expect(last_response.body).to include('Email One')
|
18
|
+
expect(last_response.body).to include('Email Two')
|
19
|
+
expect(last_response.body).to include('<a href="template/1-demo">')
|
20
|
+
expect(last_response.body).to include('<a href="template/2-no-layout">')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "Template show action" do
|
25
|
+
|
26
|
+
it "shows email preview" do
|
27
|
+
get '/template/1-demo'
|
28
|
+
expect(last_response.body).to include('<h1>Hello One</h1>')
|
29
|
+
expect(last_response.body).to include('<title>Email One</title>')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "loads corresponding data yaml file" do
|
33
|
+
get '/template/1-demo'
|
34
|
+
expect(last_response.body).to include('foo')
|
35
|
+
expect(last_response.body).to include('bar')
|
36
|
+
end
|
37
|
+
|
38
|
+
it "respects layout:false metadata attribute" do
|
39
|
+
get '/template/2-no-layout'
|
40
|
+
expect(last_response.body).not_to include('<body>')
|
41
|
+
end
|
42
|
+
|
43
|
+
it "has default title if none found" do
|
44
|
+
get 'template/3-no-title'
|
45
|
+
expect(last_response.body).to include('Untitled')
|
46
|
+
end
|
47
|
+
|
48
|
+
it "shows 404 error if template not found" do
|
49
|
+
get '/template/does-not-exist'
|
50
|
+
expect(last_response.status).to eq(404)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|