from twisted.internet import defer
from nevow import livepage,loaders,tags,rend,static,entities,util
from nevow.livepage import js
testFrameNode = js.testFrameNode
contentDocument = testFrameNode.contentDocument
gid = contentDocument.getElementById
XPathResult = js.XPathResult
null = js.null
class xpath(object):
def __init__(self, path):
self.path = path
def __repr__(self):
return 'nevow.livetest.xpath(%r)' % (self.path, )
def _asjs(self, localName):
yield livepage.var(
js.targetXPathResult,
contentDocument.evaluate(self.path,
contentDocument,
null,
XPathResult.ANY_TYPE,
null)), livepage.eol
yield livepage.var(
localName,
js.targetXPathResult.iterateNext()), livepage.eol
class Driver(object):
def __init__(self, handle, suite):
self.handle = handle
self.suite = list(suite)
self.results = {}
self.state = 0
self.iterator = self.drive()
self.nextTest()
passes = 0
failures = 0
def drive(self):
for i, test in enumerate(self.suite):
self.state = i
action, target, parameter = test
actionCallable = getattr(self, 'action_%s' % (action, ), None)
if actionCallable is not None:
test = actionCallable(target, parameter)
if test is not None:
yield test
self.handle.send(livepage.set('test-status', 'Complete'))
def nextTest(self):
try:
test = self.iterator.next()
except StopIteration:
return
self.handle.send(test)
def passed(self):
self.results[self.state] = True
self.passes += 1
self.handle.send(js.passed(self.state))
self.nextTest()
def failed(self, text=''):
self.results[self.state] = False
self.failures += 1
self.handle.send(js.failed(self.state, text))
self.nextTest()
def checkException(self):
def continueTests(ctx, status):
if status == 'passed':
self.passed()
else:
self.failed()
continuer = self.handle.transient(continueTests)
return livepage.anonymous(
[livepage.js("if (testFrameNode.contentDocument.title != 'Exception') {\n\t"),
continuer('passed'),
livepage.js("\n} else {\n\t"),
continuer('failed'),
livepage.js("\n}")])
def action_visit(self, target, param):
yield js.addLoadObserver(self.checkException()), livepage.eol
yield js.setContentLocation(target), livepage.eol
def action_assert(self, target, param):
def doAssert(ctx, actual):
if param == actual:
self.passed()
else:
self.failed("%r != %r" % (param, actual))
if isinstance(target, xpath):
yield target._asjs(js.targetNode)
else:
yield livepage.var(js.targetNode, gid(target)), livepage.eol
yield self.handle.transient(
doAssert, js.targetNode.innerHTML)
def action_value(self, target, param):
def doAssert(ctx, actual):
if param == actual:
self.passed()
else:
self.failed()
if isinstance(target, xpath):
yield target._asjs(js.targetNode)
else:
yield livepage.var(js.targetNode, gid(target)), livepage.eol
yield self.handle.transient(
doAssert, js.targetNode.value)
def action_follow(self, target, param):
if isinstance(target, xpath):
yield target._asjs(js.targetNode)
else:
yield livepage.var(js.targetNode, gid(target)), livepage.eol
yield [
js.addLoadObserver(self.checkException()),
livepage.eol,
js.setContentLocation(js.targetNode.href)]
def action_post(self, target, param):
def passed(ctx):
self.passed()
if isinstance(target, xpath):
yield target._asjs(js.targetForm)
else:
yield livepage.var(js.targetForm, contentDocument[target]), livepage.eol
yield livepage.var(js.postTarget, js.targetForm.action), livepage.eol
for key, value in param.items():
yield livepage.assign(js.targetForm[key].value, value), livepage.eol
yield js.addLoadObserver(
livepage.anonymous(
self.handle.transient(passed))), livepage.eol
yield js.sendSubmitEvent(js.targetForm, livepage.anonymous(js))
def action_submit(self, target, param):
"""This should only be used with livepage, to simulate an onsubmit.
It could be possible to make this work when not testing a livepage
app, using a monstrosity similar to that used by action_click, below.
"""
def passed(ctx):
self.passed()
if isinstance(target, xpath):
yield target._asjs(js.targetForm)
else:
yield livepage.var(js.targetForm, contentDocument[target]), livepage.eol
yield livepage.var(js.postTarget, js.targetForm.action), livepage.eol
for key, value in param.items():
yield livepage.assign(js.targetForm[key].value, value), livepage.eol
yield livepage.var(
js.inputListener,
contentDocument.defaultView.listenForInputEvents(
livepage.anonymous(
self.handle.transient(passed)))), livepage.eol
yield js.sendSubmitEvent(
js.targetForm,
livepage.anonymous(
contentDocument.defaultView.stopListening(js.inputListener)))
def action_click(self, target, param):
"""TODO: Either decide that this should only be used in the presence
of a real, honest-to-god livepage app, or figure out some way to simplify
this monstrosity.
"""
def passed(ctx):
self.passed()
if isinstance(target, xpath):
yield target._asjs(js.targetNode)
else:
yield livepage.var(js.targetNode, gid(target)), livepage.eol
## If the testee is using livepage, we don't want the test to pass
## until all input events (and the response javascript from these
## input events) have passed. To do this we use listenForInputEvents,
## passing a continuation function which will be called when all input
## event responses have been evaluated. We call stopListening
## immediately after sending the click event. This means we
## start listening for input events, simulate the click, then stop listening.
## If any input events were initiated during the click, our test only passes
## when all event responses have been processed.
## If we are not using livepage, listenForInputEvents will not be defined.
## Because it is hard to do javascript tests (if statement) from python,
## ifTesteeUsingLivePage has been defined in livetest-postscripts.
testDidPass = self.handle.transient(passed)
yield [
js.ifTesteeUsingLivePage(
## Using livepage
livepage.anonymous(
livepage.assign(
## Save the listener in a variable so we can stop listening later
js.inputListener,
contentDocument.defaultView.listenForInputEvents(
## When all observed events complete, continue running tests
livepage.anonymous(
testDidPass)))),
## Not using livepage; do nothing here.
livepage.anonymous('')), livepage.eol,
js.sendClickEvent(
## Click our node.
js.targetNode,
## Immediately after clicking the node, run this stuff.
livepage.anonymous(
js.ifTesteeUsingLivePage(
## We're done clicking the node, and we're using livepage.
## Stop listening for input events. This will fire the continuation
## immediately if no input events were observed; otherwise it
## will wait for all responses to be evaluated before firing the
## continuation.
livepage.anonymous(contentDocument.defaultView.stopListening(js.inputListener)),
## We're done clicking the node, and we are not using livepage.
## Call testDidPass.
livepage.anonymous(testDidPass))))]
def action_call(self, target, param):
# Import reactor here to avoid installing default at startup
from twisted.internet import reactor
def doit():
target(self.handle, *param).addCallback(
lambda result: self.passed()
).addErrback(
lambda result: self.failed())
reactor.callLater(0, doit)
return ''
def action_fail(self, target, param):
# Import reactor here to avoid installing default at startup
from twisted.internet import reactor
def doit():
target(self.handle, *param).addCallback(
lambda result: self.failed()
).addErrback(
lambda result: self.passed())
reactor.callLater(0, doit)
class Tester(livepage.LivePage):
addSlash = True
child_css = static.File(util.resource_filename('nevow', 'livetest.css'))
child_scripts = static.File(util.resource_filename('nevow', 'livetest.js'))
child_postscripts = static.File(util.resource_filename('nevow', 'livetest-postscripts.js'))
docFactory = loaders.stan(tags.html[
tags.head[
tags.script(src="scripts"),
tags.link(rel="stylesheet", type="text/css", href="css")],
tags.body[
tags.table(id="testprogress")[
tags.tr[
tags.th["Tests"], tags.th["Pass"], tags.th["Fail"]],
tags.tr[
tags.td(id="test-status")["Running"],
tags.td(id="test-passes", _class="test-passes")[entities.nbsp],
tags.td(id="test-failures", _class="test-failures")[entities.nbsp]]],
tags.table(id="testresults", render=tags.directive('sequence'))[
tags.tr(pattern="item", render=tags.directive('test'))[
tags.td(title=tags.slot('action'))[tags.slot('action')],
tags.td(title=tags.slot('target'))[tags.slot('target')],
tags.td(title=tags.slot('parameter'))[tags.slot('parameter')]]],
tags.iframe(id="testframe", src="asdf"),
tags.script(src="postscripts"),
livepage.glue]])
def beforeRender(self, ctx):
self.testId = 0
def render_test(self, ctx, test):
ctx.tag(id=("test-", self.testId))
action, target, parameter = test
ctx.fillSlots('action', action)
ctx.fillSlots('target', str(target))
ctx.fillSlots('parameter', str(parameter))
self.testId += 1
return ctx.tag
def goingLive(self, ctx, handle):
Driver(handle, self.original)
class ChildXPath(rend.Page):
docFactory = loaders.stan(
tags.html[
tags.body[
tags.div[
tags.span[
tags.div(id='target-node-identifier')[
'expected content']]]]])
def thingThatPasses(_):
return defer.succeed(None)
def thingThatFails(_):
return defer.fail(None)
class TestTests(rend.Page):
addSlash = True
docFactory = loaders.stan(tags.html[tags.a(href="/testtests/tests/")["Run tests"]])
child_foo = '<html><body><div id="body">foo</div><form method="POST", name="theForm" action="postTarget"><input name="blah" /></form></body></html>'
child_bar = "bar"
child_baz = '<html><body onclick="alert(event.clientX);alert( event.clientY);"><div id="body">toot</div><a id="nextPage" href="foo" onclick="alert(\'clicked\')">Foo</a></body></html>'
child_clickHandler = """<html>
<body>
<a id="theClicker" onclick="this.innerHTML='Clicked'">Click me!</a>
</body>
</html>"""
def child_postTarget(self, ctx):
return rend.Page(
docFactory=loaders.stan(
tags.html[tags.body(id="body")[str(ctx.arg('blah'))]]))
def child_testtests(self, ctx):
return self
def child_xpath(self, ctx):
## print 'lkfjasldkjasd!!!!!!!!'
return ChildXPath()
child_tests = Tester([
('visit', '/testtests/xpath', ''),
('assert', xpath('/html/body/div/span/div[@id="target-node-identifier"]'), 'expected content'),
('visit', '/testtests/foo', ''),
('visit', '/testtests/bar', ''),
('visit', '/testtests/baz', ''),
('assert', 'body', 'toot'),
('follow', 'nextPage', ''),
('assert', 'body', 'foo'),
('post', 'theForm', dict(blah="blah")),
('assert', 'body', 'blah'),
('visit', '/testtests/clickHandler', ''),
('click', 'theClicker', ''),
('assert', 'theClicker', 'Clicked'),
('call', thingThatPasses, ()),
('fail', thingThatFails, ())
])
def createResource():
return TestTests()
|