diff --git a/.circleci/config.yml b/.circleci/config.yml index da28152f..98a5c19b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,7 @@ version: 2.1 orbs: + # Required for feature specs. browser-tools: circleci/browser-tools@1.1 # Always take the latest version of the orb, this allows us to @@ -23,6 +24,7 @@ jobs: lint-code: executor: solidusio_extensions/sqlite-memory steps: + - browser-tools/install-browser-tools - solidusio_extensions/lint-code workflows: @@ -31,6 +33,7 @@ workflows: - run-specs-with-postgres - run-specs-with-mysql - lint-code + "Weekly run specs against master": triggers: - schedule: diff --git a/.github_changelog_generator b/.github_changelog_generator new file mode 100644 index 00000000..eac09621 --- /dev/null +++ b/.github_changelog_generator @@ -0,0 +1,2 @@ +issues=false +exclude-labels=infrastructure diff --git a/.gitignore b/.gitignore index 0e523682..325c3c99 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ .project .sass-cache coverage -Gemfile-local Gemfile.lock tmp nbproject @@ -15,5 +14,7 @@ pkg *.swp spec/dummy spec/examples.txt -.bundle/ -.env +/sandbox +.rvmrc +.ruby-version +.ruby-gemset diff --git a/Gemfile b/Gemfile index d7d73d34..5510b62e 100644 --- a/Gemfile +++ b/Gemfile @@ -4,25 +4,20 @@ source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } branch = ENV.fetch('SOLIDUS_BRANCH', 'master') -solidus_git, solidus_frontend_git = if (branch == 'master') || (branch >= 'v3.2') - %w[solidusio/solidus solidusio/solidus_frontend] - else - %w[solidusio/solidus] * 2 - end -gem 'solidus', github: solidus_git, branch: branch -gem 'solidus_frontend', github: solidus_frontend_git, branch: branch +gem 'solidus', github: 'solidusio/solidus', branch: branch -gem 'rails', ENV.fetch('RAILS_VERSION', nil) +# The solidus_frontend gem has been pulled out since v3.2 +gem 'solidus_frontend', github: 'solidusio/solidus_frontend' if branch == 'master' +gem 'solidus_frontend' if branch >= 'v3.2' # rubocop:disable Bundler/DuplicatedGem + +# Needed to help Bundler figure out how to resolve dependencies, +# otherwise it takes forever to resolve them. +# See https://github.com/bundler/bundler/issues/6677 +gem 'rails', '>0.a' # Provides basic authentication functionality for testing parts of your engine gem 'solidus_auth_devise' -# Asset compilation speed -gem 'mini_racer' -gem 'sassc-rails', platforms: :mri - -gem 'bourbon' - case ENV.fetch('DB', nil) when 'mysql' gem 'mysql2' @@ -32,13 +27,16 @@ else gem 'sqlite3' end -group :test do - gem 'rails-controller-testing' - gem 'webdrivers' -end +# While we still support Ruby < 3 we need to workaround a limitation in +# the 'async' gem that relies on the latest ruby, since RubyGems doesn't +# resolve gems based on the required ruby version. +gem 'async', '< 3' if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3') gemspec # Use a local Gemfile to include development dependencies that might not be -# relevant for the project or for other contributors, e.g.: `gem 'pry-debug'`. -eval_gemfile 'Gemfile-local' if File.exist? 'Gemfile-local' +# relevant for the project or for other contributors, e.g. pry-byebug. +# +# We use `send` instead of calling `eval_gemfile` to work around an issue with +# how Dependabot parses projects: https://github.com/dependabot/dependabot-core/issues/1658. +send(:eval_gemfile, 'Gemfile-local') if File.exist? 'Gemfile-local' diff --git a/app/assets/stylesheets/spree/backend/solidus_paypal_braintree.css b/app/assets/stylesheets/spree/backend/solidus_paypal_braintree.css new file mode 100644 index 00000000..e3c23662 --- /dev/null +++ b/app/assets/stylesheets/spree/backend/solidus_paypal_braintree.css @@ -0,0 +1,4 @@ +/* +Placeholder manifest file. +the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/backend/all.css' +*/ diff --git a/app/models/solidus_paypal_braintree/gateway.rb b/app/models/solidus_paypal_braintree/gateway.rb index a0a87d10..d00fd330 100644 --- a/app/models/solidus_paypal_braintree/gateway.rb +++ b/app/models/solidus_paypal_braintree/gateway.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'braintree' +require 'solidus_paypal_braintree/request_protection' module SolidusPaypalBraintree class Gateway < ::Spree::PaymentMethod diff --git a/app/models/solidus_paypal_braintree/source.rb b/app/models/solidus_paypal_braintree/source.rb index be72d405..6bff8638 100644 --- a/app/models/solidus_paypal_braintree/source.rb +++ b/app/models/solidus_paypal_braintree/source.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true +require 'solidus_paypal_braintree/request_protection' + module SolidusPaypalBraintree - class Source < SolidusSupport.payment_source_parent_class + class Source < ::Spree::PaymentSource include RequestProtection PAYPAL = "PayPalAccount" diff --git a/app/models/solidus_paypal_braintree/transaction_address.rb b/app/models/solidus_paypal_braintree/transaction_address.rb index fa8931e1..d2b860a7 100644 --- a/app/models/solidus_paypal_braintree/transaction_address.rb +++ b/app/models/solidus_paypal_braintree/transaction_address.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'active_model' +require 'solidus_paypal_braintree/country_mapper' module SolidusPaypalBraintree class TransactionAddress diff --git a/bin/rails b/bin/rails index 619178a0..6dbbbc36 100755 --- a/bin/rails +++ b/bin/rails @@ -1,15 +1,7 @@ #!/usr/bin/env ruby -# frozen_string_literal: true - -app_root = 'spec/dummy' - -unless File.exist? "#{app_root}/bin/rails" - system "bin/rake", app_root or begin - warn "Automatic creation of the dummy app failed" - exit 1 - end +if %w[g generate].include? ARGV.first + exec "#{__dir__}/rails-engine", *ARGV +else + exec "#{__dir__}/rails-sandbox", *ARGV end - -Dir.chdir app_root -exec 'bin/rails', *ARGV diff --git a/bin/rails-engine b/bin/rails-engine new file mode 100755 index 00000000..7fd48f3c --- /dev/null +++ b/bin/rails-engine @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby +# This command will automatically be run when you run "rails" with Rails gems +# installed from the root of your application. + +ENGINE_ROOT = File.expand_path('..', __dir__) +ENGINE_PATH = File.expand_path('../lib/solidus_paypal_braintree/engine', __dir__) + +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) +require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) + +require 'rails/all' +require 'rails/engine/commands' diff --git a/bin/rails-sandbox b/bin/rails-sandbox new file mode 100755 index 00000000..ad2df04d --- /dev/null +++ b/bin/rails-sandbox @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby + +app_root = 'sandbox' + +unless File.exist? "#{app_root}/bin/rails" + warn 'Creating the sandbox app...' + Dir.chdir "#{__dir__}/.." do + system "#{__dir__}/sandbox" or begin + warn 'Automatic creation of the sandbox app failed' + exit 1 + end + end +end + +Dir.chdir app_root +exec 'bin/rails', *ARGV diff --git a/bin/rake b/bin/rake new file mode 100755 index 00000000..1e6eacd3 --- /dev/null +++ b/bin/rake @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rake", "rake") diff --git a/bin/sandbox b/bin/sandbox new file mode 100755 index 00000000..6ddce5d2 --- /dev/null +++ b/bin/sandbox @@ -0,0 +1,102 @@ +#!/usr/bin/env bash + +set -e +if [ ! -z $DEBUG ] +then + set -x +fi + +case "$DB" in +postgres|postgresql) + RAILSDB="postgresql" + ;; +mysql) + RAILSDB="mysql" + ;; +sqlite3|sqlite) + RAILSDB="sqlite3" + ;; +'') + echo "~~> Use 'export DB=[postgres|mysql|sqlite]' to control the DB adapter" + RAILSDB="sqlite3" + ;; +*) + echo "Invalid value specified for the Solidus sandbox: DB=\"$DB\"." + echo "Please use 'postgres', 'mysql', or 'sqlite' instead." + exit 1 + ;; +esac +echo "~~> Using $RAILSDB as the database engine" + +if [ -n $SOLIDUS_BRANCH ] +then + BRANCH=$SOLIDUS_BRANCH +else + echo "~~> Use 'export SOLIDUS_BRANCH=[master|v3.2|...]' to control the Solidus branch" + BRANCH="master" +fi +echo "~~> Using branch $BRANCH of solidus" + +if [ -z $SOLIDUS_FRONTEND ] +then + echo "~~> Use 'export SOLIDUS_FRONTEND=[solidus_frontend|solidus_starter_frontend]' to control the Solidus frontend" + SOLIDUS_FRONTEND="solidus_frontend" +fi +echo "~~> Using branch $SOLIDUS_FRONTEND as the solidus frontend" + +extension_name="solidus_paypal_braintree" + +# Stay away from the bundler env of the containing extension. +function unbundled { + ruby -rbundler -e'b = proc {system *ARGV}; Bundler.respond_to?(:with_unbundled_env) ? Bundler.with_unbundled_env(&b) : Bundler.with_clean_env(&b)' -- $@ +} + +rm -rf ./sandbox +unbundled bundle exec rails new sandbox --database="$RAILSDB" \ + --skip-bundle \ + --skip-git \ + --skip-keeps \ + --skip-rc \ + --skip-spring \ + --skip-test \ + --skip-javascript + +if [ ! -d "sandbox" ]; then + echo 'sandbox rails application failed' + exit 1 +fi + +cd ./sandbox +cat <> Gemfile +gem 'solidus', github: 'solidusio/solidus', branch: '$BRANCH' +gem 'rails-i18n' +gem 'solidus_i18n' + +gem '$extension_name', path: '..' + +group :test, :development do + platforms :mri do + gem 'pry-byebug' + end +end +RUBY + +unbundled bundle install --gemfile Gemfile + +unbundled bundle exec rake db:drop db:create + +unbundled bundle exec rails generate solidus:install \ + --auto-accept \ + --user_class=Spree::User \ + --enforce_available_locales=true \ + --with-authentication=true \ + --payment-method=none \ + --frontend=${SOLIDUS_FRONTEND} \ + $@ + +unbundled bundle exec rails generate solidus:auth:install --auto-run-migrations +unbundled bundle exec rails generate ${extension_name}:install --auto-run-migrations + +echo +echo "๐Ÿš€ Sandbox app successfully created for $extension_name!" +echo "๐Ÿงช This app is intended for test purposes." diff --git a/bin/setup b/bin/setup index 40d7811d..67d91932 100755 --- a/bin/setup +++ b/bin/setup @@ -5,4 +5,4 @@ set -vx gem install bundler --conservative bundle update -bundle exec rake clobber +bin/rake clobber diff --git a/lib/generators/solidus_paypal_braintree/install/install_generator.rb b/lib/generators/solidus_paypal_braintree/install/install_generator.rb index b047f194..26db5553 100644 --- a/lib/generators/solidus_paypal_braintree/install/install_generator.rb +++ b/lib/generators/solidus_paypal_braintree/install/install_generator.rb @@ -4,9 +4,14 @@ module SolidusPaypalBraintree module Generators class InstallGenerator < Rails::Generators::Base class_option :auto_run_migrations, type: :boolean, default: false + source_root File.expand_path('templates', __dir__) + + def copy_initializer + template 'initializer.rb', 'config/initializers/solidus_paypal_braintree.rb' + end def add_javascripts - append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require solidus_paypal_braintree/frontend\n" + append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/solidus_paypal_braintree\n" # rubocop:disable Layout/LineLength append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/solidus_paypal_braintree\n" # rubocop:disable Layout/LineLength end @@ -16,7 +21,7 @@ def add_stylesheets end def add_migrations - run 'bundle exec rake railties:install:migrations FROM=solidus_paypal_braintree' + run 'bin/rails railties:install:migrations FROM=solidus_paypal_braintree' end def mount_engine @@ -28,9 +33,9 @@ def mount_engine def run_migrations run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]')) # rubocop:disable Layout/LineLength if run_migrations - run 'bundle exec rake db:migrate' + run 'bin/rails db:migrate' else - say_status :skipping, 'rake db:migrate, don\'t forget to run it!' + puts 'Skipping bin/rails db:migrate, don\'t forget to run it!' # rubocop:disable Rails/Output end end end diff --git a/lib/generators/solidus_paypal_braintree/install/templates/initializer.rb b/lib/generators/solidus_paypal_braintree/install/templates/initializer.rb new file mode 100644 index 00000000..896e05fe --- /dev/null +++ b/lib/generators/solidus_paypal_braintree/install/templates/initializer.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +SolidusPaypalBraintree.configure do |config| + # TODO: Remember to change this with the actual preferences you have implemented! + # config.sample_preference = 'sample_value' +end diff --git a/lib/solidus_paypal_braintree.rb b/lib/solidus_paypal_braintree.rb index 55496b18..11c83f9b 100644 --- a/lib/solidus_paypal_braintree.rb +++ b/lib/solidus_paypal_braintree.rb @@ -1,11 +1,13 @@ # frozen_string_literal: true require 'solidus_core' -require 'solidus_paypal_braintree/version' -require 'solidus_paypal_braintree/engine' +require 'solidus_support' + require 'solidus_paypal_braintree/country_mapper' require 'solidus_paypal_braintree/request_protection' -require 'solidus_support' +require 'solidus_paypal_braintree/extension_configuration' +require 'solidus_paypal_braintree/version' +require 'solidus_paypal_braintree/engine' module SolidusPaypalBraintree def self.table_name_prefix diff --git a/lib/solidus_paypal_braintree/engine.rb b/lib/solidus_paypal_braintree/engine.rb index df19ba25..c4fbf34c 100644 --- a/lib/solidus_paypal_braintree/engine.rb +++ b/lib/solidus_paypal_braintree/engine.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'solidus_core' require 'solidus_support' module SolidusPaypalBraintree @@ -13,11 +14,6 @@ class Engine < Rails::Engine inflect.acronym 'AVS' end - # use rspec for tests - config.generators do |g| - g.test_framework :rspec - end - initializer "register_solidus_paypal_braintree_gateway", after: "spree.register.payment_methods" do |app| config.to_prepare do app.config.spree.payment_methods << SolidusPaypalBraintree::Gateway @@ -64,5 +60,10 @@ class Engine < Rails::Engine end end end + + # use rspec for tests + config.generators do |g| + g.test_framework :rspec + end end end diff --git a/lib/solidus_paypal_braintree/extension_configuration.rb b/lib/solidus_paypal_braintree/extension_configuration.rb new file mode 100644 index 00000000..b56ab09f --- /dev/null +++ b/lib/solidus_paypal_braintree/extension_configuration.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module SolidusPaypalBraintree + class ExtensionConfiguration + # Define here the settings for this extension, e.g.: + # + # attr_accessor :my_setting + end + + class << self + def configuration + @configuration ||= ExtensionConfiguration.new + end + + alias config configuration + + def configure + yield configuration + end + end +end diff --git a/lib/solidus_paypal_braintree/request_protection.rb b/lib/solidus_paypal_braintree/request_protection.rb index 5c56d889..9661d7f3 100644 --- a/lib/solidus_paypal_braintree/request_protection.rb +++ b/lib/solidus_paypal_braintree/request_protection.rb @@ -6,7 +6,7 @@ module SolidusPaypalBraintree module RequestProtection include ActiveMerchant::NetworkConnectionRetries - def protected_request + def protected_request(&block) raise ArgumentError unless block_given? options = { @@ -15,7 +15,7 @@ def protected_request }, logger: Rails.logger } - retry_exceptions(options) { yield } + retry_exceptions(options, &block) end end end diff --git a/lib/solidus_paypal_braintree/factories.rb b/lib/solidus_paypal_braintree/testing_support/factories.rb similarity index 100% rename from lib/solidus_paypal_braintree/factories.rb rename to lib/solidus_paypal_braintree/testing_support/factories.rb diff --git a/solidus_paypal_braintree.gemspec b/solidus_paypal_braintree.gemspec index 23693cd3..505236b8 100644 --- a/solidus_paypal_braintree.gemspec +++ b/solidus_paypal_braintree.gemspec @@ -3,40 +3,40 @@ $:.push File.expand_path('lib', __dir__) require 'solidus_paypal_braintree/version' -Gem::Specification.new do |s| - s.name = 'solidus_paypal_braintree' - s.version = SolidusPaypalBraintree::VERSION - s.summary = 'Officially supported Paypal/Braintree extension' - s.description = 'Uses the javascript API for seamless braintree payments' - s.license = 'BSD-3-Clause' +Gem::Specification.new do |spec| + spec.name = 'solidus_paypal_braintree' + spec.version = SolidusPaypalBraintree::VERSION + spec.summary = 'Officially supported Paypal/Braintree extension' + spec.description = 'Uses the javascript API for seamless braintree payments' + spec.license = 'BSD-3-Clause' - s.author = 'Stembolt' - s.email = 'braintree+gemfile@stembolt.com' - s.homepage = 'https://github.com/solidusio/solidus_paypal_braintree' + spec.author = 'Stembolt' + spec.email = 'braintree+gemfile@stembolt.com' + spec.homepage = 'https://github.com/solidusio/solidus_paypal_braintree' - s.required_ruby_version = '>= 2.5' + spec.required_ruby_version = '>= 2.5' - s.files = Dir.chdir(File.expand_path(__dir__)) do + spec.files = Dir.chdir(File.expand_path(__dir__)) do `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end - s.bindir = "exe" - s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) } - s.require_paths = ["lib"] - - if s.respond_to?(:metadata) - s.metadata["homepage_uri"] = s.homepage if s.homepage - s.metadata["source_code_uri"] = s.homepage if s.homepage - s.metadata["rubygems_mfa_required"] = 'true' + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + if spec.respond_to?(:metadata) + spec.metadata["homepage_uri"] = spec.homepage if spec.homepage + spec.metadata["source_code_uri"] = spec.homepage if spec.homepage + spec.metadata["rubygems_mfa_required"] = 'true' end - s.add_dependency 'activemerchant', '~> 1.48' - s.add_dependency 'braintree', '~> 3.4' - s.add_dependency 'solidus_api', ['>= 2.0.0', '< 4'] - s.add_dependency 'solidus_core', ['>= 2.0.0', '< 4'] - s.add_dependency 'solidus_support', ['>= 0.8.1', '< 1'] + spec.add_dependency 'activemerchant', '~> 1.48' + spec.add_dependency 'braintree', '~> 3.4' + spec.add_dependency 'solidus_api', ['>= 2.0.0', '< 4'] + spec.add_dependency 'solidus_core', ['>= 2.0.0', '< 4'] + spec.add_dependency 'solidus_support', ['>= 0.8.1', '< 1'] - s.add_development_dependency 'selenium-webdriver' - s.add_development_dependency 'solidus_dev_support' - s.add_development_dependency 'vcr' - s.add_development_dependency 'webmock' + spec.add_development_dependency 'rails-controller-testing' + spec.add_development_dependency 'solidus_dev_support', '~> 2.5' + spec.add_development_dependency 'vcr' + spec.add_development_dependency 'webmock' end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8d4e15f3..ebc0a7b2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,29 +1,32 @@ # frozen_string_literal: true # Configure Rails Environment -ENV['RAILS_ENV'] ||= 'test' +ENV['RAILS_ENV'] = 'test' # Run Coverage report require 'solidus_dev_support/rspec/coverage' - -require File.expand_path('dummy/config/environment.rb', __dir__) - require 'rails-controller-testing' -Rails::Controller::Testing.install + +# Create the dummy app if it's still missing. +dummy_env = "#{__dir__}/dummy/config/environment.rb" +system 'bin/rake extension:test_app' unless File.exist? dummy_env +require dummy_env # Requires factories and other useful helpers defined in spree_core. require 'solidus_dev_support/rspec/feature_helper' # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. -Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].sort.each { |f| require f } +Dir["#{__dir__}/support/**/*.rb"].sort.each { |f| require f } -# Requires factories defined in lib/solidus_paypal_braintree/factories.rb -require 'solidus_paypal_braintree/factories' +# Requires factories defined in lib/solidus_paypal_braintree/testing_support/factories.rb +SolidusDevSupport::TestingSupport::Factories.load_for(SolidusPaypalBraintree::Engine) RSpec.configure do |config| config.infer_spec_type_from_file_location! config.use_transactional_fixtures = false - config.raise_errors_for_deprecations! - config.example_status_persistence_file_path = "./spec/examples.txt" + + if Spree.solidus_gem_version < Gem::Version.new('2.11') + config.extend Spree::TestingSupport::AuthorizationHelpers::Request, type: :system + end end diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index e57d6c79..e62d7bef 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -4,4 +4,4 @@ end end -Capybara.javascript_driver = ENV.fetch('CAPYBARA_DRIVER', :selenium_chrome).to_sym +Capybara.javascript_driver = (ENV['CAPYBARA_DRIVER'] || :selenium_chrome_headless).to_sym diff --git a/spec/support/factories.rb b/spec/support/factories.rb deleted file mode 100644 index 97ef967d..00000000 --- a/spec/support/factories.rb +++ /dev/null @@ -1,2 +0,0 @@ -# Requires factories defined in lib/solidus_paypal_braintree/factories.rb -require 'solidus_paypal_braintree/factories'