specimen 0.0.1.alpha.b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '091d4e836ac67953118af5d330c4254d7e29716b7cb3e2756369b45a0ba74a88'
4
+ data.tar.gz: 69249e8db11054399bbc70c2fdee9163b9cbac769ae79ed55182c61f4083448a
5
+ SHA512:
6
+ metadata.gz: 83f173e5ecfe205b8581e18851aed9de5e0e19f52c995790f53af3316d7e16238d8ff02104339c360caf0db07916347c157664c955522bc89b6ad2a6c28dbcbe
7
+ data.tar.gz: 0ab049182afb589bfe054d0325114cd0d3536c4ec9990df0e222bfd5ef8713e93b2e901dbf2ae534794e9fda09d5bd4a42a87e778d8142346ea6f1bd50d817fc
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # specimen
2
+
3
+ Doing the same things over and over again in test automation was the initial inspiration of coming up with a gem which
4
+ will help me with:
5
+
6
+ - setting up a new test automation project, including README, Gemfile, rubocop.yml, etc...
7
+ - run Cucumber and RSpec tests with configs that make sense (e.g. parallel execution, reporting)
8
+ - provide a Rails like templating approach in terms of _convention over configuration_ to provide a maintainable base for
9
+ API or UI tests using RSpec and/or Cucumber.
10
+
11
+
12
+ **Beware**
13
+
14
+ At the point of writing the README and setting the repo to public, the gem does not do much yet so the current functionality
15
+ is minimal, not optimized and not tested :). Hopefully that will change in the future.
16
+
17
+ ```shell
18
+ # install Specimen gem
19
+ $> gem install specimen
20
+
21
+ # creates a new project relative to the current working directory. Will ask for a project name.
22
+ $> specimen init
23
+
24
+ # creates a new project 'foobar' relative to the current working directory.
25
+ # Inside /foobar you will find the default dir structures for Cucumber and RSpec tests and a few more files
26
+ $> specimen init -n foobar
27
+
28
+ # look at the command help
29
+ $> specimen help init
30
+ Usage:
31
+ specimen init
32
+
33
+ Options:
34
+ --name, -n, [--project-name=PROJECT_NAME]
35
+ [--api-only], [--no-api-only], [--skip-api-only] # Default: false
36
+ [--cucumber], [--no-cucumber], [--skip-cucumber] # Default: true
37
+ ```
38
+
39
+
40
+ ### Known issues
41
+
42
+ - tests are missing
43
+ - generated Gemfile contains Watir and Selenium webdriver, it should be only one of them.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1.alpha.b
data/bin/specimen ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib_path = File.expand_path('..', __dir__) + '/lib'
4
+ $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
5
+
6
+ require 'specimen/cli'
7
+ Specimen::CLI.start!(ARGV.dup)
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'specimen/generator'
4
+
5
+ module Specimen
6
+ module CLI
7
+ class DefaultCommand < Commands::BaseCommand
8
+ include Generator
9
+
10
+ namespace :default
11
+
12
+ def self.banner(task, namespace = true, subcommand = false)
13
+ "#{basename} #{task.name}"
14
+ end
15
+
16
+ def self.source_root
17
+ File.dirname(__FILE__)
18
+ end
19
+
20
+ desc 'init', 'Initialize a new Specimen project'
21
+
22
+ method_option :project_name, aliases: %w[--name -n], type: :string, default: ''
23
+ method_option :api_only, type: :boolean, default: false
24
+ method_option :cucumber, type: :boolean, default: true
25
+
26
+ def init
27
+ SpecimenProject.start([options.dup])
28
+ end
29
+
30
+ desc 'commands', 'print all commands'
31
+
32
+ def commands
33
+ [DefaultCommand].each { |cmd| cmd.new.help }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'specimen'
4
+ require 'specimen/cli/default_command'
5
+
6
+ module Specimen
7
+ module CLI
8
+ class << self
9
+ def start!(args = ARGV)
10
+ command = args.shift
11
+
12
+ case command
13
+ when '--version', '-v'
14
+ show_version
15
+ else
16
+ DefaultCommand.start(ARGV.dup)
17
+ end
18
+ end
19
+
20
+ def show_version
21
+ puts(Specimen::VERSION::STRING)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Specimen
6
+ module Commands
7
+ class BaseCommand < Thor
8
+ include Thor::Actions
9
+
10
+ def self.banner(task, namespace = true, subcommand = false)
11
+ "#{basename} #{self.namespace} #{task.name}"
12
+ end
13
+ end
14
+
15
+ class BaseGroupCommand < Thor::Group
16
+ include Thor::Actions
17
+
18
+ def self.banner(task, namespace = true, subcommand = false)
19
+ "#{basename} #{self.namespace} #{task.name}"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hash
4
+ # Returns a new hash with all keys converted to strings.
5
+ #
6
+ # hash = { name: 'Rob', age: '28' }
7
+ #
8
+ # hash.stringify_keys
9
+ # # => {"name"=>"Rob", "age"=>"28"}
10
+ def stringify_keys
11
+ transform_keys(&:to_s)
12
+ end
13
+
14
+ # Destructively converts all keys to strings. Same as
15
+ # +stringify_keys+, but modifies +self+.
16
+ def stringify_keys!
17
+ transform_keys!(&:to_s)
18
+ end
19
+
20
+ # Returns a new hash with all keys converted to symbols, as long as
21
+ # they respond to +to_sym+.
22
+ #
23
+ # hash = { 'name' => 'Rob', 'age' => '28' }
24
+ #
25
+ # hash.symbolize_keys
26
+ # # => {:name=>"Rob", :age=>"28"}
27
+ def symbolize_keys
28
+ transform_keys { |key| key.to_sym rescue key }
29
+ end
30
+ alias_method :to_options, :symbolize_keys
31
+
32
+ # Destructively converts all keys to symbols, as long as they respond
33
+ # to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
34
+ def symbolize_keys!
35
+ transform_keys! { |key| key.to_sym rescue key }
36
+ end
37
+ alias_method :to_options!, :symbolize_keys!
38
+
39
+ # Validates all keys in a hash match <tt>*valid_keys</tt>, raising
40
+ # +ArgumentError+ on a mismatch.
41
+ #
42
+ # Note that keys are treated differently than HashWithIndifferentAccess,
43
+ # meaning that string and symbol keys will not match.
44
+ #
45
+ # { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age"
46
+ # { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: :name. Valid keys are: 'name', 'age'"
47
+ # { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
48
+ def assert_valid_keys(*valid_keys)
49
+ valid_keys.flatten!
50
+ each_key do |k|
51
+ unless valid_keys.include?(k)
52
+ raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}")
53
+ end
54
+ end
55
+ end
56
+
57
+ # Returns a new hash with all keys converted by the block operation.
58
+ # This includes the keys from the root hash and from all
59
+ # nested hashes and arrays.
60
+ #
61
+ # hash = { person: { name: 'Rob', age: '28' } }
62
+ #
63
+ # hash.deep_transform_keys{ |key| key.to_s.upcase }
64
+ # # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
65
+ def deep_transform_keys(&block)
66
+ _deep_transform_keys_in_object(self, &block)
67
+ end
68
+
69
+ # Destructively converts all keys by using the block operation.
70
+ # This includes the keys from the root hash and from all
71
+ # nested hashes and arrays.
72
+ def deep_transform_keys!(&block)
73
+ _deep_transform_keys_in_object!(self, &block)
74
+ end
75
+
76
+ # Returns a new hash with all keys converted to strings.
77
+ # This includes the keys from the root hash and from all
78
+ # nested hashes and arrays.
79
+ #
80
+ # hash = { person: { name: 'Rob', age: '28' } }
81
+ #
82
+ # hash.deep_stringify_keys
83
+ # # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
84
+ def deep_stringify_keys
85
+ deep_transform_keys(&:to_s)
86
+ end
87
+
88
+ # Destructively converts all keys to strings.
89
+ # This includes the keys from the root hash and from all
90
+ # nested hashes and arrays.
91
+ def deep_stringify_keys!
92
+ deep_transform_keys!(&:to_s)
93
+ end
94
+
95
+ # Returns a new hash with all keys converted to symbols, as long as
96
+ # they respond to +to_sym+. This includes the keys from the root hash
97
+ # and from all nested hashes and arrays.
98
+ #
99
+ # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
100
+ #
101
+ # hash.deep_symbolize_keys
102
+ # # => {:person=>{:name=>"Rob", :age=>"28"}}
103
+ def deep_symbolize_keys
104
+ deep_transform_keys { |key| key.to_sym rescue key }
105
+ end
106
+
107
+ # Destructively converts all keys to symbols, as long as they respond
108
+ # to +to_sym+. This includes the keys from the root hash and from all
109
+ # nested hashes and arrays.
110
+ def deep_symbolize_keys!
111
+ deep_transform_keys! { |key| key.to_sym rescue key }
112
+ end
113
+
114
+ private
115
+ # Support methods for deep transforming nested hashes and arrays.
116
+ def _deep_transform_keys_in_object(object, &block)
117
+ case object
118
+ when Hash
119
+ object.each_with_object(self.class.new) do |(key, value), result|
120
+ result[yield(key)] = _deep_transform_keys_in_object(value, &block)
121
+ end
122
+ when Array
123
+ object.map { |e| _deep_transform_keys_in_object(e, &block) }
124
+ else
125
+ object
126
+ end
127
+ end
128
+
129
+ def _deep_transform_keys_in_object!(object, &block)
130
+ case object
131
+ when Hash
132
+ object.keys.each do |key|
133
+ value = object.delete(key)
134
+ object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
135
+ end
136
+ object
137
+ when Array
138
+ object.map! { |e| _deep_transform_keys_in_object!(e, &block) }
139
+ else
140
+ object
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Specimen
4
+ module Generator
5
+ class FileByTemplate < Commands::BaseGroupCommand
6
+ argument :template_file, type: :string
7
+ argument :destination, type: :string
8
+ argument :data, type: :hash, default: {}
9
+
10
+ def self.source_root
11
+ "#{File.dirname(__FILE__)}/templates"
12
+ end
13
+
14
+ def create
15
+ template(template_file, destination, data)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'specimen_project_config'
4
+
5
+ module Specimen
6
+ module Generator
7
+ class SpecimenProject < Commands::BaseGroupCommand
8
+ include Generator
9
+
10
+ argument :project_options
11
+
12
+ def self.source_root
13
+ "#{File.dirname(__FILE__)}/templates"
14
+ end
15
+
16
+ def initialize(_, _, _)
17
+ super
18
+ @opts = project_options.to_h.deep_symbolize_keys
19
+ end
20
+
21
+ def start
22
+ say('Initialize new Specimen project', Color::BOLD)
23
+
24
+ @opts[:project_name].empty? ? ask_for_name : @opts[:project_name]
25
+ @opts[:destination_root] = destination_root
26
+ @config = SpecimenProjectConfig.new(@opts)
27
+ end
28
+
29
+ def execute
30
+ empty_directory(@config.project_name)
31
+ create_root_files
32
+
33
+ inside @config.project_root do
34
+ if @config.cucumber?
35
+ empty_directory('features/step_definitions')
36
+ empty_directory('features/support')
37
+ end
38
+
39
+ empty_directory('spec/support')
40
+ end
41
+ end
42
+
43
+ def finish
44
+ say("created new Specimen project in #{@config.project_root}", Color::GREEN)
45
+ end
46
+
47
+ no_commands do
48
+ def ask_for_name
49
+ @opts[:project_name] = ask('What is the name of the project?')
50
+ end
51
+
52
+ def create_root_files
53
+ project_root_files.each do |file|
54
+ dest = dot_files.include?(file) ? ".#{file}" : file
55
+ create_file_by_template(file, "#{@config.project_root}/#{dest}", @config.data)
56
+ end
57
+ end
58
+
59
+ def dot_files
60
+ %w[gemrc rbenv-gemsets rubocop.yml]
61
+ end
62
+
63
+ def project_root_files
64
+ %w[gemrc
65
+ rbenv-gemsets
66
+ rubocop.yml
67
+ Gemfile
68
+ README.md]
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Specimen
4
+ module Generator
5
+ class SpecimenProjectConfig
6
+ GEM_LIST = %w[
7
+ activesupport dotenv ffaker rest-client thor uuid
8
+ cucumber cuke_modeler parallel_tests rspec
9
+ selenium-webdriver watir
10
+ debug pry rubocop
11
+ ].freeze
12
+
13
+ def initialize(options)
14
+ @options = options
15
+ end
16
+
17
+ def data
18
+ @data ||= {
19
+ project_name: project_name,
20
+ gems: project_gems,
21
+ project_root: project_root,
22
+ api_only: api_only?,
23
+ cucumber: cucumber?
24
+ }
25
+ end
26
+
27
+ def project_gems
28
+ gems.reject! { |gem| gem.eql?('selenium-webdriver') || gem.eql?('watir') } if api_only?
29
+ gems.reject! { |gem| gem.eql?('cucumber') || gem.eql?('cuke_modeler') } unless cucumber?
30
+ gems.sort
31
+ end
32
+
33
+ def project_root
34
+ "#{@options[:destination_root]}/#{project_name}"
35
+ end
36
+
37
+ def project_name
38
+ @options[:project_name]
39
+ end
40
+
41
+ def gems
42
+ @gems ||= GEM_LIST.dup
43
+ end
44
+
45
+ def api_only?
46
+ @options[:api_only]
47
+ end
48
+
49
+ def cucumber?
50
+ @options[:cucumber]
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ <% data[:gems].sort.each do |gem| -%>
6
+ <% if gem == 'debug' || gem == 'pry' -%>
7
+ gem '<%= gem %>', require: false
8
+ <% else -%>
9
+ gem '<%= gem %>'
10
+ <% end -%>
11
+ <% end -%>
@@ -0,0 +1 @@
1
+ # <%= "#{data[:project_name]}" %>
@@ -0,0 +1 @@
1
+ gem: --no-rdoc --no-ri
@@ -0,0 +1 @@
1
+ <%= data[:project_name] %>-gems
@@ -0,0 +1,32 @@
1
+ Layout/LineLength:
2
+ Max: 120
3
+
4
+ Layout/SpaceInsideHashLiteralBraces:
5
+ EnforcedStyle: no_space
6
+
7
+ Metrics/AbcSize:
8
+ Enabled: false
9
+ CountRepeatedAttributes: true
10
+
11
+ Metrics/MethodLength:
12
+ Max: 25
13
+
14
+ Naming/AccessorMethodName:
15
+ Enabled: false
16
+
17
+ Style/Documentation:
18
+ Enabled: false
19
+
20
+ Style/EmptyCaseCondition:
21
+ Enabled: false
22
+
23
+ Style/HashSyntax:
24
+ EnforcedShorthandSyntax: either
25
+
26
+ Style/NumericLiterals:
27
+ Enabled: false
28
+
29
+ AllCops:
30
+ NewCops: enable
31
+ Exclude:
32
+ - '.irbrc'
@@ -0,0 +1 @@
1
+ project_name: 'Foo'
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'generator/file_by_template'
4
+ require_relative 'generator/specimen_project'
5
+
6
+ module Specimen
7
+ module Generator
8
+ def create_file_by_template(template, destination, data = {})
9
+ FileByTemplate.start([template, destination, data])
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Specimen
4
+ # Returns the currently loaded version of \Rails as a +Gem::Version+.
5
+ def self.gem_version
6
+ Gem::Version.new VERSION::STRING
7
+ end
8
+
9
+ module VERSION
10
+ MAJOR = 0
11
+ MINOR = 0
12
+ TINY = 1
13
+ PRE = "alpha.b"
14
+
15
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
+ end
17
+
18
+ end
data/lib/specimen.rb ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ # require Ruby extensions
6
+ require 'specimen/extensions/ruby/hash'
7
+
8
+ require 'specimen/version'
9
+
10
+ module Specimen
11
+ extend ActiveSupport::Autoload
12
+
13
+ autoload :CLI
14
+ autoload :Commands
15
+ autoload :Generator
16
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: specimen
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.alpha.b
5
+ platform: ruby
6
+ authors:
7
+ - Marek Witkowski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-06-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '7.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '7.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.14.2
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.14.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 13.2.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 13.2.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.64.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.64.1
83
+ description: Create maintainable automated tests using Rails-like COC approach
84
+ email: info@marekwitkowski.de
85
+ executables:
86
+ - specimen
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - README.md
91
+ - VERSION
92
+ - bin/specimen
93
+ - lib/specimen.rb
94
+ - lib/specimen/cli.rb
95
+ - lib/specimen/cli/default_command.rb
96
+ - lib/specimen/commands.rb
97
+ - lib/specimen/extensions/ruby/hash.rb
98
+ - lib/specimen/generator.rb
99
+ - lib/specimen/generator/file_by_template.rb
100
+ - lib/specimen/generator/specimen_project.rb
101
+ - lib/specimen/generator/specimen_project_config.rb
102
+ - lib/specimen/generator/templates/Gemfile.tt
103
+ - lib/specimen/generator/templates/README.md.tt
104
+ - lib/specimen/generator/templates/gemrc.tt
105
+ - lib/specimen/generator/templates/rbenv-gemsets.tt
106
+ - lib/specimen/generator/templates/rubocop.yml.tt
107
+ - lib/specimen/generator/templates/specimen/specimen.yml.tt
108
+ - lib/specimen/version.rb
109
+ homepage: https://github.com/dev-dots/specimen
110
+ licenses:
111
+ - MIT
112
+ metadata:
113
+ bug_tracker_uri: https://github.com/dev-dots/specimen/issues
114
+ rubygems_mfa_required: 'true'
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '3.0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: 3.0.1
129
+ requirements: []
130
+ rubygems_version: 3.5.11
131
+ signing_key:
132
+ specification_version: 4
133
+ summary: Specimen
134
+ test_files: []