-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathterrain.py
399 lines (291 loc) · 11.8 KB
/
terrain.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
"""The global lettuce functions.
Defined here are a set of functions that allow the use of lettuce
(http://lettuce.it) to perform BDD.
You are able to use the Django test Client to test template rendering and the
response codes from requests etc. etc. or use Selenium to test the interface
and expected behaviour.
:py:func:`access_reverse_url`
:py:func:`access_url`
:py:func:`check_the_field_`
:py:func:`click_on_button`
:py:func:`expect_redirect`
:py:func:`fill_the_field_with`
:py:func:`finished_selenium`
:py:func:`given_i_am_not_logged_in`
:py:func:`hit_template`
:py:func:`if_it_passes_then_should_see_text`
:py:func:`import_forms`
:py:func:`i_see_that_the_form_required_fields_are_present`
:py:func:`kill_selenium`
:py:func:`result_of_form_submission_should_be`
:py:func:`see_header`
:py:func:`set_browser`
:py:func:`set_sel_timeout`
:py:func:`silence_debug`
:py:func:`sleep_for`
:py:func:`start_selenium`
:py:func:`that_its_id_is`
:py:func:`using_selenium`
"""
import logging
import hashlib
import time
from datetime import datetime
from lxml import html
from selenium import selenium
from nose.tools import assert_equals
from django.test.client import Client
from django.core.urlresolvers import reverse
from lettuce import *
from lettuce.django import django_url
########## SET UP ###################################################
@after.all
def kill_selenium(total):
"""Called after all tests to kill selenium.
Should not be used directly
"""
world.sel.stop()
# pass # comment above and uncomment this if you want to see last sel. page
@before.all
def start_selenium():
"""Called before all tests to start selenium.
Should not be used directly
"""
world.using_selenium = False
world.timeout = 5000
world.sel = selenium(
'localhost',
4444,
'*firefox',
'http://localhost:8000')
world.sel.start()
@before.all
def set_browser():
world.browser = Client()
@before.all
def silence_debug():
print "Silencing debug logs"
logging.disable(logging.INFO)
@before.each_feature
def import_forms(feature):
"""Finds the app for these tests and loads the forms, if any.
Should not be called directly
"""
app = feature.described_at.file.split("/")[-3]
world.app = app
try:
forms_app = __import__(app, fromlist='forms')
world.forms = forms_app.forms
except:
print "Couldn't find any forms for %s" % app
#####################################################################
######### SELENIUM BITS #############################################
@step(u'Using selenium')
def using_selenium(step):
"""Setup our selenium instance and tell the world to use it.
Note that Django client tests are more rigorously checked -- this is the
place to check things like template matching and redirects.
Selenium use should be limited to JS/integration testing.
"""
world.using_selenium = True
@step(u'Finished using selenium')
def finished_selenium(step):
"""Stop using selenium - go back to Django's Client."""
world.using_selenium = False
@step(r'a timeout of "(\d+)"')
def set_sel_timeout(step, timeout):
"""Set the page timeout for selenium tests.
:param integer timeout: number of seconds to wait.
"""
world.timeout = int(timeout)
#####################################################################
######## PAGE ACCESS ################################################
@step(r'I access the url "(.*)"')
def access_url(step, url):
"""Go to the url check for a 200 response.
The :py:data:`world.dom` and :py:data:`world.templates` variables are set.
A 200 is checked for.
If using Selenium will also try the url and wait for the page to load for
:py:data:`world.timeout` which can be set with :py:func:`set_sel_timeout`.
"""
if world.using_selenium:
world.sel.open(url)
world.sel.wait_for_page_to_load(world.timeout)
response = world.browser.get(url)
assert response.status_code == 200, \
"Failed to get a 200 for %s, got %s" % (url, response.status_code)
world.dom = html.fromstring(response.content)
world.templates = [t.name for t in response.template]
@step(r'I access the reversed url "(.*)"')
def access_reverse_url(step, url):
"""Reverse the named url and visit it check for a 200 response.
The same as :py:func:`access_url` except using a django-named url.
"""
rev_url = reverse(url)
if world.using_selenium:
world.sel.open(rev_url)
world.sel.wait_for_page_to_load(world.timeout)
response = world.browser.get(rev_url)
assert response.status_code == 200, \
"Failed to get a 200 for %s, got %s" % (rev_url, response.status_code)
world.dom = html.fromstring(response.content)
world.templates = [t.name for t in response.template]
@step(r'I expect to be redirected from "([^"]*)" to "([^"]*)"')
def expect_redirect(step, from_url, to_url):
"""Go to a url and expect a 302, check the DOM returned == expected DOM.
Bit weird this one, and might not be useful. The :py:data:`to_url` you are
expecting to eventually reach (via the :py:data:`from_url`) is first hit
in the normal fashion and the DOM is saved to a string. The
:py:data:`from_url` is then hit, checked for a 302 and the eventual DOM is
compared to the stored one.
If Selenium is used, the :py:data:`from_url` is hit, and waited for as per
:py:func:`access_url`.
"""
step.given('I access the url "%s"' % to_url)
expected_dom_str = html.tostring(world.dom)
response = world.browser.get(from_url)
code = response.status_code
assert code == 302, \
"Failed to get a 302 for %s, got %s" % (from_url, code)
response = world.browser.get(from_url, follow=True)
world.dom = html.fromstring(response.content)
world.templates = [t.name for t in response.template]
assert html.tostring(world.dom) == expected_dom_str, \
"Expected DOM doesn't match redirected DOM"
if world.using_selenium:
world.sel.open(from_url)
world.sel.wait_for_page_to_load(world.timeout)
@step(r'I hit the template "(.*)"')
def hit_template(step, template):
"""Using Django's Client, check that a template was rendered.
This expects one of the url access functions to have been used, which will
set the :py:data:`world.templates` variable. Appropriate url access funcs
are: :py:func:`access_url`, :py:func:`access_reverse_url` and
:py:func:`expect_redirect`.
"""
assert world.templates and template in world.templates, \
"%s not in %s" % (template, world.templates)
#####################################################################
######### DOM #######################################################
@step(r'I see the header(\d+) "(.*)"')
def see_header(step, header, text):
"""Check page for some text in a particular header type.
Slight mis-match here between selenium and Django tests as we get the 0th
element with lxml and any with selenium.
"""
# this should work however we accessed the page (i.e. with or without
# selenium), and so the world.element is set.
header = world.dom.cssselect('h%s' % header)[0]
world.element = header
if world.using_selenium:
assert world.sel.get_text("css=h%s" % header) == text
else:
assert header.text == text
@step(r'that its id is "(.*)"')
def that_its_id_is(step, id):
"""Check the element's id stored in :py:data:`world.element`.
The :py:data:`world.element` is an :py:class:`lxml.html` element. It can
be set by using methods :py:func:`see_header`...
"""
assert_equals(world.element.attrib['id'], id)
@step(u'If the result "(.*)" is pass then I see the text "(.*)"')
def if_it_passes_then_should_see_text(step, result, text):
"""If the result is "pass" then check for the text.
Typically used in a tabular scenario where some are expected to pass and
some fail, whatever pass and fail means in that context.
"""
if not result == "pass":
return # not relevant test.
if world.using_selenium:
assert world.sel.is_text_present(text)
else:
assert False, 'This step must be implemented'
#####################################################################
######### GLOBAL FUNCS ##############################################
@step(u'I am not logged in')
def given_i_am_not_logged_in(step):
"""Do a logout with Django or Selenium.
Note that selenium use is inferred here by the call to
:py:func:`expect_redirect`.
"""
auth_logout = reverse("auth_logout")
step.given('I expect to be redirected from "%s" to "/"' % auth_logout)
# what can I assert here?!
@step(u'I see that the form "(.*)" required fields are present')
def i_see_that_the_form_required_fields_are_present(step, friendly_form_name):
"""Load the django form and check all its required fields are present.
"""
assert world.forms, "No forms for %s!" % world.app
form_name = friendly_form_name.title().replace(' ', '')
form = getattr(world.forms, form_name, None)
assert form, "No form named %s in %s" % (form_name, world.app)
form = form()
for field in filter(lambda x: x.field.required, form):
if world.using_selenium:
assert world.sel.is_element_present("css=#id_%s" % field.name), \
"No field with id id_%s" % field.name
else:
assert False, "implement me!"
@step(u'Fill the field "(.*)" with "(.*)"')
def fill_the_field_with(step, id, value):
"""Fill in the field.
Selenium only.
:param string id: an id string without the #.
"""
val = value
if "<<rnd>>" in value: # to avoid where we need unique input
hash = hashlib.new("md5")
hash.update(str(datetime.now()))
rnd = hash.hexdigest()
val = value.replace("<<rnd>>", rnd)
if world.using_selenium:
world.sel.type("css=#%s" % id, val)
else:
assert False, 'This step must be implemented'
@step(u'Sleep for "(\d+)"')
def sleep_for(step, secs):
"""Delay for a while.
Useful for selenium tests to see input (when debugging at least).
"""
time.sleep(int(secs))
@step(u'Click on "(.*)" button')
def click_on_button(step, selector):
"""Click a button, with selenium.
:param string selector: a css selector.
"""
if world.using_selenium:
world.sel.click("css=%s" % selector)
world.sel.wait_for_page_to_load(world.timeout)
else:
assert False, 'This step must be implemented'
@step(u'Check the field "(.*)" with "(checked|unchecked)"')
def check_the_field_(step, id, checked):
"""Check or uncheck a field, if needed.
Selenium only.
:param string id: id string, excluding the #.
"""
if world.using_selenium:
if checked == "checked":
if not world.sel.is_checked("css=#%s" % id):
world.sel.click("css=#%s" % id)
elif checked == "unchecked":
if world.sel.is_checked("css=#%s" % id):
world.sel.click("css=#%s" % id)
else:
assert False, 'This step must be implemented'
@step(u'Result of form submission should be "(.*)"')
def result_of_form_submission_should_be(step, result):
"""Check for an errorlist, and compare that to expectation of presence.
If we want this to be "pass" there should be no errorlist.
If we want this to be "fail" there should be an errorlist.
:param string result: should be either "pass" or "fail".
"""
if world.using_selenium:
if result == "pass":
assert not world.sel.is_element_present('css=ul.errorlist')
elif result == "fail":
assert world.sel.is_element_present('css=ul.errorlist')
else:
assert False, 'This step must be implemented'