rsel 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.gitignore +4 -0
  2. data/.yardopts +7 -0
  3. data/Gemfile +2 -0
  4. data/MIT-LICENSE +22 -0
  5. data/README.md +35 -0
  6. data/Rakefile +75 -0
  7. data/docs/custom.md +87 -0
  8. data/docs/development.md +48 -0
  9. data/docs/index.md +13 -0
  10. data/docs/install.md +33 -0
  11. data/docs/todo.md +15 -0
  12. data/docs/usage.md +50 -0
  13. data/lib/rsel.rb +4 -0
  14. data/lib/rsel/exceptions.rb +4 -0
  15. data/lib/rsel/selenium_test.rb +524 -0
  16. data/rsel.gemspec +29 -0
  17. data/spec/selenium_test_spec.rb +296 -0
  18. data/spec/spec_helper.rb +41 -0
  19. data/test/app.rb +34 -0
  20. data/test/config.ru +4 -0
  21. data/test/views/about.erb +9 -0
  22. data/test/views/form.erb +102 -0
  23. data/test/views/index.erb +15 -0
  24. data/vendor/rubyslim-0.1.1/.gitignore +7 -0
  25. data/vendor/rubyslim-0.1.1/README.md +159 -0
  26. data/vendor/rubyslim-0.1.1/Rakefile +21 -0
  27. data/vendor/rubyslim-0.1.1/bin/rubyslim +10 -0
  28. data/vendor/rubyslim-0.1.1/lib/rubyslim/list_deserializer.rb +69 -0
  29. data/vendor/rubyslim-0.1.1/lib/rubyslim/list_executor.rb +12 -0
  30. data/vendor/rubyslim-0.1.1/lib/rubyslim/list_serializer.rb +45 -0
  31. data/vendor/rubyslim-0.1.1/lib/rubyslim/ruby_slim.rb +61 -0
  32. data/vendor/rubyslim-0.1.1/lib/rubyslim/slim_error.rb +3 -0
  33. data/vendor/rubyslim-0.1.1/lib/rubyslim/slim_helper_library.rb +23 -0
  34. data/vendor/rubyslim-0.1.1/lib/rubyslim/socket_service.rb +53 -0
  35. data/vendor/rubyslim-0.1.1/lib/rubyslim/statement.rb +79 -0
  36. data/vendor/rubyslim-0.1.1/lib/rubyslim/statement_executor.rb +174 -0
  37. data/vendor/rubyslim-0.1.1/lib/rubyslim/table_to_hash_converter.rb +32 -0
  38. data/vendor/rubyslim-0.1.1/lib/test_module/library_new.rb +13 -0
  39. data/vendor/rubyslim-0.1.1/lib/test_module/library_old.rb +12 -0
  40. data/vendor/rubyslim-0.1.1/lib/test_module/should_not_find_test_slim_in_here/test_slim.rb +7 -0
  41. data/vendor/rubyslim-0.1.1/lib/test_module/simple_script.rb +9 -0
  42. data/vendor/rubyslim-0.1.1/lib/test_module/test_chain.rb +5 -0
  43. data/vendor/rubyslim-0.1.1/lib/test_module/test_slim.rb +86 -0
  44. data/vendor/rubyslim-0.1.1/lib/test_module/test_slim_with_arguments.rb +23 -0
  45. data/vendor/rubyslim-0.1.1/lib/test_module/test_slim_with_no_sut.rb +5 -0
  46. data/vendor/rubyslim-0.1.1/rubyslim.gemspec +22 -0
  47. data/vendor/rubyslim-0.1.1/spec/instance_creation_spec.rb +40 -0
  48. data/vendor/rubyslim-0.1.1/spec/it8f_spec.rb +8 -0
  49. data/vendor/rubyslim-0.1.1/spec/list_deserializer_spec.rb +66 -0
  50. data/vendor/rubyslim-0.1.1/spec/list_executor_spec.rb +187 -0
  51. data/vendor/rubyslim-0.1.1/spec/list_serialzer_spec.rb +38 -0
  52. data/vendor/rubyslim-0.1.1/spec/method_invocation_spec.rb +127 -0
  53. data/vendor/rubyslim-0.1.1/spec/slim_helper_library_spec.rb +80 -0
  54. data/vendor/rubyslim-0.1.1/spec/socket_service_spec.rb +98 -0
  55. data/vendor/rubyslim-0.1.1/spec/spec_helper.rb +6 -0
  56. data/vendor/rubyslim-0.1.1/spec/statement_executor_spec.rb +50 -0
  57. data/vendor/rubyslim-0.1.1/spec/statement_spec.rb +17 -0
  58. data/vendor/rubyslim-0.1.1/spec/table_to_hash_converter_spec.rb +31 -0
  59. metadata +241 -0
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head><title>Rsel Test Site</title></head>
4
+ <body>
5
+ <h1>Welcome</h1>
6
+ <p>This is a Sinatra webapp for unit testing Rsel.</p>
7
+
8
+ <div id="links">
9
+ <a href="/about">About this site</a>
10
+ <a href="/form">Form test</a>
11
+ </div>
12
+
13
+ </body>
14
+ </html>
15
+
@@ -0,0 +1,7 @@
1
+ .rakeTasks
2
+ *.iml
3
+ *.ipr
4
+ *.iws
5
+ .DS_Store
6
+ .idea
7
+
@@ -0,0 +1,159 @@
1
+ Ruby Slim Protocol (V0.1)
2
+ =========================
3
+
4
+ Setup
5
+ -----
6
+
7
+ Put these command in a parent of the Ruby test pages.
8
+
9
+ !define TEST_SYSTEM {slim}
10
+ !define TEST_RUNNER {<wherever you put Ruby Slim>/lib/run_ruby_slim.rb}
11
+ !define COMMAND_PATTERN {ruby -I %p %m}
12
+ !define PATH_SEPARATOR {-I}
13
+ !path <wherever you put Ruby Slim>/lib
14
+ !path <any directory where you have ruby fixtures>
15
+
16
+ Paths can be relative. You should put the following in an appropriate SetUp page:
17
+
18
+ !|import|
19
+ |<ruby module of fixtures>|
20
+
21
+ You can have as many rows in this table as you like, one for each module that
22
+ contains fixtures. Note that this needs to be the *name* of the module as
23
+ written in the Ruby code, not the filename where the module is defined.
24
+
25
+ Ruby slim works a lot like Java slim. We tried to use ruby method naming
26
+ conventions. So if you put this in a table:
27
+
28
+ |SomeDecisionTable|
29
+ |input|get output?|
30
+ |1 |2 |
31
+
32
+ Then it will call the `set_input` and `get_output` functions of the
33
+ `SomeDecisionTable` class.
34
+
35
+ The `SomeDecisionTable` class would be written in a file called
36
+ `some_decision_table.rb`, like this (the file name must correspond to the class
37
+ name defined within, and the module name must match the one you are importing):
38
+
39
+ module MyModule
40
+ class SomeDecisionTable
41
+ def set_input(input)
42
+ @x = input
43
+ end
44
+
45
+ def get_output
46
+ @x
47
+ end
48
+ end
49
+ end
50
+
51
+ Note that there is no type information for the arguments of these functions, so
52
+ they will all be treated as strings. This is important to remember! All
53
+ arguments are strings. If you are expecting integers, you will have to convert
54
+ the strings to integers within your fixtures.
55
+
56
+
57
+ Hashes
58
+ ------
59
+
60
+ There is one exception to the above rule. If you pass a HashWidget in a table,
61
+ then the argument will be converted to a Hash.
62
+
63
+ Consider, for example, this fixtures class:
64
+
65
+ module TestModule
66
+ class TestSlimWithArguments
67
+ def initialize(arg)
68
+ @arg = arg
69
+ end
70
+
71
+ def arg
72
+ @arg
73
+ end
74
+
75
+ def name
76
+ @arg[:name]
77
+ end
78
+
79
+ def addr
80
+ @arg[:addr]
81
+ end
82
+
83
+ def set_arg(hash)
84
+ @arg = hash
85
+ end
86
+ end
87
+ end
88
+
89
+ This corresponds to the following tables.
90
+
91
+ |script|test slim with arguments|!{name:bob addr:here}|
92
+ |check|name|bob|
93
+ |check|addr|here|
94
+
95
+ |script|test slim with arguments|gunk|
96
+ |check|arg|gunk|
97
+ |set arg|!{name:bob addr:here}|
98
+ |check|name|bob|
99
+ |check|addr|here|
100
+
101
+ Note the use of the HashWidgets in the table cells. These get translated into
102
+ HTML, which RubySlim recognizes and converts to a standard ruby `Hash`.
103
+
104
+
105
+ System Under Test
106
+ -----------------
107
+
108
+ If a fixture has a `sut` method that returns an object, then if a method called
109
+ by a test is not found in the fixture itself, then if it exists in the object
110
+ returned by `sut` it will be called. For example:
111
+
112
+ !|script|my fixture|
113
+ |func|1|
114
+
115
+ class MySystem
116
+ def func(x)
117
+ #this is the function that will be called.
118
+ end
119
+ end
120
+
121
+ class MyFixture
122
+ attr_reader :sut
123
+
124
+ def initialize
125
+ @sut = MySystem.new
126
+ end
127
+ end
128
+
129
+ Since the fixture `MyFixture` does not have a function named `func`, but it
130
+ _does_ have a method named `sut`, RubySlim will try to call `func` on the
131
+ object returned by `sut`.
132
+
133
+
134
+ Library Fixtures
135
+ ----------------
136
+
137
+ Ruby Slim supports the `|Library|` feature of FitNesse. If you declare certain
138
+ classes to be libraries, then if a test calls a method, and the specified
139
+ fixture does not have it, and there is no specified `sut`, then the libraries
140
+ will be searched in reverse order (latest first). If the method is found, then
141
+ it is called.
142
+
143
+ For example:
144
+
145
+ |Library|
146
+ |echo fixture|
147
+
148
+ |script|
149
+ |check|echo|a|a|
150
+
151
+ class EchoFixture
152
+ def echo(x)
153
+ x
154
+ end
155
+ end
156
+
157
+ Here, even though no fixture was specified for the script table, since a
158
+ library was declared, functions will be called on it.
159
+
@@ -0,0 +1,21 @@
1
+ require 'rake'
2
+ #require 'rcov/rcovtask'
3
+ require 'spec/rake/spectask'
4
+
5
+ task :default => :spec
6
+
7
+ desc "Run all spec tests"
8
+ Spec::Rake::SpecTask.new(:spec) do |t|
9
+ t.spec_files = Dir.glob('spec/**/*_spec.rb')
10
+ t.spec_opts = ['--color', '--format specdoc']
11
+ end
12
+
13
+ desc "Run all spec tests and generate coverage report"
14
+ Spec::Rake::SpecTask.new(:rcov) do |t|
15
+ t.spec_files = Dir.glob('spec/**/*_spec.rb')
16
+ # RCov doesn't like this part for some reason
17
+ #t.spec_opts = ['--color', '--format specdoc']
18
+ t.rcov = true
19
+ t.rcov_opts = %w{--exclude osx\/objc,gems\/,spec\/,features\/,lib\/test_module\/}
20
+ end
21
+
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubyslim/ruby_slim'
4
+ require 'jcode'
5
+ $KCODE="UTF8"
6
+
7
+ port = ARGV[0].to_i
8
+ rubySlim = RubySlim.new
9
+ rubySlim.run(port)
10
+
@@ -0,0 +1,69 @@
1
+ require 'jcode'
2
+
3
+ module ListDeserializer
4
+ class SyntaxError < Exception
5
+ end
6
+
7
+ # De-serialize the given string, and return a Ruby-native list.
8
+ # Raises a SyntaxError if the string is empty or badly-formatted.
9
+ def self.deserialize(string)
10
+ raise SyntaxError.new("Can't deserialize null") if string.nil?
11
+ raise SyntaxError.new("Can't deserialize empty string") if string.empty?
12
+ raise SyntaxError.new("Serialized list has no starting [") if string[0..0] != "["
13
+ raise SyntaxError.new("Serialized list has no ending ]") if string[-1..-1] != "]"
14
+ Deserializer.new(string).deserialize
15
+ end
16
+
17
+
18
+ class Deserializer
19
+ def initialize(string)
20
+ @string = string;
21
+ end
22
+
23
+ def deserialize
24
+ @pos = 1
25
+ @list = []
26
+ number_of_items = consume_length
27
+
28
+ # For each item in the list
29
+ number_of_items.times do
30
+ length_of_item = consume_length
31
+ item = @string[@pos...@pos+length_of_item]
32
+ length_in_bytes = length_of_item
33
+
34
+ until (item.jlength > length_of_item) do
35
+ length_in_bytes += 1
36
+ item = @string[@pos...@pos+length_in_bytes]
37
+ end
38
+
39
+ length_in_bytes -= 1
40
+ item = @string[@pos...@pos+length_in_bytes]
41
+
42
+ # Ensure the ':' list-termination character is found
43
+ term_char = @string[@pos+length_in_bytes,1]
44
+ if term_char != ':'
45
+ raise SyntaxError.new("List termination character ':' not found" +
46
+ " (got '#{term_char}' instead)")
47
+ end
48
+
49
+ @pos += length_in_bytes+1
50
+ begin
51
+ sublist = ListDeserializer.deserialize(item)
52
+ @list << sublist
53
+ rescue ListDeserializer::SyntaxError
54
+ @list << item
55
+ end
56
+ end
57
+ @list
58
+ end
59
+
60
+ # Consume the 6-digit length prefix, and return the
61
+ # length as an integer.
62
+ def consume_length
63
+ length = @string[@pos...@pos+6].to_i
64
+ @pos += 7
65
+ length
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,12 @@
1
+ require "rubyslim/statement"
2
+ require "rubyslim/statement_executor"
3
+
4
+ class ListExecutor
5
+ def initialize()
6
+ @executor = StatementExecutor.new
7
+ end
8
+
9
+ def execute(instructions)
10
+ instructions.collect {|instruction| Statement.execute(instruction, @executor)}
11
+ end
12
+ end
@@ -0,0 +1,45 @@
1
+ require 'jcode'
2
+
3
+ module ListSerializer
4
+ # Serialize a list according to the SliM protocol.
5
+ #
6
+ # Lists are enclosed in square-brackets '[...]'. Inside the opening
7
+ # bracket is a six-digit number indicating the length of the list
8
+ # (number of items), then a colon ':', then the serialization of each
9
+ # list item. For example:
10
+ #
11
+ # [] => "[000000:]"
12
+ # ["hello"] => "[000001:000005:hello:]"
13
+ # [1] => "[000001:000001:1:]"
14
+ #
15
+ # Strings are preceded by a six-digit sequence indicating their length:
16
+ #
17
+ # "" => "000000:"
18
+ # "hello" => "000005:hello"
19
+ # nil => "000004:null"
20
+ #
21
+ # See spec/list_serializer_spec.rb for more examples.
22
+ #
23
+ def self.serialize(list)
24
+ result = "["
25
+ result += length_string(list.length)
26
+
27
+ # Serialize each item in the list
28
+ list.each do |item|
29
+ item = "null" if item.nil?
30
+ item = serialize(item) if item.is_a?(Array)
31
+ item = item.to_s
32
+ result += length_string(item.jlength)
33
+ result += item + ":"
34
+ end
35
+
36
+ result += "]"
37
+ end
38
+
39
+
40
+ # Return the six-digit prefix for an element of the given length.
41
+ def self.length_string(length)
42
+ sprintf("%06d:",length)
43
+ end
44
+ end
45
+
@@ -0,0 +1,61 @@
1
+ require "rubyslim/socket_service"
2
+ require "rubyslim/list_deserializer"
3
+ require "rubyslim/list_serializer"
4
+ require "rubyslim/list_executor"
5
+
6
+ class RubySlim
7
+ def run(port)
8
+ @connected = true
9
+ @executor = ListExecutor.new
10
+ socket_service = SocketService.new()
11
+ socket_service.serve(port) do |socket|
12
+ serve_ruby_slim(socket)
13
+ end
14
+
15
+ while (@connected)
16
+ sleep(0.1)
17
+ end
18
+ end
19
+
20
+ # Read and execute instructions from the SliM socket, until a 'bye'
21
+ # instruction is reached. Each instruction is a list, serialized as a string,
22
+ # following the SliM protocol:
23
+ #
24
+ # length:command
25
+ #
26
+ # Where `length` is a 6-digit indicating the length in bytes of `command`,
27
+ # and `command` is a serialized list of instructions that may include any
28
+ # of the four standard instructions in the SliM protocol:
29
+ #
30
+ # Import: [<id>, import, <path>]
31
+ # Make: [<id>, make, <instance>, <class>, <arg>...]
32
+ # Call: [<id>, call, <instance>, <function>, <arg>...]
33
+ # CallAndAssign: [<id>, callAndAssign, <symbol>, <instance>, <function>, <arg>...]
34
+ #
35
+ # (from http://fitnesse.org/FitNesse.UserGuide.SliM.SlimProtocol)
36
+ #
37
+ def serve_ruby_slim(socket)
38
+ socket.puts("Slim -- V0.3");
39
+ said_bye = false
40
+
41
+ while !said_bye
42
+ length = socket.read(6).to_i # <length>
43
+ socket.read(1) # :
44
+ command = socket.read(length) # <command>
45
+
46
+ # Until a 'bye' command is received, deserialize the command, execute the
47
+ # instructions, and write a serialized response back to the socket.
48
+ if command.downcase != "bye"
49
+ instructions = ListDeserializer.deserialize(command);
50
+ results = @executor.execute(instructions)
51
+ response = ListSerializer.serialize(results);
52
+ socket.write(sprintf("%06d:%s", response.length, response))
53
+ socket.flush
54
+ else
55
+ said_bye = true
56
+ end
57
+ end
58
+ @connected = false
59
+ end
60
+ end
61
+
@@ -0,0 +1,3 @@
1
+ class SlimError < Exception
2
+
3
+ end
@@ -0,0 +1,23 @@
1
+ class SlimHelperLibrary
2
+ ACTOR_INSTANCE_NAME = "scriptTableActor"
3
+ attr_accessor :executor
4
+
5
+ def initialize(executor = nil)
6
+ @executor = executor
7
+ @fixtures = []
8
+ end
9
+
10
+ def get_fixture
11
+ executor.instance(ACTOR_INSTANCE_NAME)
12
+ end
13
+
14
+ def push_fixture
15
+ @fixtures << get_fixture
16
+ nil
17
+ end
18
+
19
+ def pop_fixture
20
+ executor.set_instance(ACTOR_INSTANCE_NAME, @fixtures.pop)
21
+ nil
22
+ end
23
+ end
@@ -0,0 +1,53 @@
1
+ require 'socket'
2
+ require 'thread'
3
+
4
+
5
+ class SocketService
6
+
7
+ attr_reader :closed
8
+
9
+ def initialize()
10
+ @ropeSocket = nil
11
+ @group = ThreadGroup.new
12
+ @serviceThread = nil
13
+ end
14
+
15
+ def serve(port, &action)
16
+ @closed = false
17
+ @action = action
18
+ @ropeSocket = TCPServer.open(port)
19
+ @serviceThread = Thread.start {serviceTask}
20
+ @group.add(@serviceThread)
21
+ end
22
+
23
+ def pendingSessions
24
+ @group.list.size - ((@serviceThread != nil) ? 1 : 0)
25
+ end
26
+
27
+ def serviceTask
28
+ while true
29
+ Thread.start(@ropeSocket.accept) do |s|
30
+ serverTask(s)
31
+ end
32
+ end
33
+ end
34
+
35
+ def serverTask(s)
36
+ @action.call(s)
37
+ s.close
38
+ end
39
+
40
+ def close
41
+ @serviceThread.kill
42
+ @serviceThread = nil
43
+ @ropeSocket.close
44
+ waitForServers
45
+ @closed = true
46
+ end
47
+
48
+ def waitForServers
49
+ @group.list.each do |t|
50
+ t.join
51
+ end
52
+ end
53
+ end