totally-awesome-tests 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a9def027932d8353e3fee64c8eff9abd74c5d485
4
+ data.tar.gz: 33a7b447b9b847d0ae3da6d24fd257b216887891
5
+ SHA512:
6
+ metadata.gz: 74420050ea6ded62946cfa13bd56f7d382e7cf689d29018cebf47ee2fe96dce51f71b1ffb6b0c3bd5f8908e77065d85e7b527161f868f11e9ec3b08f07cd9938
7
+ data.tar.gz: d57d4b5c084fb63a2ed63f3ddd5d1eeb9501b90c415d2e54403acb080a0c3e3881893051c418315ec811b6967bdc1e09854438c14a42240e059d889b8e8d8794
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in totally-awesome-tests.gemspec
4
+ gemspec
@@ -0,0 +1,35 @@
1
+ # Totess
2
+
3
+ Like \*Test but totally awesome.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'totally-awesome-tests'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install totally-awesome-tests
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Development
26
+
27
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
28
+
29
+ ## Contributing
30
+
31
+ 1. Fork it ( https://github.com/MadRabbit/totally-awesome-tests/fork )
32
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
33
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
34
+ 4. Push to the branch (`git push origin my-new-feature`)
35
+ 5. Create a new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "totally-awesome-tests"
5
+
6
+ Totes::Cli.new ARGV.dup
@@ -0,0 +1,2 @@
1
+ # rubygem hook
2
+ require_relative "./totes"
@@ -0,0 +1,18 @@
1
+ module Totes
2
+ dir = File.dirname(__FILE__)
3
+
4
+ autoload :VERSION, "#{dir}/totes/version"
5
+ autoload :Cli, "#{dir}/totes/cli"
6
+ autoload :Resolver, "#{dir}/totes/resolver"
7
+ autoload :Runner, "#{dir}/totes/runner"
8
+ autoload :Spec, "#{dir}/totes/spec"
9
+ autoload :Noop, "#{dir}/totes/noop"
10
+ autoload :Subject, "#{dir}/totes/subject"
11
+ autoload :Matcher, "#{dir}/totes/matcher"
12
+ autoload :Query, "#{dir}/totes/query"
13
+ autoload :Error, "#{dir}/totes/error"
14
+ autoload :Reporter, "#{dir}/totes/reporter"
15
+ autoload :Backtrace, "#{dir}/totes/backtrace"
16
+ end
17
+
18
+ require_relative "./totes/kernel"
@@ -0,0 +1,5 @@
1
+ #
2
+ # The standard autorun hook
3
+ #
4
+
5
+ at_exit { Totes::Runner.start }
@@ -0,0 +1,14 @@
1
+ module Totes
2
+ module Backtrace
3
+ #
4
+ # Filters the backtrace on an error
5
+ #
6
+ def self.filter(error)
7
+ error.set_backtrace error.backtrace.map { |line|
8
+ unless line =~ /totally-awesome-tests\/lib\/totes\/[a-z_]+\.rb/
9
+ line.gsub! /:in `block.+?$/, ""
10
+ end
11
+ }.compact
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,40 @@
1
+ module Totes
2
+ class Cli
3
+ def initialize(args)
4
+ sources = parse_args(args)
5
+
6
+ require_relative "./autorun"
7
+ Totes::Resolver.new(sources).require_all
8
+ end
9
+
10
+ private
11
+
12
+ def parse_args(args)
13
+ [].tap do |sources|
14
+ while arg = args.shift
15
+ case arg
16
+ when '-h', '--help' then print_help
17
+ else sources << arg
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def print_help
24
+ print_nicely_and_exit %Q{
25
+ Totally awesome tests usage:
26
+
27
+ totes [path ...] [OPTIONS]
28
+
29
+ OPTIONS:
30
+ -h, --help # show this help
31
+ }
32
+ end
33
+
34
+ def print_nicely_and_exit(string)
35
+ print string.gsub(string[/\A(\n +)/], "\n").strip + "\n\n"
36
+ exit
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ module Totes
2
+ class Error < StandardError
3
+
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ module Kernel
2
+ # the top level `describe` blocks
3
+ def describe(*args, &block)
4
+ Totes::Runner.specs << Totes::Spec.new(*args, &block)
5
+ end
6
+ end
@@ -0,0 +1,75 @@
1
+ module Totes
2
+ class Matcher
3
+ dir = File.dirname(__FILE__)
4
+
5
+ autoload :Be, "#{dir}/matcher/be"
6
+ autoload :BeA, "#{dir}/matcher/be_a"
7
+ autoload :Eql, "#{dir}/matcher/eql"
8
+ autoload :RespondTo, "#{dir}/matcher/respond_to"
9
+ autoload :BeAnything, "#{dir}/matcher/be_anything"
10
+
11
+ ALIASES = {
12
+ eq: "eql",
13
+ be_an: "be_a",
14
+ :== => "eql"
15
+ }
16
+
17
+ def self.build(name, *args, &block)
18
+ name = ALIASES[name.to_sym] || name.to_s
19
+ klass = name.gsub(/(\A|_)([a-z])/){ $2.upcase }
20
+ klass = const_get(klass) rescue nil
21
+
22
+ if !klass && name =~ /^be_[a-z]/
23
+ klass = BeAnything
24
+ args.unshift name.gsub(/^be_/, "")
25
+ end
26
+
27
+ klass && klass.new(*args, &block)
28
+ end
29
+
30
+ def self.error_must(template=nil)
31
+ template ? (@error_must = template) : @error_must
32
+ end
33
+
34
+ def self.error_wont(template=nil)
35
+ template ? (@error_wont = template) : @error_wont
36
+ end
37
+
38
+ def initialize(value)
39
+ @value = value
40
+ end
41
+
42
+ def test(subject, fail_on: false)
43
+ if likes(subject) == fail_on
44
+ fail(subject, positive: !fail_on)
45
+ end
46
+ end
47
+
48
+ def likes(subject)
49
+ raise "#{self.class.name}#likes(subject) is not implemented yet"
50
+ end
51
+
52
+ private
53
+
54
+ def fail(subject, positive: true)
55
+ super Totes::Error, build_error(subject, positive)
56
+ end
57
+
58
+ def build_error(subject, positive)
59
+ template = positive ? self.class.error_must : self.class.error_wont
60
+ template % {subject: subject.inspect}.merge(find_params_in(template))
61
+ end
62
+
63
+ def find_params_in(template)
64
+ {}.tap do |params|
65
+ template.scan(/%\{(.+?)\}/).each do |match|
66
+ var_name = match[0].to_sym
67
+
68
+ if instance_variable_defined?("@#{var_name}")
69
+ params[var_name] = instance_variable_get("@#{var_name}").inspect
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,10 @@
1
+ module Totes
2
+ class Matcher::Be < Totes::Matcher
3
+ error_must "expected %{subject} to be the same as %{value}"
4
+ error_wont "expected %{subject} not to be the same as %{value}"
5
+
6
+ def likes(subject)
7
+ subject === @value
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Totes
2
+ class Matcher::BeA < Totes::Matcher
3
+ error_must "expected %{subject} to be an instance of %{value}"
4
+ error_wont "expected %{subject} not to be an instance of %{value}"
5
+
6
+ def likes(subject)
7
+ subject.is_a?(@value)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ module Totes
2
+ class Totes::Matcher::BeAnything < Totes::Matcher
3
+ error_must "expected %{subject}#%{method} to be true, instead have %{result}"
4
+ error_wont "expected %{subject}#%{method} to be false, instead have %{result}"
5
+
6
+ def initialize(method_name, *args, &block)
7
+ @method = method_name =~ /\?$/ ? method_name : "#{method_name}?"
8
+ @args = args
9
+ @block = block
10
+ end
11
+
12
+ def likes(subject)
13
+ @result = subject.__send__ @method, *@args, &@block
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ module Totes
2
+ class Matcher::Eql < Totes::Matcher
3
+ error_must "expected %{subject} to be equal %{value}"
4
+ error_wont "expected %{subject} not to be equal %{value}"
5
+
6
+ def likes(subject)
7
+ # TODO make a deep matching thing work
8
+ subject == @value
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module Totes
2
+ class Totes::Matcher::RespondTo < Totes::Matcher
3
+ error_must "expected %{subject} to have method '%{value}', but it ain't have one"
4
+ error_wont "expected %{subject} not to have method '%{value}', but it got one"
5
+
6
+ def likes(subject)
7
+ subject.respond_to?(@value)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ #
2
+ # This is a completely stripped down base class
3
+ #
4
+ module Totes
5
+ class Noop
6
+ Class.new.instance_methods.each do |core_method|
7
+ define_method core_method do |*args, &block|
8
+ method_missing core_method, *args, &block
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,55 @@
1
+ #
2
+ # Represents the subject query (the left side of the assertions)
3
+ #
4
+ module Totes
5
+ class Query < Noop
6
+ def initialize(subject)
7
+ @subject = Totes::Subject.new(subject)
8
+ end
9
+
10
+ def must(*args, &block)
11
+ if matcher = args[0]
12
+ __try__ { matcher.test(@subject, fail_on: false) }
13
+ else
14
+ OperationsWrapper.new(self, :must)
15
+ end
16
+ end
17
+
18
+ def wont(*args, &block)
19
+ if matcher = args[0]
20
+ __try__ { matcher.test(@subject, fail_on: true) }
21
+ else
22
+ OperationsWrapper.new(self, :wont)
23
+ end
24
+ end
25
+
26
+ def method_missing(name, *args, &block)
27
+ Totes::Query.new(@subject.__send__ name, *args, &block)
28
+ end
29
+
30
+ private
31
+
32
+ def __try__(&block)
33
+ yield
34
+
35
+ Totes::Reporter.inst.passed self
36
+ rescue Totes::Error => e
37
+ Totes::Backtrace.filter e
38
+ Totes::Reporter.inst.failed e
39
+ end
40
+
41
+ # the naughty operations matchers wrapper
42
+ class OperationsWrapper
43
+ def initialize(query, method)
44
+ @query = query
45
+ @method = method
46
+ end
47
+
48
+ [:==, :"!=", :=~, :<=, :>=, :<, :>].each do |operation|
49
+ define_method operation do |*args, &block|
50
+ @query.__send__ @method, Totes::Matcher.build(operation, *args, &block)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,47 @@
1
+ module Totes
2
+ class Reporter
3
+ def self.inst
4
+ @inst ||= new
5
+ end
6
+
7
+ def reset
8
+ @tests = 0
9
+ @fails = []
10
+ @time = Time.new
11
+ puts "Testing\n"
12
+ end
13
+
14
+ def passed(query)
15
+ @tests += 1
16
+ print "\e[32m.\e[0m"
17
+ end
18
+
19
+ def failed(error)
20
+ @tests += 1
21
+ @fails << error
22
+ print "\e[31mF\e[0m"
23
+ end
24
+
25
+ def summary
26
+ seconds = Time.new - @time
27
+ prefix = @fails.size == 0 ? "\e[32m✔︎ " : "\e[31m✖︎ "
28
+
29
+ puts "\n\n#{prefix}Tests: %d, Failed: %d \e[0m\e[;2m(%.1f seconds, %.1f tests/sec)\e[0m\n" % [
30
+ @tests, @fails.size, seconds, @tests / seconds
31
+ ]
32
+
33
+ print_failures
34
+ end
35
+
36
+ private
37
+
38
+ def print_failures
39
+ @fails.each do |error|
40
+ puts "\n\e[31mERROR: #{error.message}\e[0m"
41
+ error.backtrace.each do |line|
42
+ puts " \e[;2m#{line.gsub Dir.pwd+"/", ""}\e[0m"
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,41 @@
1
+ # Finds all the test files and requires all everything
2
+ module Totes
3
+ class Resolver
4
+ def initialize(sources)
5
+ @sources = sources
6
+ end
7
+
8
+ def require_all
9
+ add_to_load_path('lib', 'test')
10
+ require_spec_helpers
11
+
12
+ Dir[*clean_dirs_list].each do |filename|
13
+ require "#{Dir.pwd}/#{filename}"
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def add_to_load_path(*dirs)
20
+ dirs.each do |dir|
21
+ if File.exists?(dir) && File.directory?(dir)
22
+ $LOAD_PATH << "#{Dir.pwd}/#{dir}/"
23
+ end
24
+ end
25
+ end
26
+
27
+ def require_spec_helpers
28
+ test_helper_file = File.join(Dir.pwd, "test/test_helper.rb")
29
+ require test_helper_file if File.exists?(test_helper_file)
30
+ end
31
+
32
+ def clean_dirs_list
33
+ @sources = ['test'] if @sources.empty?
34
+
35
+ @sources.map do |name|
36
+ name = name.slice(0, name.size - 1) if name[name.size-1] == '/'
37
+ File.exists?(name) && File.directory?(name) ? "#{name}/**/*_test.rb" : name
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,40 @@
1
+ module Totes
2
+ class Runner
3
+ def self.specs
4
+ @specs ||= []
5
+ end
6
+
7
+ def self.start
8
+ new(specs).run
9
+ end
10
+
11
+ def initialize(specs)
12
+ @specs = specs
13
+ Totes::Reporter.inst.reset
14
+ end
15
+
16
+ def run
17
+ @specs.each { |spec| loop_through spec }
18
+ Totes::Reporter.inst.summary
19
+ rescue StandardError => e
20
+ Totes::Backtrace.filter e
21
+ raise e
22
+ end
23
+
24
+ private
25
+
26
+ def loop_through(spec)
27
+ exec [spec]
28
+ rescue Totes::Spec::StartOver => e
29
+ spec.instance_eval { @__count[:queries] = 0; @__specs = [] } # resetting the state
30
+ loop_through(spec)
31
+ end
32
+
33
+ def exec(specs)
34
+ specs.each do |spec|
35
+ spec.instance_eval &spec.instance_variable_get("@__block")
36
+ exec spec.instance_variable_get("@__specs")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,66 @@
1
+ module Totes
2
+ class Spec
3
+ attr_reader :subject
4
+
5
+ StartOver = Class.new(StandardError)
6
+
7
+ def initialize(*args, &block)
8
+ @subject = __get_subject__(*args)
9
+ @__block = block
10
+ @__specs = [] # inner specs list
11
+ @__count = {
12
+ queries: 0, # global queries counter
13
+ current: 0 # the current query marker
14
+ }
15
+ end
16
+
17
+ def describe(*args, &block)
18
+ @__specs << self.class.new(*args, &block).tap do |spec|
19
+ spec.instance_variable_set("@__count", @__count)
20
+ end
21
+ end
22
+
23
+ # returns the _subject_ query object
24
+ def it
25
+ if @__count[:current] == @__count[:queries]
26
+ @__count[:queries] += 1
27
+ Totes::Query.new(@subject) # going in for real
28
+ elsif @__count[:current] > @__count[:queries]
29
+ @__count[:queries] += 1
30
+ SkipQuery.new # rerunning through a previous query, skipping
31
+ else
32
+ @__count[:current] += 1 # switching to the next query
33
+ raise StartOver
34
+ end
35
+ end
36
+
37
+ alias :its :it # more readable on subqueries
38
+
39
+ # builds the matchers
40
+ def method_missing(*args, &block)
41
+ Totes::Matcher.build(*args, &block) || super(*args, &block)
42
+ end
43
+
44
+ private
45
+
46
+ def __get_subject__(*args)
47
+ return args[0] # TODO make it smarter
48
+ end
49
+
50
+ class SkipQuery
51
+ def must(*args); end # don't trigger the matchers
52
+ def wont(*args); end # don't trigger the matchers
53
+
54
+ def method_missing(*args, &block)
55
+ self # endlessly return self
56
+ end
57
+
58
+ # passing core methods (like :to_s and so on onto the subject as well)
59
+ (Class.new.instance_methods - [:__send__, :__id__, :object_id]).each do |core_method|
60
+ define_method core_method do |*args, &block|
61
+ method_missing core_method, *args, &block
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,15 @@
1
+ #
2
+ # This is a smarty pants wrapper around the subject values
3
+ #
4
+ module Totes
5
+ class Subject < Noop
6
+ def initialize(subject)
7
+ @subject = subject
8
+ end
9
+
10
+ def method_missing(name, *args, &block)
11
+ # puts "#{name.inspect}(#{args.map(&:inspect).join(",")})"
12
+ @subject.__send__ name, *args, &block
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module Totes
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'totes/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "totally-awesome-tests"
8
+ spec.version = Totes::VERSION
9
+ spec.authors = ["Nikolay Nemshilov"]
10
+ spec.email = ["nemshilov@gmail.com"]
11
+
12
+ spec.summary = %q{Like totally awesome testing framework}
13
+ spec.description = %q{Like totally awesome testing framework. Did I mention "totally" already?}
14
+ spec.homepage = "https://github.com/MadRabbit/totally-awesome-tests"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "bin"
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.9"
23
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: totally-awesome-tests
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nikolay Nemshilov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
27
+ description: Like totally awesome testing framework. Did I mention "totally" already?
28
+ email:
29
+ - nemshilov@gmail.com
30
+ executables:
31
+ - totes
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".gitignore"
36
+ - ".travis.yml"
37
+ - Gemfile
38
+ - README.md
39
+ - Rakefile
40
+ - bin/totes
41
+ - lib/totally-awesome-tests.rb
42
+ - lib/totes.rb
43
+ - lib/totes/autorun.rb
44
+ - lib/totes/backtrace.rb
45
+ - lib/totes/cli.rb
46
+ - lib/totes/error.rb
47
+ - lib/totes/kernel.rb
48
+ - lib/totes/matcher.rb
49
+ - lib/totes/matcher/be.rb
50
+ - lib/totes/matcher/be_a.rb
51
+ - lib/totes/matcher/be_anything.rb
52
+ - lib/totes/matcher/eql.rb
53
+ - lib/totes/matcher/respond_to.rb
54
+ - lib/totes/noop.rb
55
+ - lib/totes/query.rb
56
+ - lib/totes/reporter.rb
57
+ - lib/totes/resolver.rb
58
+ - lib/totes/runner.rb
59
+ - lib/totes/spec.rb
60
+ - lib/totes/subject.rb
61
+ - lib/totes/version.rb
62
+ - totally-awesome-tests.gemspec
63
+ homepage: https://github.com/MadRabbit/totally-awesome-tests
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 2.4.5
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: Like totally awesome testing framework
87
+ test_files: []