├── test ├── dummy │ ├── log │ │ └── .keep │ ├── lib │ │ └── assets │ │ │ └── .keep │ ├── public │ │ ├── favicon.ico │ │ ├── apple-touch-icon.png │ │ ├── apple-touch-icon-precomposed.png │ │ ├── 500.html │ │ ├── 422.html │ │ └── 404.html │ ├── app │ │ ├── assets │ │ │ ├── images │ │ │ │ └── .keep │ │ │ ├── javascripts │ │ │ │ ├── channels │ │ │ │ │ └── .keep │ │ │ │ ├── examples.js │ │ │ │ ├── cable.js │ │ │ │ └── application.js │ │ │ ├── config │ │ │ │ └── manifest.js │ │ │ └── stylesheets │ │ │ │ ├── examples.css │ │ │ │ ├── application.css │ │ │ │ └── scaffold.css │ │ ├── models │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ ├── example.rb │ │ │ └── application_record.rb │ │ ├── controllers │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ ├── application_controller.rb │ │ │ └── examples_controller.rb │ │ ├── views │ │ │ ├── layouts │ │ │ │ ├── mailer.text.erb │ │ │ │ ├── mailer.html.erb │ │ │ │ └── application.html.erb │ │ │ └── examples │ │ │ │ ├── new.html.erb │ │ │ │ ├── edit.html.erb │ │ │ │ ├── show.html.erb │ │ │ │ ├── index.html.erb │ │ │ │ └── _form.html.erb │ │ ├── helpers │ │ │ ├── examples_helper.rb │ │ │ └── application_helper.rb │ │ ├── jobs │ │ │ └── application_job.rb │ │ ├── channels │ │ │ └── application_cable │ │ │ │ ├── channel.rb │ │ │ │ └── connection.rb │ │ └── mailers │ │ │ └── application_mailer.rb │ ├── package.json │ ├── config │ │ ├── environments │ │ │ ├── system_tester.rb │ │ │ ├── test.rb │ │ │ ├── development.rb │ │ │ └── production.rb │ │ ├── routes.rb │ │ ├── spring.rb │ │ ├── environment.rb │ │ ├── initializers │ │ │ ├── mime_types.rb │ │ │ ├── application_controller_renderer.rb │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── cookies_serializer.rb │ │ │ ├── backtrace_silencers.rb │ │ │ ├── wrap_parameters.rb │ │ │ ├── cors.rb │ │ │ ├── assets.rb │ │ │ └── inflections.rb │ │ ├── cable.yml │ │ ├── boot.rb │ │ ├── application.rb │ │ ├── database.yml │ │ ├── locales │ │ │ └── en.yml │ │ ├── secrets.yml │ │ └── puma.rb │ ├── bin │ │ ├── rake │ │ ├── bundle │ │ ├── rails │ │ ├── yarn │ │ ├── update │ │ └── setup │ ├── config.ru │ ├── Rakefile │ ├── db │ │ ├── migrate │ │ │ └── 20170624013811_create_examples.rb │ │ └── schema.rb │ └── test │ │ └── test_helper.rb ├── fabricators │ └── system_tester │ │ ├── stair_step_fabricator.rb │ │ ├── scenario_step_fabricator.rb │ │ ├── stair_fabricator.rb │ │ ├── feature_fabricator.rb │ │ ├── scenario_fabricator.rb │ │ └── step_fabricator.rb ├── system_tester_test.rb ├── integration │ └── system_tester │ │ ├── status_controller_test.rb │ │ ├── step_types_controller_test.rb │ │ ├── executions_controller_test.rb │ │ ├── stair_steps_controller_test.rb │ │ ├── features_controller_test.rb │ │ ├── scenario_steps_controller_test.rb │ │ ├── scenarios_controller_test.rb │ │ └── steps_controller_test.rb ├── models │ └── system_tester │ │ └── command_handler_test.rb └── test_helper.rb ├── lib ├── system_tester │ ├── version.rb │ └── engine.rb ├── system_tester.rb └── tasks │ └── system_tester_tasks.rake ├── screenshot.png ├── app ├── jobs │ └── system_tester │ │ └── application_job.rb ├── channels │ ├── application_cable │ │ ├── channel.rb │ │ └── connection.rb │ └── system_tester │ │ └── execution_channel.rb ├── models │ ├── system_tester │ │ ├── application_record.rb │ │ ├── action.rb │ │ ├── assertion.rb │ │ ├── click_on.rb │ │ ├── assert_text.rb │ │ ├── stair_step.rb │ │ ├── scenario_step.rb │ │ ├── select.rb │ │ ├── fill_in.rb │ │ ├── assert_selector.rb │ │ ├── command_handler.rb │ │ ├── scenario.rb │ │ ├── visit.rb │ │ ├── feature.rb │ │ ├── stair.rb │ │ └── step.rb │ └── concerns │ │ └── system_tester │ │ ├── title_validatable.rb │ │ └── fileable.rb └── controllers │ └── system_tester │ ├── status_controller.rb │ ├── step_types_controller.rb │ ├── application_controller.rb │ ├── executions_controller.rb │ ├── stair_steps_controller.rb │ ├── features_controller.rb │ ├── scenario_steps_controller.rb │ ├── scenarios_controller.rb │ └── steps_controller.rb ├── .byebug_history ├── .gitignore ├── db ├── migrate │ ├── 20170513131150_create_system_tester_features.rb │ ├── 20170513213953_create_system_tester_scenarios.rb │ ├── 20170513214255_create_system_tester_steps.rb │ ├── 20170629120850_create_system_tester_stair_steps.rb │ └── 20170513214722_create_system_tester_scenario_steps.rb └── schema.rb ├── bin └── rails ├── Gemfile ├── Rakefile ├── config └── routes.rb ├── system_tester.gemspec ├── .travis.yml ├── MIT-LICENSE ├── README.md └── Gemfile.lock /test/dummy/log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/public/apple-touch-icon.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/assets/javascripts/channels/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /test/dummy/app/helpers/examples_helper.rb: -------------------------------------------------------------------------------- 1 | module ExamplesHelper 2 | end 3 | -------------------------------------------------------------------------------- /test/dummy/app/models/example.rb: -------------------------------------------------------------------------------- 1 | class Example < ApplicationRecord 2 | end 3 | -------------------------------------------------------------------------------- /test/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /lib/system_tester/version.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | VERSION = '0.3.0' 3 | end 4 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlafranchi/system_tester/HEAD/screenshot.png -------------------------------------------------------------------------------- /test/dummy/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /test/dummy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dummy", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /test/dummy/config/environments/system_tester.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | config.schema_format = :sql 3 | end -------------------------------------------------------------------------------- /app/jobs/system_tester/application_job.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class ApplicationJob < ActiveJob::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /test/dummy/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /test/fabricators/system_tester/stair_step_fabricator.rb: -------------------------------------------------------------------------------- 1 | Fabricator(:stair_step, from: 'SystemTester::StairStep') do 2 | step 3 | stair 4 | end -------------------------------------------------------------------------------- /test/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | resources :examples 3 | mount SystemTester::Engine => "/system_tester" 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /test/fabricators/system_tester/scenario_step_fabricator.rb: -------------------------------------------------------------------------------- 1 | Fabricator(:scenario_step, from: 'SystemTester::ScenarioStep') do 2 | step 3 | scenario 4 | end -------------------------------------------------------------------------------- /test/fabricators/system_tester/stair_fabricator.rb: -------------------------------------------------------------------------------- 1 | Fabricator(:stair, from: 'SystemTester::Stair') do 2 | title { Faker::Lorem.words(2).join(" ") } 3 | end -------------------------------------------------------------------------------- /test/dummy/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | 2 | //= link_tree ../images 3 | //= link_directory ../javascripts .js 4 | //= link_directory ../stylesheets .css 5 | 6 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/views/examples/new.html.erb: -------------------------------------------------------------------------------- 1 |

New Example

2 | 3 | <%= render 'form', example: @example %> 4 | 5 | <%= link_to 'Back', examples_path %> 6 | -------------------------------------------------------------------------------- /test/dummy/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /test/dummy/config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /test/fabricators/system_tester/feature_fabricator.rb: -------------------------------------------------------------------------------- 1 | Fabricator(:feature, from: 'SystemTester::Feature') do 2 | title { Faker::Lorem.words(2).join(" ") } 3 | end -------------------------------------------------------------------------------- /test/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /lib/system_tester.rb: -------------------------------------------------------------------------------- 1 | require "rack/cors" 2 | require "acts_as_list" 3 | require "system_tester/engine" 4 | 5 | module SystemTester 6 | # Your code goes here... 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../config/application', __dir__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /test/fabricators/system_tester/scenario_fabricator.rb: -------------------------------------------------------------------------------- 1 | Fabricator(:scenario, from: 'SystemTester::Scenario') do 2 | title { Faker::Lorem.words(2).join(" ") } 3 | feature 4 | end -------------------------------------------------------------------------------- /test/dummy/app/assets/stylesheets/examples.css: -------------------------------------------------------------------------------- 1 | /* 2 | Place all the styles related to the matching controller here. 3 | They will automatically be included in application.css. 4 | */ 5 | -------------------------------------------------------------------------------- /test/system_tester_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SystemTester::Test < ActiveSupport::TestCase 4 | test "truth" do 5 | assert_kind_of Module, SystemTester 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/assets/javascripts/examples.js: -------------------------------------------------------------------------------- 1 | // Place all the behaviors and hooks related to the matching controller here. 2 | // All this logic will automatically be available in application.js. 3 | -------------------------------------------------------------------------------- /test/dummy/app/views/examples/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Editing Example

2 | 3 | <%= render 'form', example: @example %> 4 | 5 | <%= link_to 'Show', @example %> | 6 | <%= link_to 'Back', examples_path %> 7 | -------------------------------------------------------------------------------- /test/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | require 'rack/cors' 4 | 5 | # Initialize the Rails application. 6 | Rails.application.initialize! 7 | -------------------------------------------------------------------------------- /app/models/system_tester/application_record.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class ApplicationRecord < ActiveRecord::Base 3 | self.abstract_class = true 4 | establish_connection :system_tester 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /.byebug_history: -------------------------------------------------------------------------------- 1 | continue 2 | file_name_was 3 | full_file 4 | file_name 5 | base_path 6 | continue 7 | test_path.join(base_path) 8 | test_path.join(base_dir) 9 | base_path 10 | bas_path 11 | test_path 12 | full_file 13 | -------------------------------------------------------------------------------- /app/channels/system_tester/execution_channel.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class ExecutionChannel < ApplicationCable::Channel 3 | def subscribed 4 | stream_from "system_tester_execution" 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | channel_prefix: dummy_production 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | log/*.log 3 | pkg/ 4 | test/dummy/db/*.sqlite3 5 | test/dummy/db/*.sqlite3-journal 6 | test/dummy/log/*.log 7 | test/dummy/tmp/ 8 | test/dummy/test/support/ 9 | test/dummy/test/system/ 10 | .idea/ 11 | coverage/ 12 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /test/fabricators/system_tester/step_fabricator.rb: -------------------------------------------------------------------------------- 1 | Fabricator(:step, from: 'SystemTester::Step') do 2 | title { Faker::Lorem.words(2).join(" ") } 3 | arg_one { Faker::Lorem.words(2).join(" ") } 4 | arg_two { Faker::Lorem.words(2).join(" ") } 5 | end -------------------------------------------------------------------------------- /test/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /test/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /db/migrate/20170513131150_create_system_tester_features.rb: -------------------------------------------------------------------------------- 1 | class CreateSystemTesterFeatures < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :system_tester_features do |t| 4 | t.string :title 5 | t.timestamps 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__) 3 | 4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 5 | $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__) 6 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20170624013811_create_examples.rb: -------------------------------------------------------------------------------- 1 | class CreateExamples < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :examples do |t| 4 | t.string :title 5 | t.text :comment 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/controllers/system_tester/status_controller.rb: -------------------------------------------------------------------------------- 1 | require 'system_tester/version' 2 | module SystemTester 3 | class StatusController < ApplicationController 4 | 5 | def index 6 | render json: {status: "OK", version: SystemTester::VERSION} 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/models/concerns/system_tester/title_validatable.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | module TitleValidatable 3 | extend ActiveSupport::Concern 4 | 5 | included do 6 | validates_presence_of :title 7 | validates_format_of :title, with: /\A[a-zA-Z\s]*\z/ 8 | end 9 | end 10 | end -------------------------------------------------------------------------------- /app/models/system_tester/action.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class Action < Step 3 | def to_s 4 | "#{INDENT}# Action: #{commented_title}\n" 5 | end 6 | 7 | def self.bg_css 8 | "orange" 9 | end 10 | 11 | def icon 12 | "input" 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/models/system_tester/assertion.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class Assertion < Step 3 | def to_s 4 | "#{INDENT}# Assertion: #{commented_title}\n" 5 | end 6 | 7 | def self.bg_css 8 | "green" 9 | end 10 | 11 | def icon 12 | "done" 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /db/migrate/20170513213953_create_system_tester_scenarios.rb: -------------------------------------------------------------------------------- 1 | class CreateSystemTesterScenarios < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :system_tester_scenarios do |t| 4 | t.string :title 5 | t.references :system_tester_feature, foreign_key: true 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20170513214255_create_system_tester_steps.rb: -------------------------------------------------------------------------------- 1 | class CreateSystemTesterSteps < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :system_tester_steps do |t| 4 | t.string :title 5 | t.string :type 6 | t.string :arg_one 7 | t.string :arg_two 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/dummy/app/views/examples/show.html.erb: -------------------------------------------------------------------------------- 1 |

<%= notice %>

2 | 3 |

4 | Title: 5 | <%= @example.title %> 6 |

7 | 8 |

9 | Comment: 10 | <%= @example.comment %> 11 |

12 | 13 | <%= link_to 'Edit', edit_example_path(@example) %> | 14 | <%= link_to 'Back', examples_path %> 15 | -------------------------------------------------------------------------------- /test/dummy/test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../config/environment', __FILE__) 2 | require 'rails/test_help' 3 | 4 | class ActiveSupport::TestCase 5 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 6 | fixtures :all 7 | 8 | # Add more helper methods to be used by all tests here... 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/system_tester/step_types_controller.rb: -------------------------------------------------------------------------------- 1 | require_dependency "system_tester/application_controller" 2 | 3 | module SystemTester 4 | class StepTypesController < ApplicationController 5 | def index 6 | render json: Step.leafs 7 | end 8 | 9 | def parents 10 | render json: Step.parent_types 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | <%= csrf_meta_tags %> 6 | 7 | <%= stylesheet_link_tag 'application', media: 'all' %> 8 | <%= javascript_include_tag 'application' %> 9 | 10 | 11 | 12 | <%= yield %> 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/controllers/system_tester/application_controller.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class ApplicationController < ActionController::API 3 | # protect_from_forgery with: :exception 4 | 5 | # before_action :view_headers 6 | # 7 | # private 8 | # 9 | # def view_headers 10 | # Rails.logger.debug request.env 11 | # end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/dummy/bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | VENDOR_PATH = File.expand_path('..', __dir__) 3 | Dir.chdir(VENDOR_PATH) do 4 | begin 5 | exec "yarnpkg #{ARGV.join(" ")}" 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20170629120850_create_system_tester_stair_steps.rb: -------------------------------------------------------------------------------- 1 | class CreateSystemTesterStairSteps < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :system_tester_stair_steps do |t| 4 | t.integer :position 5 | t.references :system_tester_step, foreign_key: true 6 | t.references :system_tester_stair, foreign_key: { to_table: :system_tester_steps } 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/models/system_tester/click_on.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class ClickOn < Action 3 | def to_s 4 | "#{super}#{INDENT}click_on \"#{arg_one}\"\n\n" 5 | end 6 | 7 | def self.friendly_type 8 | "Click" 9 | end 10 | 11 | def self.args 12 | [ 13 | { 14 | name: 'arg_one', 15 | label: 'Element', 16 | type: 'text' 17 | } 18 | ] 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/integration/system_tester/status_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module SystemTester 4 | class StatusControllerTest < ActionDispatch::IntegrationTest 5 | test "#index" do 6 | get "/system_tester/status" 7 | assert_equal "OK", JSON.parse(response.body)["status"] 8 | assert_equal SystemTester::VERSION, JSON.parse(response.body)["version"] 9 | assert_response :success 10 | end 11 | end 12 | end -------------------------------------------------------------------------------- /test/dummy/app/assets/javascripts/cable.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command. 3 | // 4 | //= require action_cable 5 | //= require_self 6 | //= require_tree ./channels 7 | 8 | (function() { 9 | this.App || (this.App = {}); 10 | 11 | App.cable = ActionCable.createConsumer(); 12 | 13 | }).call(this); 14 | -------------------------------------------------------------------------------- /app/models/system_tester/assert_text.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class AssertText < Assertion 3 | def to_s 4 | "#{super}#{INDENT}assert_text \"#{arg_one}\"\n\n" 5 | end 6 | 7 | def self.friendly_type 8 | "Text" 9 | end 10 | 11 | def self.args 12 | [ 13 | { 14 | name: 'arg_one', 15 | label: 'Text', 16 | type: 'text' 17 | } 18 | ] 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /db/migrate/20170513214722_create_system_tester_scenario_steps.rb: -------------------------------------------------------------------------------- 1 | class CreateSystemTesterScenarioSteps < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :system_tester_scenario_steps do |t| 4 | t.integer :position 5 | t.references :system_tester_step, foreign_key: true 6 | t.references :system_tester_scenario, foreign_key: true, index: {name: "index_st_st_scenario_id"} 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /app/models/system_tester/stair_step.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class StairStep < ApplicationRecord 3 | belongs_to :step, class_name: "SystemTester::Step", foreign_key: "system_tester_step_id" 4 | belongs_to :stair, class_name: "SystemTester::Stair", foreign_type: "SystemTester::Stair", foreign_key: "system_tester_stair_id" 5 | acts_as_list scope: :system_tester_stair_id 6 | 7 | after_commit do 8 | stair.touch if stair.present? 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /app/models/system_tester/scenario_step.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class ScenarioStep < ApplicationRecord 3 | belongs_to :step, class_name: "SystemTester::Step", foreign_key: "system_tester_step_id" 4 | belongs_to :scenario, class_name: "SystemTester::Scenario", foreign_key: "system_tester_scenario_id" 5 | acts_as_list scope: :system_tester_scenario_id 6 | 7 | after_commit do 8 | scenario.feature.touch if scenario.present? && scenario.feature.present? 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/models/system_tester/select.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class Select < Action 3 | def to_s 4 | "#{super}#{INDENT}select \"#{arg_one}\", from: \"#{arg_two}\"\n\n" 5 | end 6 | 7 | def self.friendly_type 8 | "Select" 9 | end 10 | 11 | def self.args 12 | [ 13 | { 14 | name: 'arg_two', 15 | label: 'Select Label', 16 | type: 'text' 17 | }, 18 | { 19 | name: 'arg_one', 20 | label: 'Option', 21 | type: 'text' 22 | } 23 | ] 24 | end 25 | end 26 | end -------------------------------------------------------------------------------- /app/models/system_tester/fill_in.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class FillIn < Action 3 | def to_s 4 | "#{super}#{INDENT}fill_in \"#{arg_one}\", with: \"#{arg_two}\"\n\n" 5 | end 6 | 7 | def self.friendly_type 8 | "Fill" 9 | end 10 | 11 | def self.args 12 | [ 13 | { 14 | name: 'arg_one', 15 | label: 'Input Label', 16 | type: 'text' 17 | }, 18 | { 19 | name: 'arg_two', 20 | label: 'Text', 21 | type: 'text' 22 | } 23 | ] 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /test/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | Bundler.require(*Rails.groups) 6 | require "system_tester" 7 | 8 | module Dummy 9 | class Application < Rails::Application 10 | # Initialize configuration defaults for originally generated Rails version. 11 | config.load_defaults 5.1 12 | 13 | # Settings in config/environments/* take precedence over those specified here. 14 | # Application configuration should go into files in config/initializers 15 | # -- all .rb files in that directory are automatically loaded. 16 | end 17 | end 18 | 19 | -------------------------------------------------------------------------------- /app/models/system_tester/assert_selector.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class AssertSelector < Assertion 3 | def to_s 4 | "#{super}#{INDENT}assert_selector \"#{arg_one}\", text: \"#{arg_two}\"\n\n" 5 | end 6 | 7 | def self.friendly_type 8 | "Selector" 9 | end 10 | 11 | def self.args 12 | [ 13 | { 14 | name: 'arg_one', 15 | label: 'Selector', 16 | type: 'text' 17 | }, 18 | { 19 | name: 'arg_two', 20 | label: 'Text', 21 | type: 'text' 22 | } 23 | ] 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails gems 3 | # installed from the root of your application. 4 | 5 | ENGINE_ROOT = File.expand_path('../..', __FILE__) 6 | ENGINE_PATH = File.expand_path('../../lib/system_tester/engine', __FILE__) 7 | APP_PATH = File.expand_path('../../test/dummy/config/application', __FILE__) 8 | 9 | # Set up gems listed in the Gemfile. 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 11 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 12 | 13 | require 'rails/all' 14 | require 'rails/engine/commands' 15 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/cors.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Avoid CORS issues when API is called from the frontend app. 4 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. 5 | 6 | # Read more: https://github.com/cyu/rack-cors 7 | # 8 | # Rails.application.config.middleware.insert_before 0, Rack::Cors do 9 | # allow do 10 | # origins 'http://localhost:8080', 'chrome-extension://ebpjncfolmfiiphibdajgblbchkklbcf/*' 11 | # 12 | # resource '*', 13 | # headers: :any, 14 | # methods: [:get, :post, :put, :patch, :delete, :options, :head] 15 | # end 16 | # end 17 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | -------------------------------------------------------------------------------- /test/integration/system_tester/step_types_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module SystemTester 4 | class SteptypesControllerTest < ActionDispatch::IntegrationTest 5 | def setup 6 | Dir.glob(SystemTester::Engine.root.join("app", "models" , "**", "*.rb")).each do |dep| 7 | require_dependency dep 8 | end 9 | end 10 | 11 | test"#index" do 12 | get "/system_tester/step_types" 13 | assert JSON.parse(response.body).length > 0 14 | assert_response :success 15 | end 16 | 17 | test "#parents" do 18 | get "/system_tester/step_types/parents" 19 | assert JSON.parse(response.body).length > 0 20 | assert_response :success 21 | end 22 | end 23 | end -------------------------------------------------------------------------------- /test/models/system_tester/command_handler_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module SystemTester 4 | class CommandHandlerTest < ActiveSupport::TestCase 5 | test "#run" do 6 | ch = SystemTester::CommandHandler.new("echo 'hello'") 7 | result = "" 8 | ch.run do |c| 9 | result << c 10 | end 11 | assert_equal 0, ch.status 12 | assert_match "hello", result 13 | end 14 | 15 | test "#run_each_line" do 16 | ch = SystemTester::CommandHandler.new("echo 'hello'") 17 | result = "" 18 | ch.run_each_line do |line| 19 | result << line 20 | end 21 | assert_equal 0, ch.status 22 | assert_match "hello", result 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /test/dummy/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require_tree . 14 | -------------------------------------------------------------------------------- /test/dummy/app/views/examples/index.html.erb: -------------------------------------------------------------------------------- 1 |

<%= notice %>

2 | 3 |

Examples

4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | <% @examples.each do |example| %> 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | <% end %> 24 | 25 |
TitleComment
<%= example.title %><%= example.comment %><%= link_to 'Show', example %><%= link_to 'Edit', edit_example_path(example) %><%= link_to 'Destroy', example, method: :delete, data: { confirm: 'Are you sure?' } %>
26 | 27 |
28 | 29 | <%= link_to 'New Example', new_example_path %> 30 | -------------------------------------------------------------------------------- /test/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | 27 | system_tester: 28 | <<: *default 29 | database: db/system_tester.sqlite3 -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Declare your gem's dependencies in system_tester.gemspec. 4 | # Bundler will treat runtime dependencies like base dependencies, and 5 | # development dependencies will be added by default to the :development group. 6 | gemspec 7 | 8 | # Declare any dependencies that are still in development here instead of in 9 | # your gemspec. These might include edge Rails or gems from your path or 10 | # Git. Remember to move these dependencies to your gemspec before releasing 11 | # your gem to rubygems.org. 12 | 13 | # To use a debugger 14 | gem 'byebug', group: [:development, :test] 15 | gem 'rack-cors', require: 'rack/cors', group: [:development, :test] 16 | 17 | group :test do 18 | gem 'simplecov' 19 | gem 'codeclimate-test-reporter', '~> 1.0.0' 20 | gem 'mocha' 21 | end 22 | -------------------------------------------------------------------------------- /test/dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /test/dummy/app/views/examples/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: example, local: true) do |form| %> 2 | <% if example.errors.any? %> 3 |
4 |

<%= pluralize(example.errors.count, "error") %> prohibited this example from being saved:

5 | 6 | 11 |
12 | <% end %> 13 | 14 |
15 | <%= form.label :title %> 16 | <%= form.text_field :title, id: :example_title %> 17 |
18 | 19 |
20 | <%= form.label :comment %> 21 | <%= form.text_area :comment, id: :example_comment %> 22 |
23 | 24 |
25 | <%= form.submit %> 26 |
27 | <% end %> 28 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/setup' 3 | rescue LoadError 4 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 5 | end 6 | 7 | require 'rdoc/task' 8 | 9 | RDoc::Task.new(:rdoc) do |rdoc| 10 | rdoc.rdoc_dir = 'rdoc' 11 | rdoc.title = 'SystemTester' 12 | rdoc.options << '--line-numbers' 13 | rdoc.rdoc_files.include('README.md') 14 | rdoc.rdoc_files.include('lib/**/*.rb') 15 | end 16 | 17 | APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__) 18 | load 'rails/tasks/engine.rake' 19 | 20 | 21 | load 'rails/tasks/statistics.rake' 22 | 23 | 24 | 25 | require 'bundler/gem_tasks' 26 | 27 | require 'rake/testtask' 28 | 29 | Rake::TestTask.new(:test) do |t| 30 | t.libs << 'test' 31 | t.pattern = 'test/**/*_test.rb' 32 | t.verbose = false 33 | end 34 | 35 | 36 | task default: :test 37 | -------------------------------------------------------------------------------- /test/integration/system_tester/executions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module SystemTester 4 | class ExecutionsControllerTest < ActionDispatch::IntegrationTest 5 | def setup 6 | @scenario = Fabricate(:scenario, title: "Feature Tester Title") 7 | @scenario.touch 8 | CommandHandler.any_instance.expects(:run_each_line).yields("out").returns(0) 9 | end 10 | 11 | test "#create feature" do 12 | post '/system_tester/executions', params: {feature_id: @scenario.feature.id} 13 | assert_equal 0, JSON.parse(response.body)["exit_code"] 14 | assert_response :success 15 | end 16 | 17 | test "#create scenario" do 18 | post '/system_tester/executions', params: {scenario_id: @scenario.id} 19 | assert_equal 0, JSON.parse(response.body)["exit_code"] 20 | assert_response :success 21 | end 22 | 23 | end 24 | end -------------------------------------------------------------------------------- /app/controllers/system_tester/executions_controller.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class ExecutionsController < ApplicationController 3 | def create 4 | feature = params[:feature_id].present? ? Feature.find(params[:feature_id]) : nil 5 | scenario = params[:scenario_id].present? ? Scenario.find(params[:scenario_id]) : nil 6 | @cmd = feature.present? ? "bin/rails test #{feature.full_file}" : "bin/rails test #{scenario.feature.full_file}:#{scenario.line_number}" 7 | status = handle_command 8 | render json: {exit_code: status} 9 | end 10 | 11 | private 12 | 13 | def handle_command 14 | command_handler = CommandHandler.new(@cmd) 15 | position = 0 16 | command_handler.run_each_line do |char| 17 | ActionCable.server.broadcast "system_tester_execution", {output: char, position: position} 18 | position += 1 19 | end 20 | end 21 | end 22 | end -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | SystemTester::Engine.routes.draw do 2 | mount ActionCable.server => '/cable' 3 | resources :features, only: [:index, :create, :update, :destroy], :defaults => {:format => :json} 4 | resources :scenarios, only: [:index, :show, :create, :update, :destroy], :defaults => {:format => :json} 5 | resources :steps, only: [:index, :show, :new, :create, :update, :destroy], :defaults => {:format => :json} 6 | resources :scenario_steps, only: [:create, :update, :destroy], :defaults => {:format => :json} 7 | resources :stair_steps, only: [:create, :update, :destroy], :defaults => {:format => :json} 8 | resources :step_types, only: [:index], :defaults => {:format => :json} do 9 | collection do 10 | get :parents, :defaults => {:format => :json} 11 | end 12 | end 13 | resources :status, only: [:index], :defaults => {:format => :json} 14 | resources :executions, only: [:create] 15 | end 16 | -------------------------------------------------------------------------------- /test/dummy/bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /app/models/system_tester/command_handler.rb: -------------------------------------------------------------------------------- 1 | require 'pty' 2 | module SystemTester 3 | class CommandHandler 4 | attr_reader :status 5 | def initialize(cmd) 6 | @cmd = cmd 7 | @status = nil 8 | end 9 | 10 | def run(&block) 11 | pty do |r,w,pid| 12 | rescue_errno pid do 13 | yield r.getc until r.eof? 14 | end 15 | end 16 | end 17 | 18 | def run_each_line(&block) 19 | pty do |r,w,pid| 20 | rescue_errno pid do 21 | r.each do |line| 22 | yield line 23 | end 24 | end 25 | end 26 | end 27 | 28 | private 29 | 30 | def pty(&block) 31 | PTY.spawn(@cmd, &block) 32 | @status = $?.exitstatus 33 | end 34 | 35 | def rescue_errno(pid, &block) 36 | begin 37 | yield 38 | rescue Errno::EIO 39 | ensure 40 | Process.wait pid 41 | end 42 | end 43 | end 44 | end -------------------------------------------------------------------------------- /lib/system_tester/engine.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class Engine < ::Rails::Engine 3 | isolate_namespace SystemTester 4 | config.generators.api_only = true 5 | 6 | initializer :cors do |app| 7 | app.config.middleware.insert_before 0, Rack::Cors do 8 | allow do 9 | origins 'ebpjncfolmfiiphibdajgblbchkklbcf', 'http://localhost:8080' 10 | resource '/system_tester/*', 11 | :headers => :any, 12 | :methods => [:get, :post, :delete, :put, :patch, :options, :head] 13 | end 14 | end 15 | 16 | app.config.action_cable.allowed_request_origins = ['chrome-extension://ebpjncfolmfiiphibdajgblbchkklbcf', 'http://localhost:8080'] 17 | end 18 | 19 | config.to_prepare do 20 | if Rails.env.development? 21 | Dir.glob(SystemTester::Engine.root.join("app", "models" , "**", "*.rb")).each do |dep| 22 | require_dependency dep 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /system_tester.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("../lib", __FILE__) 2 | 3 | # Maintain your gem's version: 4 | require "system_tester/version" 5 | 6 | # Describe your gem and declare its dependencies: 7 | Gem::Specification.new do |s| 8 | s.name = "system_tester" 9 | s.version = SystemTester::VERSION 10 | s.authors = ["Richard LaFranchi"] 11 | s.email = ["rlafranchi@icloud.com"] 12 | s.homepage = "http://github.com/rlafranchi/system_tester" 13 | s.summary = "System Tester: Rails Sysyem Test Management" 14 | s.description = "System Tester aims to symplify QA for Rails applications by providing a development tool for creating and managing system tests" 15 | s.license = "MIT" 16 | 17 | s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] 18 | 19 | s.add_dependency "rails", "~> 5.1.0" 20 | s.add_dependency "acts_as_list", "~> 0.9.0" 21 | s.add_dependency "rack-cors" 22 | 23 | s.add_development_dependency "sqlite3" 24 | s.add_development_dependency "faker" 25 | s.add_development_dependency "fabrication" 26 | end 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: ruby 3 | cache: bundler 4 | rvm: 5 | - 2.3 6 | - 2.4 7 | - ruby-head 8 | before_script: 9 | - bin/rails db:migrate RAILS_ENV=test 10 | - bin/rails app:system_tester:migrate 11 | addons: 12 | code_climate: 13 | repo_token: 14 | secure: "TCYPUQZA9Cp1nZur+02JMf35tvmdKhYfmH7075KbyDNuOIUYwuaJNeea7Kzg6Oh8Ffc/1BZ6RDOPsttwJa+Hs6PFkYgFlABRPkSWuGuO8pH0oP+0f0BTi+4GOabZ2Owunb2MzGCF9wyqxuCkh40NHiXYstMId4QI60u8ac3VpC4nMHwvpbDiffOxoQf8nRs5XpVFPn9Izi9Ct9o/DgK5gnFOZKF6MEzKc6qezNnf9h3qUaa4dBHl371cjfsY4eIWeyp/UB/zt2ReamjOu4aVjZ7RJ9ckhVfbfhL7lqdW21dVRxy/ybgxeFQLS8j2HQ3hRbFvWvOsERbQ2Qxl8ni53yxJdkXRIPcBs+/Q9t3MrjcAJbZbHXlWNtaS95gsWhlDIhVigW4GDjHDkkXv5aYR9OfCrEX5pbi3bHarU11BZ6a5qGKF+bcUhRguGdfsjekc5347gda8sn88C3i7oABHn3ZTzkJVOCR1ULHsCX5l9S+81NHIoT8XRGyXmjxb7eClCX43TbvAYttJMlhM+hFagx40RHaB+kE7qIVh5jgCqLQYx2OgF2H8c/oCVoGgFS7aWHKxJ8bApI7hFL2nyLnHiRTO3KyLEXFNKaDj9RLKHNHdo2OYLhzt85WSYK6FxezjsX3XZRaZ2oCUdUSEwlQhqbpJpXkH9PjNKJktb2ogUZE=" 15 | after_success: 16 | - bundle exec codeclimate-test-reporter 17 | script: bin/rails test 18 | matrix: 19 | allow_failures: 20 | - rvm: ruby-head 21 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Richard LaFranchi 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 | -------------------------------------------------------------------------------- /test/dummy/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # Install JavaScript dependencies if using Yarn 22 | # system('bin/yarn') 23 | 24 | 25 | # puts "\n== Copying sample files ==" 26 | # unless File.exist?('config/database.yml') 27 | # cp 'config/database.yml.sample', 'config/database.yml' 28 | # end 29 | 30 | puts "\n== Preparing database ==" 31 | system! 'bin/rails db:setup' 32 | 33 | puts "\n== Removing old logs and tempfiles ==" 34 | system! 'bin/rails log:clear tmp:clear' 35 | 36 | puts "\n== Restarting application server ==" 37 | system! 'bin/rails restart' 38 | end 39 | -------------------------------------------------------------------------------- /lib/tasks/system_tester_tasks.rake: -------------------------------------------------------------------------------- 1 | # desc "Explaining what the task does" 2 | # create install task 3 | # TODO: 4 | # - potentially load using sql instead of migrations 5 | # - auto mount in the routes 6 | # - auto add to database.yml defaulting to sqlite3 7 | # - create test directories 8 | # - copy system_tester_system_test_case 9 | 10 | # remove migration copy in favor of custom migration task 11 | Rake::Task["system_tester:install:migrations"].clear 12 | namespace :system_tester do 13 | namespace :install do 14 | desc "(Not Supported)" 15 | task "migrations" do 16 | # do nothing 17 | puts "Not supported..." 18 | end 19 | end 20 | 21 | # see ActiveRecord::Tasks::DatabaseTasks 22 | desc "Running migrations for the System Tester db" 23 | task :migrate => :environment do 24 | include ActiveRecord::Tasks 25 | ENV['RAILS_ENV'] = "system_tester" 26 | DatabaseTasks.db_dir = Rails.root.join("db") 27 | DatabaseTasks.env = "system_tester" 28 | DatabaseTasks.migrations_paths = [SystemTester::Engine.root.join("db", "migrate")] 29 | DatabaseTasks.root = Rails.root 30 | DatabaseTasks.create_current("system_tester") 31 | DatabaseTasks.migrate 32 | end 33 | end -------------------------------------------------------------------------------- /app/controllers/system_tester/stair_steps_controller.rb: -------------------------------------------------------------------------------- 1 | require_dependency "system_tester/application_controller" 2 | 3 | module SystemTester 4 | class StairStepsController < ApplicationController 5 | 6 | def create 7 | stair_step = StairStep.new(stair_step_params) 8 | if stair_step.save 9 | render json: stair_step.to_json(stair_step_json_options) 10 | else 11 | render json: { errors: stair_step.errors }, status: :unprocessable_entity 12 | end 13 | end 14 | 15 | def update 16 | stair_step = StairStep.find(params[:id]) 17 | if stair_step.update(stair_step_params) 18 | render json: stair_step.to_json(stair_step_json_options) 19 | else 20 | render json: { errors: stair_step.errors }, status: :unprocessable_entity 21 | end 22 | end 23 | 24 | def destroy 25 | StairStep.find(params[:id]).destroy! 26 | render json: {} 27 | end 28 | 29 | private 30 | 31 | def stair_step_params 32 | params.require(:stair_step).permit(:position, :system_tester_step_id, :system_tester_stair_id) 33 | end 34 | 35 | def stair_step_json_options 36 | { 37 | include: :step 38 | } 39 | end 40 | 41 | end 42 | end -------------------------------------------------------------------------------- /app/models/system_tester/scenario.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class Scenario < ApplicationRecord 3 | include SystemTester::TitleValidatable 4 | validates_uniqueness_of :title, scope: :system_tester_feature_id 5 | 6 | INDENT = " " * 4 7 | belongs_to :feature, class_name: "SystemTester::Feature", foreign_key: "system_tester_feature_id" 8 | has_many :scenario_steps, 9 | -> { order 'position asc' }, 10 | class_name: "SystemTester::ScenarioStep", 11 | foreign_key: "system_tester_scenario_id", 12 | dependent: :destroy 13 | has_many :steps, -> { order 'position asc' }, through: :scenario_steps 14 | 15 | after_commit do 16 | feature.touch if feature.present? 17 | end 18 | 19 | def to_s 20 | str = "" 21 | str << open 22 | str << steps.map(&:to_s).join("") 23 | str << close 24 | str 25 | end 26 | 27 | def line_number 28 | lines = feature.to_s.split("\n") 29 | lines.index { |line| open.chomp === line } + 1 30 | end 31 | 32 | private 33 | 34 | def open 35 | "#{INDENT}test \"#{title}\" do\n" 36 | end 37 | 38 | def close 39 | "#{INDENT}end\n" 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /app/controllers/system_tester/features_controller.rb: -------------------------------------------------------------------------------- 1 | require_dependency "system_tester/application_controller" 2 | 3 | module SystemTester 4 | class FeaturesController < ApplicationController 5 | def index 6 | render json: Feature.all.to_json(feature_json_options) 7 | end 8 | 9 | def create 10 | feature = Feature.new(feature_params) 11 | if feature.save 12 | render json: feature.to_json(feature_json_options) 13 | else 14 | render json: { errors: feature.errors }, status: :unprocessable_entity 15 | end 16 | end 17 | 18 | def update 19 | feature = Feature.find(params[:id]) 20 | if feature.update(feature_params) 21 | render json: feature.to_json(feature_json_options) 22 | else 23 | render json: { errors: feature.errors }, status: :unprocessable_entity 24 | end 25 | end 26 | 27 | def destroy 28 | Feature.find(params[:id]).destroy! 29 | render json: {} 30 | end 31 | 32 | private 33 | 34 | def feature_params 35 | params.require(:feature).permit(:title) 36 | end 37 | 38 | def feature_json_options 39 | { 40 | methods: [:to_s], 41 | include: :scenarios 42 | } 43 | end 44 | 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /app/controllers/system_tester/scenario_steps_controller.rb: -------------------------------------------------------------------------------- 1 | require_dependency "system_tester/application_controller" 2 | 3 | module SystemTester 4 | class ScenarioStepsController < ApplicationController 5 | 6 | def create 7 | scenario_step = ScenarioStep.new(scenario_step_params) 8 | if scenario_step.save 9 | render json: scenario_step.to_json(scenario_step_json_options) 10 | else 11 | render json: { errors: scenario_step.errors }, status: :unprocessable_entity 12 | end 13 | end 14 | 15 | def update 16 | scenario_step = ScenarioStep.find(params[:id]) 17 | if scenario_step.update(scenario_step_params) 18 | render json: scenario_step.to_json(scenario_step_json_options) 19 | else 20 | render json: { errors: scenario_step.errors }, status: :unprocessable_entity 21 | end 22 | end 23 | 24 | def destroy 25 | ScenarioStep.find(params[:id]).destroy! 26 | render json: {} 27 | end 28 | 29 | private 30 | 31 | def scenario_step_params 32 | params.require(:scenario_step).permit(:position, :system_tester_step_id, :system_tester_scenario_id) 33 | end 34 | 35 | def scenario_step_json_options 36 | { 37 | include: :step 38 | } 39 | end 40 | 41 | end 42 | end -------------------------------------------------------------------------------- /test/dummy/app/assets/stylesheets/scaffold.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #fff; 3 | color: #333; 4 | margin: 33px; 5 | } 6 | 7 | body, p, ol, ul, td { 8 | font-family: verdana, arial, helvetica, sans-serif; 9 | font-size: 13px; 10 | line-height: 18px; 11 | } 12 | 13 | pre { 14 | background-color: #eee; 15 | padding: 10px; 16 | font-size: 11px; 17 | } 18 | 19 | a { 20 | color: #000; 21 | } 22 | 23 | a:visited { 24 | color: #666; 25 | } 26 | 27 | a:hover { 28 | color: #fff; 29 | background-color: #000; 30 | } 31 | 32 | th { 33 | padding-bottom: 5px; 34 | } 35 | 36 | td { 37 | padding: 0 5px 7px; 38 | } 39 | 40 | div.field, 41 | div.actions { 42 | margin-bottom: 10px; 43 | } 44 | 45 | #notice { 46 | color: green; 47 | } 48 | 49 | .field_with_errors { 50 | padding: 2px; 51 | background-color: red; 52 | display: table; 53 | } 54 | 55 | #error_explanation { 56 | width: 450px; 57 | border: 2px solid red; 58 | padding: 7px 7px 0; 59 | margin-bottom: 20px; 60 | background-color: #f0f0f0; 61 | } 62 | 63 | #error_explanation h2 { 64 | text-align: left; 65 | font-weight: bold; 66 | padding: 5px 5px 5px 15px; 67 | font-size: 12px; 68 | margin: -7px -7px 0; 69 | background-color: #c00; 70 | color: #fff; 71 | } 72 | 73 | #error_explanation ul li { 74 | font-size: 12px; 75 | list-style: square; 76 | } 77 | 78 | label { 79 | display: block; 80 | } 81 | -------------------------------------------------------------------------------- /app/models/system_tester/visit.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class Visit < Action 3 | @@engines = {} 4 | 5 | def to_s 6 | "#{super}#{INDENT}visit \"#{arg_one}\"\n\n" 7 | end 8 | 9 | def self.args 10 | [ 11 | { 12 | name: "arg_one", 13 | label: "Path or Url", 14 | type: "autocomplete", 15 | options: collect_routes(Rails.application.routes.routes) 16 | } 17 | ] 18 | end 19 | 20 | private 21 | 22 | # Snatched from action_dispatch/routing/inspector 23 | def self.collect_routes(routes) 24 | routes.collect do |route| 25 | ActionDispatch::Routing::RouteWrapper.new(route) 26 | end.reject do |route| 27 | route.internal? || route.reqs.starts_with?("system_tester", "SystemTester") || route.verb != "GET" 28 | end.collect do |route| 29 | collect_engine_routes(route) 30 | { 31 | name: "#{route.name}_path", 32 | value: route.path 33 | } 34 | end.concat(@@engines.values) 35 | end 36 | 37 | def self.collect_engine_routes(route) 38 | name = route.endpoint 39 | return unless route.engine? 40 | return if @@engines[name] 41 | 42 | routes = route.rack_app.routes 43 | if routes.is_a?(ActionDispatch::Routing::RouteSet) 44 | @@engines[name] = collect_routes(routes.routes) 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /test/dummy/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | # Shared secrets are available across all environments. 14 | 15 | # shared: 16 | # api_key: a1B2c3D4e5F6 17 | 18 | # Environmental secrets are only available for that specific environment. 19 | 20 | development: 21 | secret_key_base: 18a018785429ff51a471552fb17eece138e9422dca71868a5a512616e05a8e9728760b39fa2b1e3f8e269057cf215f5c5e3adfc73f0bffcfeceb80a4c753bafd 22 | 23 | test: 24 | secret_key_base: 4849af7e2ba9212bf8793b8eb85d34365c595cab2834a499aa7b1ff7f178b64e66ba0ce0138cddfc1757c88ff78fe862decc530ec3d6e46f64071fd7d0bc4216 25 | 26 | # Do not keep production secrets in the unencrypted secrets file. 27 | # Instead, either read values from the environment. 28 | # Or, use `bin/rails secrets:setup` to configure encrypted secrets 29 | # and move the `production:` environment over there. 30 | 31 | production: 32 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 33 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/examples_controller.rb: -------------------------------------------------------------------------------- 1 | class ExamplesController < ApplicationController 2 | before_action :set_example, only: [:show, :edit, :update, :destroy] 3 | 4 | # GET /examples 5 | def index 6 | @examples = Example.all 7 | end 8 | 9 | # GET /examples/1 10 | def show 11 | end 12 | 13 | # GET /examples/new 14 | def new 15 | @example = Example.new 16 | end 17 | 18 | # GET /examples/1/edit 19 | def edit 20 | end 21 | 22 | # POST /examples 23 | def create 24 | @example = Example.new(example_params) 25 | 26 | if @example.save 27 | redirect_to @example, notice: 'Example was successfully created.' 28 | else 29 | render :new 30 | end 31 | end 32 | 33 | # PATCH/PUT /examples/1 34 | def update 35 | if @example.update(example_params) 36 | redirect_to @example, notice: 'Example was successfully updated.' 37 | else 38 | render :edit 39 | end 40 | end 41 | 42 | # DELETE /examples/1 43 | def destroy 44 | @example.destroy 45 | redirect_to examples_url, notice: 'Example was successfully destroyed.' 46 | end 47 | 48 | private 49 | # Use callbacks to share common setup or constraints between actions. 50 | def set_example 51 | @example = Example.find(params[:id]) 52 | end 53 | 54 | # Only allow a trusted parameter "white list" through. 55 | def example_params 56 | params.require(:example).permit(:title, :comment) 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /app/controllers/system_tester/scenarios_controller.rb: -------------------------------------------------------------------------------- 1 | require_dependency "system_tester/application_controller" 2 | 3 | module SystemTester 4 | class ScenariosController < ApplicationController 5 | def index 6 | render json: Scenario.all.to_json(scenario_json_options) 7 | end 8 | 9 | def show 10 | scenario = Scenario.find(params[:id]) 11 | render json: scenario.to_json(scenario_json_options) 12 | end 13 | 14 | def create 15 | scenario = Scenario.new(scenario_params) 16 | if scenario.save 17 | render json: scenario.to_json(scenario_json_options) 18 | else 19 | render json: { errors: scenario.errors }, status: :unprocessable_entity 20 | end 21 | end 22 | 23 | def update 24 | scenario = Scenario.find(params[:id]) 25 | if scenario.update(scenario_params) 26 | render json: scenario.to_json(scenario_json_options) 27 | else 28 | render json: { errors: scenario.errors }, status: :unprocessable_entity 29 | end 30 | end 31 | 32 | def destroy 33 | Scenario.find(params[:id]).destroy! 34 | render json: {} 35 | end 36 | 37 | private 38 | 39 | def scenario_params 40 | params.require(:scenario).permit(:title, :system_tester_feature_id) 41 | end 42 | 43 | def scenario_json_options 44 | { 45 | include: { 46 | scenario_steps: { 47 | include: { 48 | step: { 49 | methods: [:friendly_type, :parent_type, :bg_css, :text_css, :icon] 50 | } 51 | } 52 | }, 53 | feature: {} 54 | }, 55 | methods: [:to_s] 56 | } 57 | end 58 | 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /app/models/system_tester/feature.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class Feature < ApplicationRecord 3 | include SystemTester::TitleValidatable 4 | include SystemTester::Fileable 5 | validates_uniqueness_of :title 6 | 7 | has_many :scenarios, foreign_key: "system_tester_feature_id", class_name: 'SystemTester::Scenario', dependent: :destroy 8 | 9 | def to_s 10 | str = "" 11 | str << open 12 | str << scenarios.map(&:to_s).join("") 13 | str << close 14 | str 15 | end 16 | alias_method :code_to_write, :to_s 17 | 18 | private 19 | 20 | def open 21 | str = "" 22 | stairs.each do |stair| 23 | str << "require 'support/system_tester/#{stair.method_name}'\n" 24 | end 25 | str << "require_relative 'system_tester_system_test_case'\n\n" 26 | str << "module SystemTester\n" 27 | str << " class #{stripped_title.camelize}Test < SystemTesterSystemTestCase\n" 28 | stairs.each do |stair| 29 | str << " include #{stair.module_name}\n" 30 | end 31 | str << "\n" unless stairs.empty? 32 | str 33 | end 34 | 35 | def close 36 | " end\nend\n" 37 | end 38 | 39 | def stripped_title 40 | title.gsub(/\s+/,"") 41 | end 42 | 43 | def stripped_title_was 44 | title_was.gsub(/\s+/,"") 45 | end 46 | 47 | def stairs 48 | Stair.joins(:scenarios).where("system_tester_scenarios.system_tester_feature_id = ?", id) 49 | end 50 | 51 | def base_dir 52 | File.join("system", "system_tester") 53 | end 54 | 55 | def file_name 56 | "#{stripped_title.underscore}_test.rb" 57 | end 58 | 59 | def file_name_was 60 | "#{stripped_title_was.underscore}_test.rb" 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /app/controllers/system_tester/steps_controller.rb: -------------------------------------------------------------------------------- 1 | require_dependency "system_tester/application_controller" 2 | 3 | module SystemTester 4 | class StepsController < ApplicationController 5 | def index 6 | render json: Step.all.to_json(step_json_options) 7 | end 8 | 9 | def show 10 | render json: Step.find(params[:id]).to_json(step_json_options) 11 | end 12 | 13 | def new 14 | render json: Step.new(step_params).to_json(step_json_options) 15 | end 16 | 17 | def create 18 | step = Step.new(step_params) 19 | if step.save 20 | render json: step.to_json(step_json_options) 21 | else 22 | render json: { errors: step.errors }, status: :unprocessable_entity 23 | end 24 | end 25 | 26 | def update 27 | step = Step.find(params[:id]) 28 | if step.update(step_params) 29 | render json: step.to_json(step_json_options) 30 | else 31 | render json: { errors: step.errors }, status: :unprocessable_entity 32 | end 33 | end 34 | 35 | def destroy 36 | Step.find(params[:id]).destroy! 37 | render json: {} 38 | end 39 | 40 | private 41 | 42 | def step_params 43 | params.require(:step).permit(:title, :type, :arg_one, :arg_two) 44 | end 45 | 46 | def step_json_options 47 | { 48 | methods: [:type, :friendly_type, :parent_type, :to_s, :bg_css, :text_css, :icon, :module], 49 | include: { 50 | scenarios: {}, 51 | stair_steps: { 52 | include: { 53 | step: { 54 | methods: [:friendly_type, :parent_type, :bg_css, :text_css, :icon] 55 | } 56 | } 57 | } 58 | } 59 | } 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /app/models/system_tester/stair.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class Stair < Step 3 | include SystemTester::TitleValidatable 4 | validates_uniqueness_of :title 5 | include SystemTester::Fileable 6 | has_many :stair_steps, 7 | -> { order 'position asc' }, 8 | class_name: "SystemTester::StairStep", 9 | foreign_key: "system_tester_stair_id", 10 | dependent: :destroy 11 | has_many :steps, -> { order 'position asc' }, through: :stair_steps 12 | 13 | def to_s 14 | "#{INDENT}# Stair: #{title}\n#{INDENT}#{method_name}\n\n" 15 | end 16 | 17 | def module 18 | str = "" 19 | str << open 20 | str << steps.map(&:to_s).join("") 21 | str << close 22 | str 23 | end 24 | alias_method :code_to_write, :module 25 | 26 | def module_name 27 | stripped_title.camelize 28 | end 29 | 30 | def method_name 31 | stripped_title.underscore 32 | end 33 | 34 | def icon 35 | "clear_all" 36 | end 37 | 38 | def self.bg_css 39 | "deep-purple" 40 | end 41 | 42 | private 43 | 44 | def open 45 | str = "" 46 | str << "module SystemTester\n" 47 | str << " module #{module_name}\n" 48 | str << " def #{method_name}\n" 49 | str 50 | end 51 | 52 | def close 53 | " end\n end\nend\n" 54 | end 55 | 56 | def stripped_title 57 | title.gsub(/\s+/,"") 58 | end 59 | 60 | def stripped_title_was 61 | title_was.gsub(/\s+/,"") 62 | end 63 | 64 | def method_name_was 65 | stripped_title_was.underscore 66 | end 67 | 68 | def base_dir 69 | File.join("support", "system_tester") 70 | end 71 | 72 | def file_name 73 | "#{method_name}.rb" 74 | end 75 | 76 | def file_name_was 77 | "#{method_name_was}.rb" 78 | end 79 | end 80 | end -------------------------------------------------------------------------------- /test/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

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

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../test/dummy/config/environment.rb", __FILE__) 2 | ActiveRecord::Migrator.migrations_paths = [File.expand_path("../../test/dummy/db/migrate", __FILE__)] 3 | ActiveRecord::Migrator.migrations_paths << File.expand_path('../../db/migrate', __FILE__) 4 | require "rails/test_help" 5 | require "mocha/mini_test" 6 | require "faker" 7 | require "fabrication" 8 | require "simplecov" 9 | SimpleCov.start :rails do 10 | filters.clear 11 | add_filter do |src| 12 | !(src.filename =~ /^#{SimpleCov.root}/) unless src.filename =~ /system_tester/ 13 | end 14 | add_filter '/app/jobs/' 15 | add_filter '/lib/' 16 | add_filter '/test/' 17 | end 18 | 19 | Fabrication.configure do |config| 20 | config.fabricator_path = 'test/fabricators' 21 | config.path_prefix = SystemTester::Engine.root 22 | end 23 | 24 | class ActionDispatch::IntegrationTest 25 | 26 | # Drop all columns after each test case. 27 | teardown :clean_up 28 | def clean_up 29 | SystemTester::Feature.destroy_all 30 | SystemTester::Stair.destroy_all 31 | path1 = Rails.root.join("test", "system", "system_tester") 32 | path2 = Rails.root.join("test", "support", "system_tester") 33 | FileUtils.rm_r(path1) if File.exist?(path1) 34 | FileUtils.rm_r(path2) if File.exist?(path2) 35 | end 36 | 37 | end 38 | 39 | # Filter out Minitest backtrace while allowing backtrace from other libraries 40 | # to be shown. 41 | Minitest.backtrace_filter = Minitest::BacktraceFilter.new 42 | 43 | 44 | # Load fixtures from the engine 45 | # if ActiveSupport::TestCase.respond_to?(:fixture_path=) 46 | # ActiveSupport::TestCase.fixture_path = File.expand_path("./fabricators/system_tester", __FILE__) 47 | # ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path 48 | # ActiveSupport::TestCase.file_fixture_path = File.expand_path("./fixtures/files", __FILE__) 49 | # # ActiveSupport::TestCase.fixtures :all 50 | # end 51 | -------------------------------------------------------------------------------- /test/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = false 17 | # config.public_file_server.headers = { 18 | # 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}" 19 | # } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /app/models/concerns/system_tester/fileable.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | module SystemTester 4 | module Fileable 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | before_save do 9 | FileUtils.mkdir_p(base_path) 10 | unless File.exists?(system_test_case_path) || "support".match(system_test_case_path) 11 | File.open(system_test_case_path, "w+") { |f| f.write(system_test_case_content) } 12 | end 13 | end 14 | 15 | after_touch do 16 | save_file 17 | end 18 | 19 | before_create do 20 | save_file 21 | end 22 | 23 | before_update do 24 | if title_changed? 25 | delete_old_file 26 | end 27 | save_file 28 | end 29 | 30 | before_destroy do 31 | delete_file 32 | end 33 | 34 | def full_file 35 | File.join(base_path, file_name) 36 | end 37 | 38 | private 39 | 40 | def test_path 41 | Rails.root.join("test") 42 | end 43 | 44 | def base_path 45 | File.join(test_path, base_dir) 46 | end 47 | 48 | def old_file 49 | File.join(base_path, file_name_was) 50 | end 51 | 52 | def delete_file 53 | File.delete(full_file) if File.exist?(full_file) 54 | end 55 | 56 | def delete_old_file 57 | File.delete(old_file) 58 | end 59 | 60 | def save_file 61 | File.open(full_file, 'w+') do |f| 62 | f.write(code_to_write) 63 | end 64 | end 65 | 66 | def system_test_case_path 67 | File.join(base_path, "system_tester_system_test_case.rb") 68 | end 69 | 70 | def system_test_case_content 71 | < 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

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

63 |
64 |

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

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /test/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

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

63 |
64 |

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

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /test/integration/system_tester/stair_steps_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module SystemTester 4 | class StairStepsControllerTest < ActionDispatch::IntegrationTest 5 | def setup 6 | @stair_step = Fabricate(:stair_step) 7 | @stair_step.touch 8 | end 9 | 10 | test "#create" do 11 | assert_difference 'SystemTester::StairStep.count', 1 do 12 | post '/system_tester/stair_steps', params: stair_step_params 13 | end 14 | assert_response :success 15 | end 16 | 17 | test "#create invalid" do 18 | post '/system_tester/stair_steps', params: stair_step_params(nil, -1, -1) 19 | assert JSON.parse(response.body)["errors"]["step"].present? 20 | assert JSON.parse(response.body)["errors"]["stair"].present? 21 | assert_response :unprocessable_entity 22 | end 23 | 24 | test "#update" do 25 | Fabricate(:stair_step, position: 2, step: @stair_step.step, stair: @stair_step.stair) 26 | put stair_step_path, params: stair_step_params(2) 27 | assert_equal 2, @stair_step.reload.position 28 | assert_response :success 29 | end 30 | 31 | test "#update invalid" do 32 | put stair_step_path, params: stair_step_params(nil,-1, -1) 33 | assert JSON.parse(response.body)["errors"]["step"].present? 34 | assert JSON.parse(response.body)["errors"]["stair"].present? 35 | assert_response :unprocessable_entity 36 | end 37 | 38 | test "#destroy" do 39 | assert_difference 'SystemTester::StairStep.count', -1 do 40 | delete stair_step_path 41 | end 42 | assert_response :success 43 | end 44 | 45 | private 46 | 47 | def stair_step_path 48 | "/system_tester/stair_steps/#{@stair_step.id}" 49 | end 50 | 51 | def stair_step_params(position=nil, step_id=nil, stair_id=nil) 52 | { 53 | stair_step: { 54 | position: position || 1, 55 | system_tester_step_id: step_id || @stair_step.step.id, 56 | system_tester_stair_id: stair_id || @stair_step.stair.id 57 | } 58 | } 59 | end 60 | end 61 | end -------------------------------------------------------------------------------- /test/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}" 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | # Print deprecation notices to the Rails logger. 35 | config.active_support.deprecation = :log 36 | 37 | # Raise an error on page load if there are pending migrations. 38 | config.active_record.migration_error = :page_load 39 | 40 | # Debug mode disables concatenation and preprocessing of assets. 41 | # This option may cause significant delays in view rendering with a large 42 | # number of complex assets. 43 | config.assets.debug = true 44 | 45 | # Suppress logger output for asset requests. 46 | config.assets.quiet = true 47 | 48 | # Raises error for missing translations 49 | # config.action_view.raise_on_missing_translations = true 50 | 51 | # Use an evented file watcher to asynchronously detect changes in source code, 52 | # routes, locales, etc. This feature depends on the listen gem. 53 | # config.file_watcher = ActiveSupport::EventedFileUpdateChecker 54 | end 55 | -------------------------------------------------------------------------------- /test/integration/system_tester/features_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module SystemTester 4 | class FeaturesControllerTest < ActionDispatch::IntegrationTest 5 | def setup 6 | @feature = Fabricate(:feature, title: "Feature Tester Title") 7 | @feature.touch 8 | end 9 | 10 | test "#index" do 11 | get '/system_tester/features' 12 | feature_from_response = get_feature(JSON.parse(response.body)) 13 | assert_match "FeatureTesterTitle", feature_from_response["to_s"] 14 | assert_response :success 15 | end 16 | 17 | test "#create" do 18 | assert_difference 'SystemTester::Feature.count', 1 do 19 | post '/system_tester/features', params: feature_params 20 | end 21 | assert_response :success 22 | end 23 | 24 | test "#create invalid" do 25 | post '/system_tester/features', params: feature_params(@feature.title) 26 | assert JSON.parse(response.body)["errors"]["title"].present? 27 | assert_response :unprocessable_entity 28 | end 29 | 30 | test "#update" do 31 | new_title = "New Feature Title" 32 | put feature_path, params: feature_params(new_title) 33 | assert File.exist?(Rails.root.join("test", "system", "system_tester", "new_feature_title_test.rb")) 34 | assert_equal new_title, @feature.reload.title 35 | assert_response :success 36 | end 37 | 38 | test "#update invalid" do 39 | other_feature = Fabricate(:feature, title: "Other Feature Title") 40 | put feature_path, params: feature_params(other_feature.title) 41 | assert JSON.parse(response.body)["errors"]["title"].present? 42 | assert_response :unprocessable_entity 43 | end 44 | 45 | test "#destroy" do 46 | assert_difference 'SystemTester::Feature.count', -1 do 47 | delete feature_path 48 | end 49 | assert_response :success 50 | end 51 | 52 | private 53 | 54 | def feature_path 55 | "/system_tester/features/#{@feature.id}" 56 | end 57 | 58 | def feature_params(title=Faker::Lorem.words(2).join(" ")) 59 | { 60 | feature: { 61 | title: title 62 | } 63 | } 64 | end 65 | 66 | def get_feature(response) 67 | response.select { |f| f["id"] == @feature.id }.first 68 | end 69 | end 70 | end -------------------------------------------------------------------------------- /test/integration/system_tester/scenario_steps_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module SystemTester 4 | class ScenarioStepsControllerTest < ActionDispatch::IntegrationTest 5 | def setup 6 | @scenario_step = Fabricate(:scenario_step) 7 | @scenario_step.touch 8 | end 9 | 10 | test "#create" do 11 | assert_difference 'SystemTester::ScenarioStep.count', 1 do 12 | post '/system_tester/scenario_steps', params: scenario_step_params 13 | end 14 | assert_response :success 15 | end 16 | 17 | test "#create invalid" do 18 | post '/system_tester/scenario_steps', params: scenario_step_params(nil, -1, -1) 19 | assert JSON.parse(response.body)["errors"]["step"].present? 20 | assert JSON.parse(response.body)["errors"]["scenario"].present? 21 | assert_response :unprocessable_entity 22 | end 23 | 24 | test "#create for stair" do 25 | stair = Fabricate(:stair) 26 | assert_difference 'SystemTester::ScenarioStep.count', 1 do 27 | post '/system_tester/scenario_steps', params: scenario_step_params(nil, stair.id) 28 | end 29 | assert_response :success 30 | end 31 | 32 | test "#update" do 33 | Fabricate(:scenario_step, position: 2, step: @scenario_step.step, scenario: @scenario_step.scenario) 34 | put scenario_step_path, params: scenario_step_params(2) 35 | assert_equal 2, @scenario_step.reload.position 36 | assert_response :success 37 | end 38 | 39 | test "#update invalid" do 40 | put scenario_step_path, params: scenario_step_params(nil,-1, -1) 41 | assert JSON.parse(response.body)["errors"]["step"].present? 42 | assert JSON.parse(response.body)["errors"]["scenario"].present? 43 | assert_response :unprocessable_entity 44 | end 45 | 46 | test "#destroy" do 47 | assert_difference 'SystemTester::ScenarioStep.count', -1 do 48 | delete scenario_step_path 49 | end 50 | assert_response :success 51 | end 52 | 53 | private 54 | 55 | def scenario_step_path 56 | "/system_tester/scenario_steps/#{@scenario_step.id}" 57 | end 58 | 59 | def scenario_step_params(position=nil, step_id=nil, scenario_id=nil) 60 | { 61 | scenario_step: { 62 | position: position || 1, 63 | system_tester_step_id: step_id || @scenario_step.step.id, 64 | system_tester_scenario_id: scenario_id || @scenario_step.scenario.id 65 | } 66 | } 67 | end 68 | end 69 | end -------------------------------------------------------------------------------- /test/integration/system_tester/scenarios_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module SystemTester 4 | class ScenariosControllerTest < ActionDispatch::IntegrationTest 5 | def setup 6 | @scenario = Fabricate(:scenario) 7 | @scenario.touch 8 | end 9 | 10 | test "#index" do 11 | get '/system_tester/scenarios' 12 | scenario_from_response = get_scenario(JSON.parse(response.body)) 13 | assert_match @scenario.title, scenario_from_response["to_s"] 14 | assert_response :success 15 | end 16 | 17 | test "#show" do 18 | get scenario_path 19 | assert_response :success 20 | end 21 | 22 | test "#create" do 23 | assert_difference 'SystemTester::Scenario.count', 1 do 24 | post '/system_tester/scenarios', params: scenario_params 25 | end 26 | assert_response :success 27 | end 28 | 29 | test "#create invalid" do 30 | post '/system_tester/scenarios', params: scenario_params(@scenario.title, @scenario.system_tester_feature_id) 31 | assert JSON.parse(response.body)["errors"]["title"].present? 32 | assert_response :unprocessable_entity 33 | end 34 | 35 | test "#update" do 36 | new_title = Faker::Lorem.words(2).join(" ") 37 | put scenario_path, params: scenario_params(new_title) 38 | assert_equal new_title, @scenario.reload.title 39 | assert_response :success 40 | end 41 | 42 | test "#update invalid" do 43 | other_scenario = Fabricate(:scenario, title: "Other scenario Title") 44 | put scenario_path, params: scenario_params(other_scenario.title, other_scenario.system_tester_feature_id) 45 | assert JSON.parse(response.body)["errors"]["title"].present? 46 | assert_response :unprocessable_entity 47 | end 48 | 49 | test "#destroy" do 50 | assert_difference 'SystemTester::Scenario.count', -1 do 51 | delete scenario_path 52 | end 53 | assert_response :success 54 | end 55 | 56 | private 57 | 58 | def scenario_path 59 | "/system_tester/scenarios/#{@scenario.id}" 60 | end 61 | 62 | def scenario_params(title=nil, feature_id=nil) 63 | title = title || Faker::Lorem.words(2).join(" ") 64 | { 65 | scenario: { 66 | title: title, 67 | system_tester_feature_id: feature_id || Fabricate(:feature).id 68 | } 69 | } 70 | end 71 | 72 | def get_scenario(response) 73 | response.select { |f| f["id"] == @scenario.id }.first 74 | end 75 | end 76 | end -------------------------------------------------------------------------------- /test/dummy/config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | # preload_app! 34 | 35 | # If you are preloading your application and using Active Record, it's 36 | # recommended that you close any connections to the database before workers 37 | # are forked to prevent connection leakage. 38 | # 39 | # before_fork do 40 | # ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) 41 | # end 42 | 43 | # The code in the `on_worker_boot` will be called if you are using 44 | # clustered mode by specifying a number of `workers`. After each worker 45 | # process is booted, this block will be run. If you are using the `preload_app!` 46 | # option, you will want to use this block to reconnect to any threads 47 | # or connections that may have been created at application boot, as Ruby 48 | # cannot share connections between processes. 49 | # 50 | # on_worker_boot do 51 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 52 | # end 53 | # 54 | 55 | # Allow puma to be restarted by `rails restart` command. 56 | plugin :tmp_restart 57 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 20170629120850) do 14 | 15 | create_table "system_tester_features", force: :cascade do |t| 16 | t.string "title" 17 | t.datetime "created_at", null: false 18 | t.datetime "updated_at", null: false 19 | end 20 | 21 | create_table "system_tester_scenario_steps", force: :cascade do |t| 22 | t.integer "position" 23 | t.integer "system_tester_step_id" 24 | t.integer "system_tester_scenario_id" 25 | t.datetime "created_at", null: false 26 | t.datetime "updated_at", null: false 27 | t.index ["system_tester_scenario_id"], name: "index_st_st_scenario_id" 28 | t.index ["system_tester_step_id"], name: "index_system_tester_scenario_steps_on_system_tester_step_id" 29 | end 30 | 31 | create_table "system_tester_scenarios", force: :cascade do |t| 32 | t.string "title" 33 | t.integer "system_tester_feature_id" 34 | t.datetime "created_at", null: false 35 | t.datetime "updated_at", null: false 36 | t.index ["system_tester_feature_id"], name: "index_system_tester_scenarios_on_system_tester_feature_id" 37 | end 38 | 39 | create_table "system_tester_stair_steps", force: :cascade do |t| 40 | t.integer "position" 41 | t.integer "system_tester_step_id" 42 | t.integer "system_tester_stair_id" 43 | t.datetime "created_at", null: false 44 | t.datetime "updated_at", null: false 45 | t.index ["system_tester_stair_id"], name: "index_system_tester_stair_steps_on_system_tester_stair_id" 46 | t.index ["system_tester_step_id"], name: "index_system_tester_stair_steps_on_system_tester_step_id" 47 | end 48 | 49 | create_table "system_tester_steps", force: :cascade do |t| 50 | t.string "title" 51 | t.string "type" 52 | t.string "arg_one" 53 | t.string "arg_two" 54 | t.datetime "created_at", null: false 55 | t.datetime "updated_at", null: false 56 | end 57 | 58 | end 59 | -------------------------------------------------------------------------------- /app/models/system_tester/step.rb: -------------------------------------------------------------------------------- 1 | module SystemTester 2 | class Step < ApplicationRecord 3 | # include TitleValidatable 4 | validates_presence_of :title 5 | validates_uniqueness_of :title, scope: [:type, :arg_one, :arg_two] 6 | 7 | INDENT = " " * 6 8 | has_many :scenario_steps, 9 | class_name: "SystemTester::ScenarioStep", 10 | foreign_key: "system_tester_step_id", 11 | dependent: :destroy 12 | has_many :scenarios, through: :scenario_steps 13 | 14 | has_many :stair_steps, 15 | class_name: "SystemTester::StairStep", 16 | foreign_key: "system_tester_step_id", 17 | dependent: :destroy 18 | has_many :stairs, through: :stair_steps 19 | 20 | after_commit do 21 | stairs.each do |stair| 22 | stair.touch 23 | end 24 | scenarios.each do |scenario| 25 | scenario.feature.touch if scenario.feature.present? 26 | end 27 | end 28 | 29 | def self.friendly_type 30 | name.demodulize 31 | end 32 | 33 | def self.parent_type 34 | ancestors.fourth.name ? ancestors.fourth.name.demodulize : "Step" 35 | end 36 | 37 | def self.bg_css 38 | "teal" 39 | end 40 | 41 | def self.text_css 42 | "white-text" 43 | end 44 | 45 | def friendly_type 46 | self.class.friendly_type 47 | end 48 | 49 | def parent_type 50 | self.class.parent_type 51 | end 52 | 53 | def bg_css 54 | self.class.bg_css 55 | end 56 | 57 | def text_css 58 | self.class.text_css 59 | end 60 | 61 | def module 62 | "" 63 | end 64 | 65 | def self.args 66 | [ 67 | { 68 | name: 'arg_one', 69 | label: 'One', 70 | type: 'text' 71 | }, 72 | { 73 | name: 'arg_two', 74 | label: 'Two', 75 | type: 'text' 76 | } 77 | ] 78 | end 79 | 80 | def self.leafs 81 | (descendants - direct_descendants).map do |desc| 82 | { 83 | name: desc.to_s, 84 | friendly: desc.friendly_type, 85 | parent: desc.parent_type, 86 | args: desc.args 87 | } 88 | end.sort_by { |step_type| [step_type[:parent_type], step_type[:friendly]] } 89 | end 90 | 91 | def self.parent_types 92 | direct_descendants.map do |desc| 93 | { 94 | name: desc.to_s, 95 | friendly: desc.friendly_type 96 | } 97 | end.sort_by { |step_type| step_type[:friendly] } 98 | end 99 | 100 | def commented_title 101 | title.split("\n").map { |line| "# #{line}" }.join("\n")[2..-1] 102 | end 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /test/dummy/db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 20170629120850) do 14 | 15 | create_table "examples", force: :cascade do |t| 16 | t.string "title" 17 | t.text "comment" 18 | t.datetime "created_at", null: false 19 | t.datetime "updated_at", null: false 20 | end 21 | 22 | create_table "system_tester_features", force: :cascade do |t| 23 | t.string "title" 24 | t.datetime "created_at", null: false 25 | t.datetime "updated_at", null: false 26 | end 27 | 28 | create_table "system_tester_scenario_steps", force: :cascade do |t| 29 | t.integer "position" 30 | t.integer "system_tester_step_id" 31 | t.integer "system_tester_scenario_id" 32 | t.datetime "created_at", null: false 33 | t.datetime "updated_at", null: false 34 | t.index ["system_tester_scenario_id"], name: "index_st_st_scenario_id" 35 | t.index ["system_tester_step_id"], name: "index_system_tester_scenario_steps_on_system_tester_step_id" 36 | end 37 | 38 | create_table "system_tester_scenarios", force: :cascade do |t| 39 | t.string "title" 40 | t.integer "system_tester_feature_id" 41 | t.datetime "created_at", null: false 42 | t.datetime "updated_at", null: false 43 | t.index ["system_tester_feature_id"], name: "index_system_tester_scenarios_on_system_tester_feature_id" 44 | end 45 | 46 | create_table "system_tester_stair_steps", force: :cascade do |t| 47 | t.integer "position" 48 | t.integer "system_tester_step_id" 49 | t.integer "system_tester_stair_id" 50 | t.datetime "created_at", null: false 51 | t.datetime "updated_at", null: false 52 | t.index ["system_tester_stair_id"], name: "index_system_tester_stair_steps_on_system_tester_stair_id" 53 | t.index ["system_tester_step_id"], name: "index_system_tester_stair_steps_on_system_tester_step_id" 54 | end 55 | 56 | create_table "system_tester_steps", force: :cascade do |t| 57 | t.string "title" 58 | t.string "type" 59 | t.string "arg_one" 60 | t.string "arg_two" 61 | t.datetime "created_at", null: false 62 | t.datetime "updated_at", null: false 63 | end 64 | 65 | end 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/rlafranchi/system_tester.svg?branch=master)](https://travis-ci.org/rlafranchi/system_tester) 2 | [![Code Climate](https://codeclimate.com/github/rlafranchi/system_tester/badges/gpa.svg)](https://codeclimate.com/github/rlafranchi/system_tester) 3 | [![Test Coverage](https://codeclimate.com/github/rlafranchi/system_tester/badges/coverage.svg)](https://codeclimate.com/github/rlafranchi/system_tester/coverage) 4 | # System Tester 5 | A development tool for creating and managing system tests in Ruby on Rails >= 5.1 applications 6 | 7 | ## Usage 8 | This gem is used only in development and is a mountable rails engine that creates JSON endpoints for a chrome extension. 9 | 10 | ## Installation 11 | Add this line to your application's Gemfile: 12 | 13 | ```ruby 14 | gem 'system_tester', group: :development 15 | ``` 16 | 17 | Add the system_tester db to `config/database.yml` 18 | ```yml 19 | # you may want to consider a shared MySQL/Postgres database 20 | # if you have a team that needs shared access 21 | system_tester: 22 | <<: *default 23 | database: db/system_tester.sqlite3 24 | ``` 25 | 26 | And then install and run the migrations: 27 | ```bash 28 | $ bundle install 29 | $ rails system_tester:migrate 30 | ``` 31 | 32 | Mount the engine in config/routes.rb: 33 | ```ruby 34 | Rails.application.routes.draw do 35 | # ... other routes 36 | 37 | # Mount in development environment only 38 | if Rails.env.development? 39 | mount SystemTester::Engine => "/system_tester" 40 | end 41 | end 42 | ``` 43 | 44 | Start your dev server and you are good to go. 45 | 46 | ## Chrome extension 47 | [![System Tester Demo](https://img.youtube.com/vi/bxHQKaIT7iM/0.jpg)](https://youtu.be/bxHQKaIT7iM) 48 | 49 | The chrome extension provides a Devtools tab and acts as a client to this gem. 50 | 51 | **[ADD TO CHROME](https://chrome.google.com/webstore/detail/system-tester/ebpjncfolmfiiphibdajgblbchkklbcf)** 52 | 53 | ## Design 54 | System Tester is designed to work with a chrome extension, but could potentially be used for other applications as well. 55 | Run `rails routes` to see what endpoints are provided. The following design was set in place for the easy of reusing 56 | testing steps: 57 | 58 | * Features - the top level: one test script for a feature 59 | * Scenarios - 1:M assocation from Feature to Scenario. A Scenario is one test method. 60 | * Steps - Basically a line of code that can be an action or an assertion. STI is used to provide different step types. 61 | * ScenarioSteps - M:M assocation for Steps and Scenarios which also as a position attribute to allow for reordering. 62 | * Stairs - an ordered group of steps that can be reused. 63 | * StairSteps - M:M association to support stairs, Stair inherits from Step so the fk points back to the same table. 64 | 65 | ## License 66 | The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 67 | -------------------------------------------------------------------------------- /test/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Attempt to read encrypted secrets from `config/secrets.yml.enc`. 18 | # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or 19 | # `config/secrets.yml.key`. 20 | config.read_encrypted_secrets = true 21 | 22 | # Disable serving static files from the `/public` folder by default since 23 | # Apache or NGINX already handles this. 24 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 25 | 26 | # Compress JavaScripts and CSS. 27 | config.assets.js_compressor = :uglifier 28 | # config.assets.css_compressor = :sass 29 | 30 | # Do not fallback to assets pipeline if a precompiled asset is missed. 31 | config.assets.compile = false 32 | 33 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 34 | 35 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 36 | # config.action_controller.asset_host = 'http://assets.example.com' 37 | 38 | # Specifies the header that your server uses for sending files. 39 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 40 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 41 | 42 | # Mount Action Cable outside main process or domain 43 | # config.action_cable.mount_path = nil 44 | # config.action_cable.url = 'wss://example.com/cable' 45 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 46 | 47 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 48 | # config.force_ssl = true 49 | 50 | # Use the lowest log level to ensure availability of diagnostic information 51 | # when problems arise. 52 | config.log_level = :debug 53 | 54 | # Prepend all log lines with the following tags. 55 | config.log_tags = [ :request_id ] 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Use a real queuing backend for Active Job (and separate queues per environment) 61 | # config.active_job.queue_adapter = :resque 62 | # config.active_job.queue_name_prefix = "dummy_#{Rails.env}" 63 | config.action_mailer.perform_caching = false 64 | 65 | # Ignore bad email addresses and do not raise email delivery errors. 66 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 67 | # config.action_mailer.raise_delivery_errors = false 68 | 69 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 70 | # the I18n.default_locale when a translation cannot be found). 71 | config.i18n.fallbacks = true 72 | 73 | # Send deprecation notices to registered listeners. 74 | config.active_support.deprecation = :notify 75 | 76 | # Use default logging formatter so that PID and timestamp are not suppressed. 77 | config.log_formatter = ::Logger::Formatter.new 78 | 79 | # Use a different logger for distributed setups. 80 | # require 'syslog/logger' 81 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 82 | 83 | if ENV["RAILS_LOG_TO_STDOUT"].present? 84 | logger = ActiveSupport::Logger.new(STDOUT) 85 | logger.formatter = config.log_formatter 86 | config.logger = ActiveSupport::TaggedLogging.new(logger) 87 | end 88 | 89 | # Do not dump schema after migrations. 90 | config.active_record.dump_schema_after_migration = false 91 | end 92 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | system_tester (0.3.0) 5 | acts_as_list (~> 0.9.0) 6 | rack-cors 7 | rails (~> 5.1.0) 8 | 9 | GEM 10 | remote: https://rubygems.org/ 11 | specs: 12 | actioncable (5.1.2) 13 | actionpack (= 5.1.2) 14 | nio4r (~> 2.0) 15 | websocket-driver (~> 0.6.1) 16 | actionmailer (5.1.2) 17 | actionpack (= 5.1.2) 18 | actionview (= 5.1.2) 19 | activejob (= 5.1.2) 20 | mail (~> 2.5, >= 2.5.4) 21 | rails-dom-testing (~> 2.0) 22 | actionpack (5.1.2) 23 | actionview (= 5.1.2) 24 | activesupport (= 5.1.2) 25 | rack (~> 2.0) 26 | rack-test (~> 0.6.3) 27 | rails-dom-testing (~> 2.0) 28 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 29 | actionview (5.1.2) 30 | activesupport (= 5.1.2) 31 | builder (~> 3.1) 32 | erubi (~> 1.4) 33 | rails-dom-testing (~> 2.0) 34 | rails-html-sanitizer (~> 1.0, >= 1.0.3) 35 | activejob (5.1.2) 36 | activesupport (= 5.1.2) 37 | globalid (>= 0.3.6) 38 | activemodel (5.1.2) 39 | activesupport (= 5.1.2) 40 | activerecord (5.1.2) 41 | activemodel (= 5.1.2) 42 | activesupport (= 5.1.2) 43 | arel (~> 8.0) 44 | activesupport (5.1.2) 45 | concurrent-ruby (~> 1.0, >= 1.0.2) 46 | i18n (~> 0.7) 47 | minitest (~> 5.1) 48 | tzinfo (~> 1.1) 49 | acts_as_list (0.9.7) 50 | activerecord (>= 3.0) 51 | arel (8.0.0) 52 | builder (3.2.3) 53 | byebug (9.0.6) 54 | codeclimate-test-reporter (1.0.8) 55 | simplecov (<= 0.13) 56 | concurrent-ruby (1.0.5) 57 | docile (1.1.5) 58 | erubi (1.6.1) 59 | fabrication (2.16.1) 60 | faker (1.7.3) 61 | i18n (~> 0.5) 62 | globalid (0.4.0) 63 | activesupport (>= 4.2.0) 64 | i18n (0.8.4) 65 | json (2.1.0) 66 | loofah (2.0.3) 67 | nokogiri (>= 1.5.9) 68 | mail (2.6.6) 69 | mime-types (>= 1.16, < 4) 70 | metaclass (0.0.4) 71 | method_source (0.8.2) 72 | mime-types (3.1) 73 | mime-types-data (~> 3.2015) 74 | mime-types-data (3.2016.0521) 75 | mini_portile2 (2.2.0) 76 | minitest (5.10.2) 77 | mocha (1.2.1) 78 | metaclass (~> 0.0.1) 79 | nio4r (2.1.0) 80 | nokogiri (1.8.0) 81 | mini_portile2 (~> 2.2.0) 82 | rack (2.0.3) 83 | rack-cors (0.4.1) 84 | rack-test (0.6.3) 85 | rack (>= 1.0) 86 | rails (5.1.2) 87 | actioncable (= 5.1.2) 88 | actionmailer (= 5.1.2) 89 | actionpack (= 5.1.2) 90 | actionview (= 5.1.2) 91 | activejob (= 5.1.2) 92 | activemodel (= 5.1.2) 93 | activerecord (= 5.1.2) 94 | activesupport (= 5.1.2) 95 | bundler (>= 1.3.0, < 2.0) 96 | railties (= 5.1.2) 97 | sprockets-rails (>= 2.0.0) 98 | rails-dom-testing (2.0.3) 99 | activesupport (>= 4.2.0) 100 | nokogiri (>= 1.6) 101 | rails-html-sanitizer (1.0.3) 102 | loofah (~> 2.0) 103 | railties (5.1.2) 104 | actionpack (= 5.1.2) 105 | activesupport (= 5.1.2) 106 | method_source 107 | rake (>= 0.8.7) 108 | thor (>= 0.18.1, < 2.0) 109 | rake (12.0.0) 110 | simplecov (0.13.0) 111 | docile (~> 1.1.0) 112 | json (>= 1.8, < 3) 113 | simplecov-html (~> 0.10.0) 114 | simplecov-html (0.10.1) 115 | sprockets (3.7.1) 116 | concurrent-ruby (~> 1.0) 117 | rack (> 1, < 3) 118 | sprockets-rails (3.2.0) 119 | actionpack (>= 4.0) 120 | activesupport (>= 4.0) 121 | sprockets (>= 3.0.0) 122 | sqlite3 (1.3.13) 123 | thor (0.19.4) 124 | thread_safe (0.3.6) 125 | tzinfo (1.2.3) 126 | thread_safe (~> 0.1) 127 | websocket-driver (0.6.5) 128 | websocket-extensions (>= 0.1.0) 129 | websocket-extensions (0.1.2) 130 | 131 | PLATFORMS 132 | ruby 133 | 134 | DEPENDENCIES 135 | byebug 136 | codeclimate-test-reporter (~> 1.0.0) 137 | fabrication 138 | faker 139 | mocha 140 | rack-cors 141 | simplecov 142 | sqlite3 143 | system_tester! 144 | 145 | BUNDLED WITH 146 | 1.15.1 147 | -------------------------------------------------------------------------------- /test/integration/system_tester/steps_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module SystemTester 4 | class StepsControllerTest < ActionDispatch::IntegrationTest 5 | def setup 6 | @click = Fabricate(:step, type: 'SystemTester::ClickOn') 7 | @fill = Fabricate(:step, type: 'SystemTester::FillIn') 8 | @visit = Fabricate(:step, type: 'SystemTester::Visit') 9 | @select = Fabricate(:step, type: 'SystemTester::Select') 10 | @assert_selector = Fabricate(:step, type: 'SystemTester::AssertSelector') 11 | @assert_text = Fabricate(:step, type: 'SystemTester::AssertText') 12 | @stair = Fabricate(:stair) 13 | # just test one step 14 | @step = @click 15 | end 16 | 17 | test "#index" do 18 | get '/system_tester/steps' 19 | step_from_response = get_step(JSON.parse(response.body)) 20 | assert_match "click_on", step_from_response["to_s"] 21 | assert_response :success 22 | end 23 | 24 | test "#show" do 25 | get step_path 26 | assert_response :success 27 | end 28 | 29 | # TODO new endopint not being used by client 30 | # the intention is to preview the code before saving 31 | test "#new" do 32 | get '/system_tester/steps/new', params: {step: {title: "happy", type: "SystemTester::ClickOn"}} 33 | assert_response :success 34 | end 35 | 36 | test "#create" do 37 | assert_difference 'SystemTester::Step.count', 1 do 38 | post '/system_tester/steps', params: step_params 39 | end 40 | assert_response :success 41 | end 42 | 43 | test "#create invalid" do 44 | post '/system_tester/steps', params: step_params("") 45 | assert JSON.parse(response.body)["errors"]["title"].present? 46 | assert_response :unprocessable_entity 47 | end 48 | 49 | test "#create stair" do 50 | assert_difference 'SystemTester::Step.count', 1 do 51 | post '/system_tester/steps', params: step_params(nil, 'SystemTester::Stair') 52 | end 53 | assert_response :success 54 | end 55 | 56 | test "#update" do 57 | new_title = Faker::Lorem.words(2).join(" ") 58 | put step_path, params: step_params(new_title) 59 | assert_equal new_title, @step.reload.title 60 | assert_response :success 61 | end 62 | 63 | test "#update stair" do 64 | new_title = "New Stair Title" 65 | put stair_path, params: step_params(new_title, @stair.type) 66 | assert_equal new_title, @stair.reload.title 67 | assert File.exist?(Rails.root.join("test", "support", "system_tester", "new_stair_title.rb")) 68 | assert_response :success 69 | end 70 | 71 | test "#update one connected to a step and scenario" do 72 | Fabricate(:scenario_step, step: @step) 73 | Fabricate(:stair_step, step: @step) 74 | new_title = Faker::Lorem.words(2).join(" ") 75 | put step_path, params: step_params(new_title) 76 | assert_equal new_title, @step.reload.title 77 | assert_response :success 78 | end 79 | 80 | test "#update invalid" do 81 | put step_path, params: step_params("") 82 | assert JSON.parse(response.body)["errors"]["title"].present? 83 | assert_response :unprocessable_entity 84 | end 85 | 86 | test "#destroy" do 87 | assert_difference 'SystemTester::Step.count', -1 do 88 | delete step_path 89 | end 90 | assert_response :success 91 | end 92 | 93 | test "#destroy stair" do 94 | assert_difference 'SystemTester::Stair.count', -1 do 95 | delete stair_path 96 | end 97 | assert_response :success 98 | end 99 | 100 | private 101 | 102 | def step_path 103 | "/system_tester/steps/#{@step.id}" 104 | end 105 | 106 | def stair_path 107 | "/system_tester/steps/#{@stair.id}" 108 | end 109 | 110 | def step_params(title=nil, type=nil) 111 | title = title || Faker::Lorem.words(2).join(" ") 112 | { 113 | step: { 114 | title: title, 115 | type: type || @step.type, 116 | arg_one: @step.arg_one, 117 | arg_two: @step.arg_two 118 | } 119 | } 120 | end 121 | 122 | def get_step(response) 123 | response.select { |f| f["id"] == @step.id }.first 124 | end 125 | end 126 | end --------------------------------------------------------------------------------