rsel 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.yardopts +7 -0
- data/Gemfile +2 -0
- data/MIT-LICENSE +22 -0
- data/README.md +35 -0
- data/Rakefile +75 -0
- data/docs/custom.md +87 -0
- data/docs/development.md +48 -0
- data/docs/index.md +13 -0
- data/docs/install.md +33 -0
- data/docs/todo.md +15 -0
- data/docs/usage.md +50 -0
- data/lib/rsel.rb +4 -0
- data/lib/rsel/exceptions.rb +4 -0
- data/lib/rsel/selenium_test.rb +524 -0
- data/rsel.gemspec +29 -0
- data/spec/selenium_test_spec.rb +296 -0
- data/spec/spec_helper.rb +41 -0
- data/test/app.rb +34 -0
- data/test/config.ru +4 -0
- data/test/views/about.erb +9 -0
- data/test/views/form.erb +102 -0
- data/test/views/index.erb +15 -0
- data/vendor/rubyslim-0.1.1/.gitignore +7 -0
- data/vendor/rubyslim-0.1.1/README.md +159 -0
- data/vendor/rubyslim-0.1.1/Rakefile +21 -0
- data/vendor/rubyslim-0.1.1/bin/rubyslim +10 -0
- data/vendor/rubyslim-0.1.1/lib/rubyslim/list_deserializer.rb +69 -0
- data/vendor/rubyslim-0.1.1/lib/rubyslim/list_executor.rb +12 -0
- data/vendor/rubyslim-0.1.1/lib/rubyslim/list_serializer.rb +45 -0
- data/vendor/rubyslim-0.1.1/lib/rubyslim/ruby_slim.rb +61 -0
- data/vendor/rubyslim-0.1.1/lib/rubyslim/slim_error.rb +3 -0
- data/vendor/rubyslim-0.1.1/lib/rubyslim/slim_helper_library.rb +23 -0
- data/vendor/rubyslim-0.1.1/lib/rubyslim/socket_service.rb +53 -0
- data/vendor/rubyslim-0.1.1/lib/rubyslim/statement.rb +79 -0
- data/vendor/rubyslim-0.1.1/lib/rubyslim/statement_executor.rb +174 -0
- data/vendor/rubyslim-0.1.1/lib/rubyslim/table_to_hash_converter.rb +32 -0
- data/vendor/rubyslim-0.1.1/lib/test_module/library_new.rb +13 -0
- data/vendor/rubyslim-0.1.1/lib/test_module/library_old.rb +12 -0
- data/vendor/rubyslim-0.1.1/lib/test_module/should_not_find_test_slim_in_here/test_slim.rb +7 -0
- data/vendor/rubyslim-0.1.1/lib/test_module/simple_script.rb +9 -0
- data/vendor/rubyslim-0.1.1/lib/test_module/test_chain.rb +5 -0
- data/vendor/rubyslim-0.1.1/lib/test_module/test_slim.rb +86 -0
- data/vendor/rubyslim-0.1.1/lib/test_module/test_slim_with_arguments.rb +23 -0
- data/vendor/rubyslim-0.1.1/lib/test_module/test_slim_with_no_sut.rb +5 -0
- data/vendor/rubyslim-0.1.1/rubyslim.gemspec +22 -0
- data/vendor/rubyslim-0.1.1/spec/instance_creation_spec.rb +40 -0
- data/vendor/rubyslim-0.1.1/spec/it8f_spec.rb +8 -0
- data/vendor/rubyslim-0.1.1/spec/list_deserializer_spec.rb +66 -0
- data/vendor/rubyslim-0.1.1/spec/list_executor_spec.rb +187 -0
- data/vendor/rubyslim-0.1.1/spec/list_serialzer_spec.rb +38 -0
- data/vendor/rubyslim-0.1.1/spec/method_invocation_spec.rb +127 -0
- data/vendor/rubyslim-0.1.1/spec/slim_helper_library_spec.rb +80 -0
- data/vendor/rubyslim-0.1.1/spec/socket_service_spec.rb +98 -0
- data/vendor/rubyslim-0.1.1/spec/spec_helper.rb +6 -0
- data/vendor/rubyslim-0.1.1/spec/statement_executor_spec.rb +50 -0
- data/vendor/rubyslim-0.1.1/spec/statement_spec.rb +17 -0
- data/vendor/rubyslim-0.1.1/spec/table_to_hash_converter_spec.rb +31 -0
- metadata +241 -0
data/rsel.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "rsel"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.summary = "Runs Selenium tests from FitNesse"
|
5
|
+
s.description = <<-EOS
|
6
|
+
Rsel provides a Slim fixture for running Selenium tests, with
|
7
|
+
step methods written in Ruby.
|
8
|
+
EOS
|
9
|
+
s.authors = ["Marcus French", "Dale Straw", "Eric Pierce"]
|
10
|
+
s.email = "epierce@automation-excellence.com"
|
11
|
+
s.homepage = "http://github.com/a-e/rsel"
|
12
|
+
s.platform = Gem::Platform::RUBY
|
13
|
+
|
14
|
+
s.add_dependency 'rubyslim-unofficial'
|
15
|
+
s.add_dependency 'xpath'
|
16
|
+
s.add_dependency 'selenium-client'
|
17
|
+
|
18
|
+
s.add_development_dependency 'rake', '0.8.7'
|
19
|
+
s.add_development_dependency 'sinatra'
|
20
|
+
s.add_development_dependency 'mongrel'
|
21
|
+
s.add_development_dependency 'yard'
|
22
|
+
s.add_development_dependency 'rspec', '>= 2.2.0'
|
23
|
+
|
24
|
+
s.files = `git ls-files`.split("\n")
|
25
|
+
# Don't include .jar files in distribution
|
26
|
+
s.files.reject! { |f| f =~ /.jar$/ }
|
27
|
+
|
28
|
+
s.require_path = 'lib'
|
29
|
+
end
|
@@ -0,0 +1,296 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Rsel::SeleniumTest do
|
4
|
+
context 'initialization' do
|
5
|
+
before(:each) do
|
6
|
+
@st.visit('/')
|
7
|
+
end
|
8
|
+
|
9
|
+
it "sets correct default configuration" do
|
10
|
+
@st.url.should == 'http://localhost:8070/'
|
11
|
+
@st.browser.host.should == 'localhost'
|
12
|
+
@st.browser.port.should == 4444
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
context 'checkbox' do
|
18
|
+
before(:each) do
|
19
|
+
@st.visit('/form').should be_true
|
20
|
+
end
|
21
|
+
|
22
|
+
context "enable" do
|
23
|
+
context "passes when" do
|
24
|
+
it "checkbox with the given label is present" do
|
25
|
+
@st.enable_checkbox("I like cheese").should be_true
|
26
|
+
@st.enable_checkbox("I like salami").should be_true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "fails when" do
|
31
|
+
it "checkbox with the given label is absent" do
|
32
|
+
@st.enable_checkbox("I dislike bacon").should be_false
|
33
|
+
@st.enable_checkbox("I like broccoli").should be_false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "disable" do
|
39
|
+
context "passes when" do
|
40
|
+
it "checkbox with the given label is present" do
|
41
|
+
@st.disable_checkbox("I like cheese").should be_true
|
42
|
+
@st.disable_checkbox("I like salami").should be_true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
context "fails when" do
|
46
|
+
it "checkbox with the given label is absent" do
|
47
|
+
@st.disable_checkbox("I dislike bacon").should be_false
|
48
|
+
@st.disable_checkbox("I like broccoli").should be_false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "is enabled" do
|
54
|
+
context "passes when" do
|
55
|
+
it "checkbox with the given label exists and is checked" do
|
56
|
+
@st.enable_checkbox("I like cheese").should be_true
|
57
|
+
@st.checkbox_is_enabled("I like cheese").should be_true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "fails when" do
|
62
|
+
it "checkbox with the given label exists but is unchecked" do
|
63
|
+
@st.disable_checkbox("I like cheese").should be_true
|
64
|
+
@st.checkbox_is_enabled("I like cheese").should be_false
|
65
|
+
end
|
66
|
+
|
67
|
+
it "checkbox with the given label does not exist" do
|
68
|
+
@st.checkbox_is_enabled("I dislike bacon").should be_false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "is disabled" do
|
74
|
+
context "passes when" do
|
75
|
+
it "checkbox with the given label exists and is unchecked" do
|
76
|
+
@st.disable_checkbox("I like cheese").should be_true
|
77
|
+
@st.checkbox_is_disabled("I like cheese").should be_true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "fails when" do
|
82
|
+
it "checkbox with the given label exists but is checked" do
|
83
|
+
@st.enable_checkbox("I like cheese").should be_true
|
84
|
+
@st.checkbox_is_disabled("I like cheese").should be_false
|
85
|
+
end
|
86
|
+
|
87
|
+
it "checkbox with the given label does not exist" do
|
88
|
+
@st.checkbox_is_disabled("I dislike bacon").should be_false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
context 'dropdown' do
|
96
|
+
before(:each) do
|
97
|
+
@st.visit('/form').should be_true
|
98
|
+
end
|
99
|
+
|
100
|
+
context "select" do
|
101
|
+
context "passes when" do
|
102
|
+
it "value exists in a dropdown" do
|
103
|
+
@st.select_from_dropdown("Tall", "Height").should be_true
|
104
|
+
@st.select_from_dropdown("Medium", "Weight").should be_true
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "fails when" do
|
109
|
+
it "dropdown exists, but the value doesn't" do
|
110
|
+
@st.select_from_dropdown("Giant", "Height").should be_false
|
111
|
+
@st.select_from_dropdown("Obese", "Weight").should be_false
|
112
|
+
end
|
113
|
+
|
114
|
+
it "no such dropdown exists" do
|
115
|
+
@st.select_from_dropdown("Over easy", "Eggs").should be_false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "verify" do
|
121
|
+
# TODO
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
context 'navigation' do
|
127
|
+
before(:each) do
|
128
|
+
@st.visit('/').should be_true
|
129
|
+
end
|
130
|
+
|
131
|
+
context "visit" do
|
132
|
+
context "passes when" do
|
133
|
+
it "page exists" do
|
134
|
+
@st.visit("/about").should be_true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "fails when" do
|
139
|
+
it "page does not exist" do
|
140
|
+
@st.visit("/bad/path").should be_false
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context "reload the current page" do
|
146
|
+
# TODO
|
147
|
+
end
|
148
|
+
|
149
|
+
context "go back to the previous page" do
|
150
|
+
it "passes and loads the correct URL" do
|
151
|
+
@st.visit("/about")
|
152
|
+
@st.visit("/")
|
153
|
+
@st.click_back.should be_true
|
154
|
+
@st.see_title("About this site").should be_true
|
155
|
+
end
|
156
|
+
|
157
|
+
#it "fails when there is no previous page in the history" do
|
158
|
+
# TODO: No obvious way to test this, since everything is running in the
|
159
|
+
# same session
|
160
|
+
#end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "clicking a link" do
|
164
|
+
it "passes and loads the correct page when a link exists" do
|
165
|
+
@st.click_link("About this site").should be_true
|
166
|
+
@st.see_title("About this site").should be_true
|
167
|
+
@st.see("This site is really cool").should be_true
|
168
|
+
end
|
169
|
+
|
170
|
+
it "fails when a link does not exist" do
|
171
|
+
@st.follow("Bogus link").should be_false
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
context 'text field' do
|
178
|
+
before(:each) do
|
179
|
+
@st.visit('/form').should be_true
|
180
|
+
end
|
181
|
+
|
182
|
+
context "type into or fill in" do
|
183
|
+
context "passes when" do
|
184
|
+
it "text field with the given label exists" do
|
185
|
+
@st.type_into_field("Eric", "First name").should be_true
|
186
|
+
@st.fill_in_with("Last name", "Pierce").should be_true
|
187
|
+
end
|
188
|
+
|
189
|
+
it "text field with the given id exists" do
|
190
|
+
@st.type_into_field("Eric", "first_name").should be_true
|
191
|
+
@st.fill_in_with("last_name", "Pierce").should be_true
|
192
|
+
end
|
193
|
+
|
194
|
+
it "texarea with the given label exists" do
|
195
|
+
@st.type_into_field("Blah blah blah", "Life story").should be_true
|
196
|
+
@st.fill_in_with("Life story", "Jibber jabber").should be_true
|
197
|
+
end
|
198
|
+
|
199
|
+
it "texarea with the given id exists" do
|
200
|
+
@st.type_into_field("Blah blah blah", "biography").should be_true
|
201
|
+
@st.fill_in_with("biography", "Jibber jabber").should be_true
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context "fails when" do
|
206
|
+
it "no field with the given label or id exists" do
|
207
|
+
@st.type_into_field("Matthew", "Middle name").should be_false
|
208
|
+
@st.fill_in_with("middle_name", "Matthew").should be_false
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "should contain" do
|
214
|
+
context "passes when" do
|
215
|
+
it "textarea contains the text" do
|
216
|
+
@st.fill_in_with("Life story", "Blah dee blah")
|
217
|
+
@st.field_contains("Life story", "blah").should be_true
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context "fails when" do
|
222
|
+
it "textarea does not contain the text" do
|
223
|
+
@st.fill_in_with("Life story", "Blah dee blah")
|
224
|
+
@st.field_contains("Life story", "spam").should be_false
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context "should equal" do
|
230
|
+
context "passes when" do
|
231
|
+
it "textarea equals the text" do
|
232
|
+
@st.fill_in_with("Life story", "Blah dee blah")
|
233
|
+
@st.field_equals("Life story", "Blah dee blah").should be_true
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context "fails when" do
|
238
|
+
it "textarea does not exactly equal the text" do
|
239
|
+
@st.fill_in_with("Life story", "Blah dee blah")
|
240
|
+
@st.field_equals("Life story", "Blah dee").should be_false
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
context 'visibility' do
|
248
|
+
before(:each) do
|
249
|
+
@st.visit('/').should be_true
|
250
|
+
end
|
251
|
+
|
252
|
+
context "should see" do
|
253
|
+
context "passes when" do
|
254
|
+
it "text is present" do
|
255
|
+
@st.see('Welcome').should be_true
|
256
|
+
@st.see('This is a Sinatra webapp').should be_true
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
context "fails when" do
|
261
|
+
it "text is absent" do
|
262
|
+
@st.see('Nonexistent').should be_false
|
263
|
+
@st.see('Some bogus text').should be_false
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
it "is case-sensitive" do
|
268
|
+
@st.see('Sinatra webapp').should be_true
|
269
|
+
@st.see('sinatra Webapp').should be_false
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
context "should not see" do
|
274
|
+
context "passes when" do
|
275
|
+
it "text is absent" do
|
276
|
+
@st.do_not_see('Nonexistent').should be_true
|
277
|
+
@st.do_not_see('Some bogus text').should be_true
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
context "fails when" do
|
282
|
+
it "fails when test is present" do
|
283
|
+
@st.do_not_see('Welcome').should be_false
|
284
|
+
@st.do_not_see('This is a Sinatra webapp').should be_false
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
it "is case-sensitive" do
|
289
|
+
@st.do_not_see('Sinatra webapp').should be_false
|
290
|
+
@st.do_not_see('sinatra Webapp').should be_true
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
end
|
296
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'rsel'
|
3
|
+
require 'selenium/client'
|
4
|
+
|
5
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test', 'app'))
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.include Rsel
|
9
|
+
|
10
|
+
config.before(:all) do
|
11
|
+
@st = Rsel::SeleniumTest.new('http://localhost:8070/')
|
12
|
+
@st.open_browser
|
13
|
+
end
|
14
|
+
|
15
|
+
config.after(:all) do
|
16
|
+
@st.close_browser
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# Monkeypatch the Selenium::Client::Protocol module,
|
23
|
+
# to prevent http_post from spewing out a bunch of `puts`es on failure.
|
24
|
+
# (It's really distracting when running spec tests)
|
25
|
+
module Selenium
|
26
|
+
module Client
|
27
|
+
module Protocol
|
28
|
+
def http_post(data)
|
29
|
+
start = Time.now
|
30
|
+
called_from = caller.detect{|line| line !~ /(selenium-client|vendor|usr\/lib\/ruby|\(eval\))/i}
|
31
|
+
http = Net::HTTP.new(@host, @port)
|
32
|
+
http.open_timeout = default_timeout_in_seconds
|
33
|
+
http.read_timeout = default_timeout_in_seconds
|
34
|
+
response = http.post('/selenium-server/driver/', data, HTTP_HEADERS)
|
35
|
+
# <-- Here is where all the puts statements were -->
|
36
|
+
[ response.body[0..1], response.body ]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
data/test/app.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'sinatra/base'
|
5
|
+
require 'rack'
|
6
|
+
require 'erb'
|
7
|
+
|
8
|
+
class TestApp < Sinatra::Base
|
9
|
+
set :root, File.dirname(__FILE__)
|
10
|
+
set :static, true
|
11
|
+
|
12
|
+
get '/' do
|
13
|
+
erb :index
|
14
|
+
end
|
15
|
+
|
16
|
+
get '/about' do
|
17
|
+
erb :about
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/form' do
|
21
|
+
erb :form
|
22
|
+
end
|
23
|
+
|
24
|
+
# Allow shutting down the app with a request
|
25
|
+
get '/shutdown' do
|
26
|
+
Process.kill('KILL', Process.pid)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
if __FILE__ == $0
|
32
|
+
Rack::Handler::Mongrel.run TestApp, :Port => 8070
|
33
|
+
end
|
34
|
+
|
data/test/config.ru
ADDED
data/test/views/form.erb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head><title>Rsel Test Forms</title></head>
|
4
|
+
<body>
|
5
|
+
<h1>Forms</h1>
|
6
|
+
<p>This page has some random forms to test.</p>
|
7
|
+
|
8
|
+
<div id="person_form">
|
9
|
+
<form action="/thanks" method="get">
|
10
|
+
<p>
|
11
|
+
<label for="first_name">First name</label>
|
12
|
+
<input id="first_name" type="text" />
|
13
|
+
</p>
|
14
|
+
<p>
|
15
|
+
<label for="last_name">Last name</label>
|
16
|
+
<input id="last_name" type="text" />
|
17
|
+
</p>
|
18
|
+
<p>
|
19
|
+
<label for="biography">Life story</label>
|
20
|
+
<textarea id="biography" cols="80" rows="10">
|
21
|
+
</textarea>
|
22
|
+
</p>
|
23
|
+
<p>
|
24
|
+
<label for="height">Height</label>
|
25
|
+
<select id="height">
|
26
|
+
<option value="short">Short</option>
|
27
|
+
<option value="average" selected="selected">Average</option>
|
28
|
+
<option value="tall" >Tall</option>
|
29
|
+
</select>
|
30
|
+
</p>
|
31
|
+
<p>
|
32
|
+
<label for="weight">Weight</label>
|
33
|
+
<select id="weight">
|
34
|
+
<option value="light">Light</option>
|
35
|
+
<option value="medium">Medium</option>
|
36
|
+
<option value="heavy">Heavy</option>
|
37
|
+
</select>
|
38
|
+
</p>
|
39
|
+
<p><button value="submit_person_form">Submit person form</button></p>
|
40
|
+
</form>
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<div id="preferences_form">
|
44
|
+
<form action="/thanks" method="get">
|
45
|
+
<p>
|
46
|
+
<label for="favorite_colors">Favorite Colors</label>
|
47
|
+
<select id="favorite_colors" multiple="multiple">
|
48
|
+
<option value="r">Red</option>
|
49
|
+
<option value="g">Green</option>
|
50
|
+
<option value="b">Blue</option>
|
51
|
+
</select>
|
52
|
+
</p>
|
53
|
+
<p>
|
54
|
+
<label for="like_cheese">I like cheese</label>
|
55
|
+
<input id="like_cheese" type="checkbox" checked="checked"/>
|
56
|
+
</p>
|
57
|
+
<p>
|
58
|
+
<label for="like_salami">I like salami</label>
|
59
|
+
<input id="like_salami" type="checkbox" />
|
60
|
+
</p>
|
61
|
+
<table>
|
62
|
+
<tr>
|
63
|
+
<th>Apple</th>
|
64
|
+
<td>
|
65
|
+
<label for="like_apple">Like</label>
|
66
|
+
<input id="like_apple" type="checkbox" checked="checked"/>
|
67
|
+
</td>
|
68
|
+
</tr>
|
69
|
+
<tr>
|
70
|
+
<th>Banana</th>
|
71
|
+
<td>
|
72
|
+
<label for="like_banana">Like</label>
|
73
|
+
<input id="like_banana" type="checkbox"/>
|
74
|
+
</td>
|
75
|
+
</tr>
|
76
|
+
</table>
|
77
|
+
<p>
|
78
|
+
<label for="message">Message</label>
|
79
|
+
<input id="message" type="text" value="Your message goes here" />
|
80
|
+
</p>
|
81
|
+
<p><input type="submit" value="Save preferences" /></p>
|
82
|
+
</form>
|
83
|
+
</div>
|
84
|
+
|
85
|
+
<div id="other_form">
|
86
|
+
<form action="/thanks" method="get">
|
87
|
+
<p>
|
88
|
+
<label for="quotes">Quotes</label>
|
89
|
+
<select id="quotes">
|
90
|
+
<option value="1">Single 'quotes'</option>
|
91
|
+
<option value="2">"Double" quotes</option>
|
92
|
+
<option value="3">'Single' and "Double" quotes</option>
|
93
|
+
</select>
|
94
|
+
</p>
|
95
|
+
<p><input id="readonly" type="text" disabled="disabled" /></p>
|
96
|
+
<p><input type="submit" value="Save is disabled" disabled="disabled" /></p>
|
97
|
+
</form>
|
98
|
+
</div>
|
99
|
+
|
100
|
+
</body>
|
101
|
+
</html>
|
102
|
+
|