├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .gitmodules ├── .simplecov ├── CHANGES.md ├── Gemfile ├── MIT-LICENSE ├── README.md ├── Rakefile ├── app ├── assets │ ├── images │ │ └── rails_email_preview │ │ │ └── favicon.png │ └── stylesheets │ │ └── rails_email_preview │ │ ├── application.css │ │ └── bootstrap3.css ├── controllers │ └── rails_email_preview │ │ ├── application_controller.rb │ │ └── emails_controller.rb ├── helpers │ └── rails_email_preview │ │ └── emails_helper.rb ├── models │ └── rails_email_preview │ │ └── preview.rb ├── presenters │ └── rails_email_preview │ │ └── preview_list_presenter.rb └── views │ ├── integrations │ └── cms │ │ ├── _customize_cms_for_rails_email_preview.html.erb │ │ ├── comfy_v1_integration.js │ │ └── comfy_v2_integration.js │ ├── layouts │ └── rails_email_preview │ │ ├── _flash_notices.html.erb │ │ ├── application.html.erb │ │ └── email.html.erb │ └── rails_email_preview │ └── emails │ ├── _email_iframe.html.erb │ ├── _format_nav.html.erb │ ├── _headers.html.erb │ ├── _headers_and_nav.html.erb │ ├── _i18n_nav.html.erb │ ├── _nav.html.erb │ ├── _send_form.html.erb │ ├── email_iframe.js │ ├── index.html.erb │ └── show.html.erb ├── config ├── i18n-tasks.yml ├── initializers │ └── rails_email_preview.rb ├── locales │ ├── de.yml │ ├── en.yml │ ├── es.yml │ └── ru.yml └── routes.rb ├── doc └── img │ ├── rep-edit-sofa.png │ ├── rep-nav.png │ ├── rep-show-default.png │ └── rep-show.png ├── lib ├── generators │ └── rails_email_preview │ │ ├── install_generator.rb │ │ └── update_previews_generator.rb ├── rails_email_preview.rb └── rails_email_preview │ ├── delivery_handler.rb │ ├── engine.rb │ ├── integrations │ └── comfortable_mexica_sofa.rb │ ├── main_app_route_delegator.rb │ ├── version.rb │ └── view_hooks.rb ├── rails_email_preview.gemspec ├── shared.gemfile └── spec ├── dummy ├── README.rdoc ├── Rakefile ├── app │ ├── assets │ │ ├── config │ │ │ └── manifest.js │ │ ├── images │ │ │ ├── .keep │ │ │ └── cat.png │ │ ├── javascripts │ │ │ └── application.js │ │ └── stylesheets │ │ │ └── application.css │ ├── controllers │ │ ├── admin_controller.rb │ │ ├── application_controller.rb │ │ └── concerns │ │ │ └── .keep │ ├── helpers │ │ └── application_helper.rb │ ├── mailer_previews │ │ ├── auth_mailer_preview.rb │ │ └── newsletter_mailer_preview.rb │ ├── mailers │ │ ├── .keep │ │ ├── application_mailer.rb │ │ ├── auth_mailer.rb │ │ └── newsletter_mailer.rb │ ├── models │ │ ├── .keep │ │ └── concerns │ │ │ └── .keep │ └── views │ │ ├── auth_mailer │ │ ├── email_confirmation.html.erb │ │ └── password_reset.html.erb │ │ ├── layouts │ │ └── admin.html.erb │ │ ├── newsletter_mailer │ │ ├── monthly_newsletter.html.erb │ │ └── weekly_newsletter.html.erb │ │ └── rails_email_preview │ │ └── _my_hook.html.erb ├── bin │ ├── bundle │ ├── rails │ └── rake ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── backtrace_silencers.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ ├── mime_types.rb │ │ ├── rails_email_preview.rb │ │ ├── secret_token.rb │ │ ├── session_store.rb │ │ └── wrap_parameters.rb │ ├── locales │ │ ├── de.yml │ │ ├── en.yml │ │ └── es.yml │ └── routes.rb ├── lib │ └── assets │ │ └── .keep ├── log │ └── .keep └── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ └── favicon.ico ├── features ├── email_show_spec.rb ├── email_test_send_spec.rb ├── emails_list_spec.rb └── take_screenshots_spec.rb ├── gemfiles ├── i18n-tasks.gemfile ├── rails_6_1.gemfile ├── rails_7_0.gemfile └── rails_7_1.gemfile ├── preview_list_presenter_spec.rb ├── spec_helper.rb ├── support ├── save_screenshots.rb └── with_layout.rb └── update_previews_generator_spec.rb /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ main ] 4 | pull_request: 5 | types: [ opened, synchronize ] 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | include: 12 | - ruby_version: '3.3' 13 | gemfile: rails_7_1 14 | upload_coverage: true 15 | - ruby_version: '3.2' 16 | gemfile: rails_7_1 17 | # - ruby_version: '3.1' 18 | # gemfile: rails_7_1 19 | - ruby_version: '3.3' 20 | gemfile: rails_7_0 21 | - ruby_version: '3.3' 22 | gemfile: rails_6_1 23 | env: 24 | CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} 25 | BUNDLE_GEMFILE: ${{ github.workspace }}/spec/gemfiles/${{ matrix.gemfile }}.gemfile 26 | steps: 27 | - name: "Determine whether to upload coverage" 28 | if: ${{ env.CC_TEST_REPORTER_ID && matrix.upload_coverage }} 29 | run: echo COVERAGE=1 >> $GITHUB_ENV 30 | - uses: actions/checkout@v4 31 | - name: Set up Ruby ${{ matrix.ruby_version }} and ${{ matrix.gemfile }}.gemfile 32 | uses: ruby/setup-ruby@v1 33 | with: 34 | ruby-version: ${{ matrix.ruby_version }} 35 | bundler: ${{ matrix.bundler || 'Gemfile.lock' }} 36 | bundler-cache: true 37 | cache-version: 1000 38 | - name: Run tests 39 | if: ${{ !env.COVERAGE }} 40 | run: bundle exec rspec --format d 41 | - name: Run tests and upload coverage 42 | uses: paambaati/codeclimate-action@v3.0.0 43 | if: ${{ env.COVERAGE }} 44 | with: 45 | coverageCommand: bundle exec rspec --format d 46 | i18n-tasks: 47 | runs-on: ubuntu-latest 48 | env: 49 | BUNDLE_GEMFILE: ${{ github.workspace }}/spec/gemfiles/i18n-tasks.gemfile 50 | steps: 51 | - uses: actions/checkout@v4 52 | - name: Set up Ruby and i18n-tasks.gemfile 53 | uses: ruby/setup-ruby@v1 54 | with: 55 | ruby-version: 3.3 56 | bundler-cache: true 57 | - name: Run i18n-tasks 58 | run: bundle exec i18n-tasks health 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.lock 2 | *.gem 3 | .idea/ 4 | .rvmrc 5 | .bundle/ 6 | .ruby-version 7 | .ruby-gemset 8 | log/*.log 9 | pkg/ 10 | spec/dummy/db/*.sqlite3 11 | spec/dummy/log/*.log 12 | spec/dummy/tmp/ 13 | coverage/ 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "spec/screenshots"] 2 | path = spec/screenshots 3 | url = https://github.com/glebm/rep_spec_screenshots.git 4 | -------------------------------------------------------------------------------- /.simplecov: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | SimpleCov.start do 3 | add_filter '/spec/' 4 | add_group 'Commands', 'app/commands' 5 | add_group 'Controllers', 'app/controllers' 6 | add_group 'Forms', 'app/forms' 7 | add_group 'Helpers', 'app/helpers' 8 | add_group 'Jobs', 'app/jobs' 9 | add_group 'Mailers', %w(app/mailers app/mailer_previews) 10 | add_group 'Models', 'app/models' 11 | add_group 'Policies', 'app/policies' 12 | add_group 'View models', 'app/view_models' 13 | add_group 'Lib', 'lib/' 14 | formatter SimpleCov::Formatter::HTMLFormatter unless ENV['CI'] 15 | end 16 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | * Remove explicit dependency on `sassc-rails`. Allow the use of this gem with either: 2 | `dartsass-sprockets`, `sassc-rails`, `dartsass-rails`, or `cssbundling-rails` 3 | * Drop support for EOL ruby and rails versions (rails >6.1, ruby >3.1) 4 | 5 | ## v2.2.3 6 | 7 | * Fixes Rails 7 compatibility. 8 | [#88](https://github.com/glebm/rails_email_preview/pull/88) 9 | [#90](https://github.com/glebm/rails_email_preview/issues/90) 10 | 11 | ## v2.2.2 12 | 13 | 1. Fixes deprecation warnings on Rails 6. 14 | 2. Fixes unintentional processing of email HTML as ERB. 15 | RCE vulnerability if preview body contains user input. 16 | [#82](https://github.com/glebm/rails_email_preview/issues/82) 17 | 18 | ## v2.2.1 19 | 20 | Fixes support for Rails <5.2 (regression introduced in v2.2.0). 21 | 22 | ## v2.2.0 23 | 24 | Adds CSP nonce to inline script tags if CSP is enabled on Rails v5.2+. 25 | 26 | ## v2.1.0 27 | 28 | Use `sassc-rails` instead of `sass-rails`. 29 | 30 | ## v2.0.6 31 | 32 | CMS integration now supports Comfy v2. 33 | 34 | ## v2.0.4 35 | 36 | Depend on `sass` instead of `sass-rails`. 37 | 38 | ## v2.0.3 39 | 40 | Fix a URL generation issue in the CMS integration on Rails 5. 41 | 42 | ## v2.0.2 43 | 44 | * Document roadie-rails support. 45 | * Fix body iframe height calculation. 46 | 47 | ## v2.0.1 48 | 49 | Drop support for all versions of Rails below 4.2. 50 | Fix Rails 5 deprecation warnings. 51 | 52 | ## v1.0.3 53 | 54 | Rails 5 support. 55 | 56 | ## v1.0.2 57 | 58 | Added a couple of variables for further default theme customization. 59 | 60 | ## v1.0.1 61 | 62 | Added `RailsEmailPreview.find_preview_classes(dir)` that also finds classes in subdirectories, and changed the default 63 | initializer to load classes like this: 64 | 65 | ```ruby 66 | RailsEmailPreview.preview_classes = RailsEmailPreview.find_preview_classes('app/mailer_previews') 67 | ``` 68 | 69 | ## v1.0.0 70 | 71 | **Breaking**: REP now uses a lightweight default theme with no dependencies by default. 72 | 73 | If you are using REP with the Bootstrap 3 theme, here are the configuration changes you need to make: 74 | 75 | * `@import "rails_email_preview/bootstrap3"` instead of `rails_email_preview/application`. 76 | * Add the following styles configuration to your REP initializer: 77 | 78 | ```ruby 79 | config.style.merge!( 80 | btn_active_class_modifier: 'active', 81 | btn_danger_class: 'btn btn-danger', 82 | btn_default_class: 'btn btn-default', 83 | btn_group_class: 'btn-group btn-group-sm', 84 | btn_primary_class: 'btn btn-primary', 85 | form_control_class: 'form-control', 86 | list_group_class: 'list-group', 87 | list_group_item_class: 'list-group-item', 88 | row_class: 'row', 89 | ) 90 | ``` 91 | 92 | The following REP internal class names have changed: 93 | 94 | * `.rep-email-options` is now `.rep--email-options`. 95 | * `.rep-headers-list` is now `.rep--headers-list`. 96 | * `.rep-email-show` is now `.rep--email-show`. 97 | * `.breadcrumb` is now `.rep--breadcrumbs`. 98 | * `.breadcrumb .active` is now `.rep--breadcrumbs__active`. 99 | * `.rep-send-to-wrapper` is gone, but now there is `.rep--send-to-form`. 100 | 101 | All REP views are now wrapped in a `div` with the `rep--main-container` class. 102 | 103 | REP no longer depends on slim and slim-rails. 104 | 105 | Fixed minor email locale handling bugs in navigation and the CMS integration. 106 | 107 | ## v0.2.31 108 | 109 | * Compatibility with namespaced email classes in the CMS. 110 | 111 | ## v0.2.30 112 | 113 | * Compatibility with namespaced email classes. 114 | * Change Sass stylesheets extensions from `.sass` to `.css.sass`. [#61](https://github.com/glebm/rails_email_preview/issues/61). 115 | * Spanish translation. Thanks, @epergo! 116 | 117 | ## v0.2.29 118 | 119 | * Latest CMS compatibility 120 | * Rails 4.2: avoid deprecation warnings 121 | 122 | ## v0.2.28 123 | 124 | * CMS beta compatibility 125 | 126 | ## v0.2.27 127 | 128 | * Improve CMS compatibility 129 | * New hook: breadcrumb 130 | 131 | ## v0.2.26 132 | 133 | * Fix an issue with preview list [#47](https://github.com/glebm/rails_email_preview/issues/47). 134 | * Fix a number of minor issues. 135 | 136 | ## v0.2.25 137 | 138 | * Show attachment headers in the link's hover text (HTML title). 139 | * Faster loading via `DOMContentLoaded` on the iframe as opposed to `load`. 140 | 141 | ## v0.2.24 142 | 143 | * Fix regression: Rails 3 support. 144 | 145 | ## v0.2.23 146 | 147 | * **View hooks** to inject or replace UI selectively. 148 | * Fix regression in attachments caused by having a controller action named `headers` (name conflict). 149 | 150 | ## v0.2.22 151 | 152 | * **Preview params** set from URL query. Thank you, @OlgaGr! 153 | * Routes now include locale and part type as segments (with defaults). 154 | * Faster loading using **srcdoc** iframe attribute; new progress bar. 155 | * New language: Russian. 156 | * Minor bugfixes. 157 | 158 | ## v0.2.21 159 | 160 | * **Attachments**. Thanks, @rzane! 161 | * CMS: 1.12 compatibility, better error messages. 162 | 163 | ## v0.2.20 164 | 165 | * REP will fall back to :en if its set locale is not in the list of available locales 166 | 167 | ## v0.2.19 168 | 169 | * Fixes for CMS integration 170 | 171 | ## v0.2.18 172 | 173 | * UI language is now set to :en by default, to avoid #32 174 | * Rails 3 compatibility issues fixed 175 | 176 | ## v0.2.17 177 | 178 | * Fix preview generator 179 | 180 | ## v0.2.15 .. v0.2.16 181 | 182 | * minor bugfixes 183 | * UI improvements 184 | 185 | ## v0.2.13 .. v0.2.14 186 | 187 | * clean up dependencies 188 | * squell compatibility 189 | 190 | ## v0.2.11 .. v0.2.12 191 | 192 | * german translation thanks to @baschtl 193 | * email iframe resizes on window resize 194 | * bugfixes 195 | 196 | ## v0.2.10 197 | 198 | * simplified setting custom layout with `layout=` 199 | * bugfixes 200 | 201 | ## v0.2.9 202 | 203 | * updated bootstrap, turbolinks 204 | * internal: tests + screenshots in spec/screenshots/ after each test run 205 | 206 | ## v0.2.8 207 | 208 | bugs fixed, looks improved 209 | 210 | ## v0.2.7 211 | 212 | * config.style to customize classes in REP views 213 | 214 | ## v0.2.4 .. 0.2.6 215 | 216 | * UI enhancements 217 | * CMS integration bug fixes 218 | * Send email bug fixes 219 | 220 | ## v0.2.3 221 | 222 | * Send Email from REP 223 | 224 | ## v0.2.0 225 | 226 | * inline_main_app_routes! (enables easy layout switching) 227 | * parent_controller (enables easy authorization integration) 228 | * Backwards incompatible: root_url is now rep_root_url, internal routes are prefixed too. 229 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | gem 'rails' 6 | gem 'i18n-tasks' 7 | 8 | eval_gemfile './shared.gemfile' 9 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012-2013 Gleb Mazovetskiy 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rails Email Preview [![Build Status][badge-ci]][ci] [![Test Coverage][coverage-badge]][coverage] [![Code Climate][codeclimate-badge]][codeclimate] [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/glebm/rails_email_preview?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 2 | 3 | Preview email in the browser with this Rails engine. Compatible with Rails 6.1+. 4 | 5 | An email review: 6 | 7 | ![screenshot][rep-show-screenshot] 8 | 9 | The list of all email previews: 10 | 11 | ![screenshot][rep-nav-screenshot] 12 | 13 | REP comes with two themes: a simple standalone theme, and a theme that uses [Bootstrap 3][rep-show-default-screenshot]. 14 | 15 | ## Installation 16 | 17 | Add [![Gem Version][gem-badge]][gem] to Gemfile: 18 | 19 | ```ruby 20 | gem 'rails_email_preview', '~> 2.2.3' 21 | ``` 22 | 23 | Add an initializer and the routes: 24 | 25 | ```console 26 | $ rails g rails_email_preview:install 27 | ``` 28 | 29 | Generate preview classes and method stubs in app/mailer_previews/ 30 | 31 | ```console 32 | $ rails g rails_email_preview:update_previews 33 | ``` 34 | 35 | ## Usage 36 | 37 | The last generator above will add a stub for each of your emails, then you populate the stubs with mock data: 38 | 39 | ```ruby 40 | # app/mailer_previews/user_mailer_preview.rb: 41 | class UserMailerPreview 42 | # preview methods should return Mail objects, e.g.: 43 | def invitation 44 | UserMailer.invitation mock_user('Alice'), mock_user('Bob') 45 | end 46 | 47 | def welcome 48 | UserMailer.welcome mock_user 49 | end 50 | 51 | private 52 | # You can put all your mock helpers in a module 53 | # or you can use your factories / fabricators, just make sure you are not creating anything 54 | def mock_user(name = 'Bill Gates') 55 | fake_id User.new(name: name, email: "user#{rand 100}@test.com") 56 | end 57 | 58 | def fake_id(obj) 59 | # overrides the method on just this object 60 | obj.define_singleton_method(:id) { 123 + rand(100) } 61 | obj 62 | end 63 | end 64 | ``` 65 | 66 | ### Parameters as instance variables 67 | 68 | All parameters in the search query will be available to the preview class as instance variables. 69 | For example, if URL to mailer preview looks like: 70 | 71 | /emails/user_mailer_preview-welcome?**user_id=1** 72 | 73 | The method `welcome` in `UserMailerPreview` have a `@user_id` instance variable defined: 74 | 75 | ```ruby 76 | class UserMailerPreview 77 | def welcome 78 | user = @user_id ? User.find(@user_id) : mock_user 79 | UserMailer.welcome(user) 80 | end 81 | end 82 | ``` 83 | 84 | Now you can preview or send the welcome email to a specific user. 85 | 86 | ### Routing 87 | 88 | You can access REP urls like this: 89 | 90 | ```ruby 91 | # engine root: 92 | rails_email_preview.rep_root_url 93 | # list of emails (same as root): 94 | rails_email_preview.rep_emails_url 95 | # email show: 96 | rails_email_preview.rep_email_url('user_mailer-welcome') 97 | ``` 98 | 99 | ### Sending Emails 100 | 101 | You can send emails via REP. This is especially useful when testing with limited clients (Blackberry, Outlook, etc.). 102 | This will use the environment's mailer settings, but the handler will `perform_deliveries`. 103 | Uncomment this line in the initializer to disable sending test emails: 104 | 105 | ```ruby 106 | config.enable_send_email = false 107 | ``` 108 | 109 | ### Editing Emails 110 | 111 | Emails can be stored in the database and edited in the browser. 112 | REP works with [Comfortable Mexican Sofa CMS](https://github.com/comfy/comfortable-mexican-sofa) to achieve this -- see the [CMS Guide](https://github.com/glebm/rails_email_preview/wiki/Edit-Emails-with-Comfortable-Mexican-Sofa) to learn more. 113 | 114 | [![screenshot](https://raw.github.com/glebm/rails_email_preview/master/doc/img/rep-edit-sofa.png)](https://github.com/glebm/rails_email_preview/wiki/Edit-Emails-with-Comfortable-Mexican-Sofa) 115 | 116 | ### CSS inlining 117 | 118 | For CSS inlining, REP supports [Roadie](https://github.com/Mange/roadie) and 119 | [Premailer](https://github.com/alexdunae/premailer). 120 | Both of these automatically translate CSS rules into inline styles and turn 121 | relative URLs into absolute ones. 122 | 123 | Roadie additionally extracts styles that cannot be inlined into a separate 124 | ` 48 | 49 | 50 | 51 | 52 |
53 |

The page you were looking for doesn't exist.

54 |

You may have mistyped the address or the page may have moved.

55 |
56 |

If you are the application owner check the logs for more information.

57 | 58 | 59 | -------------------------------------------------------------------------------- /spec/dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

The change you wanted was rejected.

54 |

Maybe you tried to change something you didn't have access to.

55 |
56 |

If you are the application owner check the logs for more information.

57 | 58 | 59 | -------------------------------------------------------------------------------- /spec/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

We're sorry, but something went wrong.

54 |
55 |

If you are the application owner check the logs for more information.

56 | 57 | 58 | -------------------------------------------------------------------------------- /spec/dummy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glebm/rails_email_preview/95fb1478046549bdc35f431b99f2c9470714b3e3/spec/dummy/public/favicon.ico -------------------------------------------------------------------------------- /spec/features/email_show_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'email show', :type => :feature do 4 | let(:url_args) { {preview_id: 'auth_mailer_preview-email_confirmation'} } 5 | it 'shows email' do 6 | visit rails_email_preview.rep_email_path(url_args) 7 | expect(page).to have_content('Dummy Email Confirmation') 8 | expect(page).to have_content I18n.t('rails_email_preview.emails.show.breadcrumb_list', locale: :en) 9 | expect(page).to have_content 'Hook before headers_and_nav' 10 | expect(page).to have_content 'Hook after headers_content' 11 | end 12 | 13 | it 'shows email in de' do 14 | begin 15 | RailsEmailPreview.locale = :de 16 | visit rails_email_preview.rep_email_path(url_args) 17 | expect(page).to have_content('Dummy Email Confirmation') 18 | expect(page).to have_content I18n.t('rails_email_preview.emails.show.breadcrumb_list', 19 | locale: :de) 20 | ensure 21 | RailsEmailPreview.locale = nil 22 | end 23 | end 24 | 25 | it 'falls back to en on unknown locale' do 26 | begin 27 | RailsEmailPreview.locale = :fr 28 | visit rails_email_preview.rep_email_path(url_args) 29 | expect(page).to have_content 'Dummy Email Confirmation' 30 | expect(page).to have_content I18n.t('rails_email_preview.emails.show.breadcrumb_list', 31 | locale: :en) 32 | ensure 33 | RailsEmailPreview.locale = nil 34 | end 35 | end 36 | 37 | it 'shows locale links' do 38 | visit rails_email_preview.rep_email_path(url_args) 39 | %w(en es).each do |locale| 40 | rails_email_preview.rep_email_path(url_args.merge(email_locale: locale)) 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/features/email_test_send_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'email test send', :type => :feature do 4 | let(:url_args) { {preview_id: 'auth_mailer_preview-email_confirmation'} } 5 | it 'shows email' do 6 | page.driver.post rails_email_preview.rep_test_deliver_path(url_args), {recipient_email: 'test@test.com'} 7 | expect(page.driver.response.location).to eq rails_email_preview.rep_email_url(url_args) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/features/emails_list_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'emails list', :type => :feature do 4 | it 'shows emails' do 5 | visit rails_email_preview.rep_root_path 6 | [I18n.t('rails_email_preview.emails.index.list_title'), 'Auth', 'Email confirmation', 'Newsletter', 'Weekly newsletter', '4 emails in 2 mailers'].each do |text| 7 | expect(page).to have_content text 8 | end 9 | end 10 | 11 | it 'uses REP template by default' do 12 | visit rails_email_preview.rep_root_path 13 | expect(page).to have_title I18n.t('layouts.rails_email_preview.application.head_title') 14 | 15 | favicon = find 'head > link[rel=icon]', visible: :all 16 | expect(favicon[:href]).to start_with 'data:image/png;base64,' 17 | 18 | style = find 'head > style', visible: :all 19 | expect(style.text(:all)).to include '#rep-src-iframe-container' 20 | end 21 | 22 | it 'uses app template when specified' do 23 | with_layout 'admin' do 24 | visit rails_email_preview.rep_root_path 25 | expect(page).to have_title 'Dummy Admin' 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/features/take_screenshots_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | unless ENV['TRAVIS'] 3 | describe 'Take screenshots', type: :feature, js: true do 4 | it 'list page' do 5 | visit rails_email_preview.rep_root_path 6 | screenshot! 'list' 7 | end 8 | 9 | it 'list page in de' do 10 | begin 11 | RailsEmailPreview.locale = :de 12 | visit rails_email_preview.rep_root_path 13 | screenshot! 'list-de' 14 | ensure 15 | RailsEmailPreview.locale = nil 16 | end 17 | end 18 | 19 | it 'show email page' do 20 | visit rails_email_preview.rep_email_path(preview_id: 'auth_mailer_preview-email_confirmation') 21 | screenshot! 'show' 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/gemfiles/i18n-tasks.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'i18n-tasks' 4 | -------------------------------------------------------------------------------- /spec/gemfiles/rails_6_1.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec path: '../..' 4 | eval_gemfile '../../shared.gemfile' 5 | 6 | gem 'rails', '~> 6.1.7' 7 | gem 'puma', '~> 5.6.5' 8 | -------------------------------------------------------------------------------- /spec/gemfiles/rails_7_0.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec path: '../..' 4 | eval_gemfile '../../shared.gemfile' 5 | 6 | gem 'rails', '~> 7.0.4' 7 | 8 | -------------------------------------------------------------------------------- /spec/gemfiles/rails_7_1.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec path: '../..' 4 | eval_gemfile '../../shared.gemfile' 5 | 6 | gem 'rails', '~> 7.1' 7 | 8 | -------------------------------------------------------------------------------- /spec/preview_list_presenter_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'PreviewListPresenter' do 4 | Presenter = RailsEmailPreview::PreviewListPresenter 5 | Preview = RailsEmailPreview::Preview 6 | context 'columns' do 7 | it 'are balanced equally when possible' do 8 | previews = [ 9 | a = Preview.new(preview_class_name: 'A', preview_method: 'x'), 10 | b = Preview.new(preview_class_name: 'B', preview_method: 'x') 11 | ] 12 | expect(Presenter.new(previews).columns.to_a).to eq([[['A', [a]]], [['B', [b]]]]) 13 | end 14 | context 'when impossible to balance equally' do 15 | it 'the first column has more previews if possible' do 16 | previews = [ 17 | a = Preview.new(preview_class_name: 'A', preview_method: 'x'), 18 | b = Preview.new(preview_class_name: 'B', preview_method: 'x'), 19 | c = Preview.new(preview_class_name: 'C', preview_method: 'x') 20 | ] 21 | expect(Presenter.new(previews).columns.to_a).to eq([[['A', [a]], ['B', [b]]], [['C', [c]]]]) 22 | end 23 | it 'two columns even if the first column has fewer previews' do 24 | previews = [ 25 | a = Preview.new(preview_class_name: 'A', preview_method: 'x'), 26 | b1 = Preview.new(preview_class_name: 'B', preview_method: 'x'), 27 | b2 = Preview.new(preview_class_name: 'B', preview_method: 'y'), 28 | b3 = Preview.new(preview_class_name: 'B', preview_method: 'z'), 29 | b4 = Preview.new(preview_class_name: 'B', preview_method: 't') 30 | ] 31 | expect(Presenter.new(previews).columns.to_a).to eq([[['A', [a]]], [['B', [b1, b2, b3, b4]]]]) 32 | end 33 | end 34 | it 'does not fail with no previews' do 35 | expect(Presenter.new([]).columns.to_a).to eq([[], []]) 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # Configure Rails Environment 2 | ENV['RAILS_ENV'] = ENV['RACK_ENV'] = 'test' 3 | if ENV['COVERAGE'] && !%w(rbx jruby).include?(RUBY_ENGINE) && !ENV['MIGRATION_SPEC'] 4 | require 'simplecov' 5 | SimpleCov.command_name 'RSpec' 6 | end 7 | 8 | require File.expand_path('../dummy/config/environment.rb', __FILE__) 9 | 10 | require 'rspec/rails' 11 | require 'capybara/rails' 12 | require 'capybara/rspec' 13 | require 'fileutils' 14 | 15 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } 16 | 17 | require 'capybara/cuprite' 18 | 19 | browser_path = ENV['CHROMIUM_BIN'] || %w[ 20 | /usr/bin/chromium-browser 21 | /snap/bin/chromium 22 | /Applications/Chromium.app/Contents/MacOS/Chromium 23 | /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome 24 | ].find { |path| File.executable?(path) } 25 | 26 | Capybara.register_driver :cuprite do |app| 27 | options = { 28 | window_size: [800, 800], 29 | timeout: 15, 30 | } 31 | options[:browser_path] = browser_path if browser_path 32 | Capybara::Cuprite::Driver.new(app, options) 33 | end 34 | 35 | Capybara.javascript_driver = ENV['CAPYBARA_JS_DRIVER']&.to_sym || :cuprite 36 | Capybara.asset_host = ENV['CAPYBARA_ASSET_HOST'] if ENV['CAPYBARA_ASSET_HOST'] 37 | Capybara.configure do |config| 38 | config.run_server = true 39 | config.server_port = 7000 40 | config.default_max_wait_time = 10 41 | end 42 | 43 | RSpec.configure do |config| 44 | config.include SaveScreenshots 45 | config.include WithLayout 46 | 47 | config.around(:each) do |ex| 48 | Dir.chdir(Rails.root) { ex.run } 49 | end 50 | end 51 | 52 | Rails.backtrace_cleaner.remove_silencers! 53 | -------------------------------------------------------------------------------- /spec/support/save_screenshots.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | module SaveScreenshots 3 | def screenshot!(name) 4 | return unless [:selenium, :webkit, :cuprite].include?(Capybara.current_driver) 5 | dir = File.expand_path('../screenshots', File.dirname(__FILE__)) 6 | name = "#{name}.png" unless name =~ /\.png$/ 7 | FileUtils.mkpath(dir) unless File.directory?(dir) 8 | puts "saving screenshot: #{name}" 9 | save_screenshot File.join(dir, name), full: true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/support/with_layout.rb: -------------------------------------------------------------------------------- 1 | module WithLayout 2 | def with_layout(layout) 3 | RailsEmailPreview.layout = layout 4 | yield 5 | ensure 6 | RailsEmailPreview.layout = nil 7 | end 8 | end -------------------------------------------------------------------------------- /spec/update_previews_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'rails g rails_email_preview:update_previews' do 4 | context 'new mailer class' do 5 | let(:test_mailer_path) { 'app/mailers/new_mailer.rb' } 6 | let(:test_mailer_src) { <<-RUBY } 7 | class NewMailer < ApplicationMailer 8 | def notification(user) 9 | mail to: user.email 10 | end 11 | end 12 | RUBY 13 | let(:expected_preview_path) { 'app/mailer_previews/new_mailer_preview.rb' } 14 | let(:expected_preview_src) { <<-RUBY } 15 | class NewMailerPreview 16 | def notification 17 | NewMailer.notification user 18 | end 19 | end 20 | RUBY 21 | before do 22 | File.open(test_mailer_path, 'w') { |f| f.write test_mailer_src } 23 | end 24 | after do 25 | [expected_preview_path, test_mailer_path].each do |f| 26 | FileUtils.rm(f) if File.exist?(f) 27 | Object.send(:remove_const, :NewMailer) if defined?(NewMailer) 28 | Object.send(:remove_const, :NewMailerPreview) if defined?(NewMailerPreview) 29 | end 30 | end 31 | it 'creates a stub preview class' do 32 | if Rails.respond_to?(:autoloaders) && Rails.autoloaders.respond_to?(:zeitwerk_enabled?) 33 | require Rails.root.join(test_mailer_path) 34 | end 35 | Rails::Generators.invoke('rails_email_preview:update_previews', []) 36 | path = Rails.root.join(expected_preview_path) 37 | expect(File).to exist(path) 38 | expect(File.read(path)).to( 39 | eq(expected_preview_src) 40 | ) 41 | end 42 | end 43 | end 44 | --------------------------------------------------------------------------------