Mailing List Archive

SVN: zope.tutorial/trunk/ A really, really bad attempt to get the tutorial working on a sample
Log message for revision 40291:
A really, really bad attempt to get the tutorial working on a sample
doctest. But once you install jsonserver, it will work. I have some new
ideas, so the next revision should be *much* cleaner.



Changed:
U zope.tutorial/trunk/browser/configure.zcml
U zope.tutorial/trunk/browser/index.pt
A zope.tutorial/trunk/browser/runner_macros.pt
A zope.tutorial/trunk/browser/tutorial-browser.js
A zope.tutorial/trunk/browser/tutorial.css
A zope.tutorial/trunk/browser/tutorial.py
A zope.tutorial/trunk/browser/tutorials-runner.js
U zope.tutorial/trunk/cli.py
U zope.tutorial/trunk/configure.zcml
U zope.tutorial/trunk/interfaces.py
U zope.tutorial/trunk/manager.py
A zope.tutorial/trunk/runner.py
A zope.tutorial/trunk/sample_tutorial.txt
D zope.tutorial/trunk/selenium.py
U zope.tutorial/trunk/session.txt
A zope.tutorial/trunk/testbrowser.py
U zope.tutorial/trunk/tutorial.py
U zope.tutorial/trunk/tutorials.zcml

-=-
Modified: zope.tutorial/trunk/browser/configure.zcml
===================================================================
--- zope.tutorial/trunk/browser/configure.zcml 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/browser/configure.zcml 2005-11-21 03:46:47 UTC (rev 40291)
@@ -1,6 +1,7 @@
<configure
xmlns="http://namespaces.zope.org/browser"
xmlns:zope="http://namespaces.zope.org/zope"
+ xmlns:jsonrpc="http://namespaces.zope.org/jsonrpc"
i18n_domain="zope">

<resource
@@ -8,6 +9,16 @@
file="tutorial.css"
/>

+ <resource
+ name="tutorials-runner.js"
+ file="tutorials-runner.js"
+ />
+
+ <resource
+ name="tutorial-browser.js"
+ file="tutorial-browser.js"
+ />
+
<page
for="*"
name="tutorial_macros"
@@ -31,6 +42,22 @@
permission="zope.View"
/>

+ <!-- JSON Methods -->
+
+ <jsonrpc:view
+ for="..interfaces.ITutorialSessionManager"
+ methods="createSession deleteSession"
+ class=".tutorial.TutorialSessionManager"
+ permission="zope.View"
+ />
+
+ <jsonrpc:view
+ for="..interfaces.ITutorialSession"
+ methods="getNextStep setCommandResult keepGoing"
+ class=".tutorial.TutorialSession"
+ permission="zope.View"
+ />
+
<!-- Make Selenium available -->
<resourceDirectory
name="selenium"

Modified: zope.tutorial/trunk/browser/index.pt
===================================================================
--- zope.tutorial/trunk/browser/index.pt 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/browser/index.pt 2005-11-21 03:46:47 UTC (rev 40291)
@@ -6,39 +6,30 @@
href="/@@/tutorial.css" />
<script
language="JavaScript" type="text/javascript"
- src="/@@/selenium/jsunit/app/jsUnitCore.js"></script>
+ src="/@@/selenium/htmlutils.js"></script>
<script
language="JavaScript" type="text/javascript"
- src="/@@/selenium/xmlextras.js"></script>
- <script
- language="JavaScript" type="text/javascript"
src="/@@/selenium/selenium-browserbot.js"></script>
<script
language="JavaScript" type="text/javascript"
- src="/@@/selenium/selenium-api.js"></script>
+ src="/@@/selenium/xpath.js"></script>
<script
language="JavaScript" type="text/javascript"
- src="/@@/selenium/selenium-commandhandlers.js"></script>
+ src="/@@/jsolait/init.js"></script>
<script
language="JavaScript" type="text/javascript"
- src="/@@/selenium/selenium-executionloop.js"></script>
+ src="/@@/jsolait/lib/urllib.js"></script>
<script
language="JavaScript" type="text/javascript"
- src="/@@/selenium/selenium-seleneserunner.js"></script>
+ src="/@@/jsolait/lib/jsonrpc.js"></script>
<script
language="JavaScript" type="text/javascript"
- src="/@@/selenium/selenium-logging.js"></script>
+ src="/@@/tutorial-browser.js"></script>
<script
language="JavaScript" type="text/javascript"
- src="/@@/selenium/htmlutils.js"></script>
- <script
- language="JavaScript" type="text/javascript"
- src="/@@/selenium/xpath.js"></script>
- <script
- language="JavaScript" type="text/javascript"
- src="/@@/selenium/user-extensions.js"></script>
+ src="/@@/tutorials-runner.js"></script>
</head>
- <body>
+ <body onload="javascript:initializeTutorialRunner()">

<table width="100%" style="height: 100%;">
<tr>
@@ -46,13 +37,12 @@
<metal:block use-macro="context/@@tutorial_macros/runner" />
</td>
<td width="70%" height="30%">
- <b>Last Four Commands</b><br/>
- <div id="commandList"></div>
+ <div id="tutorial-text"></div>
</td>
</tr>
<tr>
<td colspan="2" height="70%">
- <iframe name="myiframe" id="myiframe" src=""
+ <iframe name="tutorial-content" id="tutorial-content" src=""
height="100%" width="100%"></iframe>
</td>
</tr>

Added: zope.tutorial/trunk/browser/runner_macros.pt
===================================================================
--- zope.tutorial/trunk/browser/runner_macros.pt 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/browser/runner_macros.pt 2005-11-21 03:46:47 UTC (rev 40291)
@@ -0,0 +1,30 @@
+<metal:block define-macro="runner">
+
+ <h1 class="main">Tutorials Runner</h1>
+
+ <div style="padding: 3px">
+ <p>
+ Please select one of the tutorials:
+ </p>
+
+ <div style="text-align: center">
+ <select id="tutorial-selector" style="width: 80%">
+ <option tal:repeat="tutorial context/values"
+ tal:attributes="value tutorial/zope:name"
+ tal:content="tutorial/title">
+ Tutorial 1
+ </option>
+ </select>
+ </div>
+
+ <p style="text-align: center">
+ <input type="submit" id="start-button" name="START" value="Start"
+ onclick="javascript:startTutorial()"/>
+ <input type="submit" id="stop-button" name="STOP" value="Stop"
+ style="display: None" onclick="javascript:stopTutorial()"/>
+ <input type="submit" id="next-button" name="NEXT" value="Next"
+ style="display: None" onclick="javascript:runNextStep()"/>
+ </p>
+ </div>
+
+</metal:block>
\ No newline at end of file


Property changes on: zope.tutorial/trunk/browser/runner_macros.pt
___________________________________________________________________
Name: svn:eol-style
+ native

Added: zope.tutorial/trunk/browser/tutorial-browser.js
===================================================================
--- zope.tutorial/trunk/browser/tutorial-browser.js 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/browser/tutorial-browser.js 2005-11-21 03:46:47 UTC (rev 40291)
@@ -0,0 +1,100 @@
+var commands = {};
+
+function getContentDocument() {
+ return frames['tutorial-content'].document;
+}
+
+function setHighlighted(element) {
+ element.style.border = "1px red solid";
+}
+
+function setOriginal(element, original) {
+ element.style.border = original;
+}
+
+function blinkElement(element) {
+ var original = element.style.border;
+
+ frames['tutorial-content'].setTimeout("setHighlighted(element)", 1000);
+ frames['tutorial-content'].setTimeout("setOriginal(element, original)", 1000);
+}
+
+function nullAction() {
+}
+commands['nullAction'] = nullAction;
+
+function displayText(text) {
+ div = bot.locateElementByIdentifier('tutorial-text', document);
+ div.innerHTML = text;
+}
+commands['displayText'] = displayText;
+
+
+function finishTutorial() {
+ displayText(Array(''));
+ stopTutorial();
+}
+commands['finishTutorial'] = finishTutorial
+
+/* ------------------------------------------------------------------------ */
+
+function openUrl(url) {
+ iframe = bot.locateElementByIdentifier('tutorial-content', document);
+ iframe.contentDocument.location.href = url;
+ return url
+}
+commands['openUrl'] = openUrl;
+
+function getUrl() {
+ iframe = bot.locateElementByIdentifier('tutorial-content', document);
+ return iframe.contentDocument.location.href;
+}
+commands['getUrl'] = getUrl;
+
+function getTitle() {
+ iframe = bot.locateElementByIdentifier('tutorial-content', document);
+ return iframe.contentDocument.title;
+}
+commands['getTitle'] = getTitle;
+
+function getContent() {
+ // TODO: There seems to be no way of getting the entire source. Sigh. :-(
+ iframe = bot.locateElementByIdentifier('tutorial-content', document);
+ return iframe.contentDocument.body.innerHTML;
+}
+commands['getContent'] = getContent;
+
+function reload() {
+ iframe = bot.locateElementByIdentifier('tutorial-content', document);
+ return iframe.contentDocument.location.reload()
+}
+commands['reload'] = reload;
+
+function goBack(steps) {
+ for (x=0; x<steps; x++) {
+ frames['tutorial-content'].history.back()
+ }
+}
+commands['goBack'] = goBack;
+
+
+
+function clickLink(text, url, id) {
+ if (text) {
+ link = contentBot.findElementByTagNameAndText(
+ getContentDocument(), 'a', text);
+ }
+
+ if (url) {
+ link = contentBott.findElementByTagNameAndAttributeValue(
+ getContentDocument(), 'a', 'href', url);
+ }
+
+ if (id) {
+ link = contentBot.locateElementByIdentifier(
+ 'tutorial-content', getContentDocument());
+ }
+ blinkElement(link);
+ contentBot.clickElement(link);
+}
+commands['clickLink'] = clickLink;

Added: zope.tutorial/trunk/browser/tutorial.css
===================================================================
--- zope.tutorial/trunk/browser/tutorial.css 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/browser/tutorial.css 2005-11-21 03:46:47 UTC (rev 40291)
@@ -0,0 +1,47 @@
+html {
+ height: 100%;
+}
+
+body {
+ font-family: Verdana, Helvetica, Arial, sans-serif;
+ font-size: 0.8em;
+ margin: 0;
+ padding: 0;
+ overflow: auto;
+}
+
+div, p {
+ font-size: 0.8em;
+}
+
+td {
+ padding: 0;
+ margin: 0;
+ position: static;
+ border: 2px solid #2475BB;
+}
+
+tr {
+ vertical-align: top;
+}
+
+iframe {
+ width: 100%;
+ height: 100%;
+ border: 0;
+ background: white;
+ overflow: auto;
+}
+
+/* --- Style --- */
+
+h1.main {
+ color: white;
+ font-size: 100%;
+ background-color: #2475BB;
+ padding: 2px;
+}
+
+#tutorial-text {
+ padding: 1em;
+}
\ No newline at end of file

Added: zope.tutorial/trunk/browser/tutorial.py
===================================================================
--- zope.tutorial/trunk/browser/tutorial.py 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/browser/tutorial.py 2005-11-21 03:46:47 UTC (rev 40291)
@@ -0,0 +1,87 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tutorial and Tutorial Manager views
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import thread
+import types
+import time
+import zope.proxy
+
+from jsonserver.jsonrpc import MethodPublisher
+from zope.app.basicskin.standardmacros import StandardMacros
+from zope.app.apidoc.utilities import renderText
+
+from zope.tutorial import testbrowser
+import zope.testbrowser
+
+class TutorialMacros(StandardMacros):
+ """Page Template METAL macros for Tutorial"""
+ macro_pages = ('runner_macros',)
+
+
+class TutorialsRunner(object):
+
+ pass
+
+
+class TutorialSessionManager(MethodPublisher):
+
+ def createSession(self):
+ name = self.context.createSession()
+ self.context[name].initialize()
+ return name
+
+ def deleteSession(self, id):
+ self.context.deleteSession(id)
+
+
+def run(tutorial, example):
+ OldBrowser = zope.testbrowser.Browser
+ zope.testbrowser.Browser = testbrowser.Browser
+ exec compile(example.source, '<string>', "single") in tutorial.globs
+ # Eek, gotta remove the __builtins__
+ del tutorial.globs['__builtins__']
+ tutorial.locked = False
+ zope.testbrowser.Browser = OldBrowser
+
+
+class TutorialSession(MethodPublisher):
+
+ def getNextStep(self):
+ tutorial = zope.proxy.removeAllProxies(self.context)
+ step = tutorial.getNextStep()
+ if isinstance(step, types.StringTypes):
+ text = renderText(step, format='zope.source.rest')
+ return {'action': 'displayText',
+ 'params': (text,)}
+ elif step is None:
+ return {'action': 'finishTutorial',
+ 'params': ()}
+ else:
+ tutorial.locked = True
+ testbrowser.State.reset()
+ thread.start_new_thread(run, (tutorial, step))
+ while tutorial.locked and not testbrowser.State.hasAction():
+ time.sleep(0.1)
+ return testbrowser.State.action
+
+ def setCommandResult(self, result):
+ testbrowser.State.result = result
+ return True
+
+ def keepGoing(self):
+ return self.context.keepGoing()


Property changes on: zope.tutorial/trunk/browser/tutorial.py
___________________________________________________________________
Name: svn:eol-style
+ native

Added: zope.tutorial/trunk/browser/tutorials-runner.js
===================================================================
--- zope.tutorial/trunk/browser/tutorials-runner.js 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/browser/tutorials-runner.js 2005-11-21 03:46:47 UTC (rev 40291)
@@ -0,0 +1,82 @@
+var CurrentTutorial = '';
+var SessionId = '';
+var ServerConnection = null;
+
+var bot = null;
+var contentBot = null;
+
+function toggleDisplayElement(id) {
+
+ element = bot.locateElementByIdentifier(id, document)
+
+ // Reference the style ...
+ if (element.style) {
+ style = element.style;
+ }
+
+ if (typeof(style.display) == 'undefined' &&
+ !( window.ScriptEngine && ScriptEngine().indexOf('InScript') + 1 ) ) {
+ //The browser does not allow us to change the display style
+ //Alert something sensible (not what I have here ...)
+ window.alert( 'Your browser does not support this' );
+ return;
+ }
+
+ // Change the display style
+ if (style.display != '') {
+ style.display = '';
+ }
+ else {
+ style.display = 'none'
+ }
+}
+
+function initializeTutorialRunner() {
+ /* Create bots */
+ bot = PageBot.createForWindow(window);
+ contentBot = PageBot.createForWindow(frames['tutorial-content']);
+}
+
+function startTutorial() {
+ CurrentTutorial = bot.locateElementByIdentifier(
+ 'tutorial-selector', document).value;
+ toggleDisplayElement('start-button');
+ toggleDisplayElement('stop-button');
+ toggleDisplayElement('next-button');
+
+ /* Create a session */
+ var addr = document.URL + CurrentTutorial + '/++sessions++/'
+ var jsonrpc = importModule("jsonrpc");
+ var server = new jsonrpc.ServiceProxy(addr, ['createSession']);
+ SessionId = server.createSession()
+
+ /* Create a new server connection to the session */
+ var addr = document.URL + CurrentTutorial + '/++sessions++' + SessionId
+ ServerConnection = new jsonrpc.ServiceProxy(
+ addr, ['getNextStep', 'setCommandResult', 'keepGoing']);
+}
+
+function stopTutorial() {
+ /* Delete the session */
+ addr = document.URL + CurrentTutorial + '/++sessions++/'
+ var jsonrpc = importModule("jsonrpc");
+ var server = new jsonrpc.ServiceProxy(addr, ['deleteSession']);
+ server.deleteSession(SessionId);
+
+ CurrentTutorial = '';
+ SessionId = '';
+ ServerConnection = null;
+ toggleDisplayElement('start-button');
+ toggleDisplayElement('stop-button');
+ toggleDisplayElement('next-button');
+}
+
+function runNextStep() {
+ var keepGoing = true;
+ while (keepGoing) {
+ command = ServerConnection.getNextStep();
+ result = commands[command.action].apply(null, command.params);
+ answer = ServerConnection.setCommandResult(result);
+ keepGoing = ServerConnection.keepGoing();
+ }
+}

Modified: zope.tutorial/trunk/cli.py
===================================================================
--- zope.tutorial/trunk/cli.py 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/cli.py 2005-11-21 03:46:47 UTC (rev 40291)
@@ -19,7 +19,7 @@
import types
import zope.interface

-from zope.tutorial import interfaces
+from zope.tutorial import interfaces, runner

class SimpleCLIController(object):
"""A dummy Command-line based controller.
@@ -77,3 +77,15 @@
self.end()
else:
self.run(step)
+
+
+class ExecutingCLIController(SimpleCLIController):
+
+ def __init__(self, session):
+ super(ExecutingCLIController, self).__init__(session)
+ self.erunner = runner.ExampleRunner(session.globs)
+
+ def run(self, example):
+ """See interfaces.ITutorialController"""
+ super(ExecutingCLIController, self).run(example)
+ self.erunner.run(example)

Modified: zope.tutorial/trunk/configure.zcml
===================================================================
--- zope.tutorial/trunk/configure.zcml 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/configure.zcml 2005-11-21 03:46:47 UTC (rev 40291)
@@ -2,23 +2,78 @@
xmlns="http://namespaces.zope.org/zope"
i18n_domain="zope">

+ <!-- Tutorial Configuration -->
+
+ <content class=".tutorial.Tutorial">
+ <require
+ permission="zope.View"
+ interface=".interfaces.ITutorial"
+ />
+ </content>
+
+ <!-- Tutorial Manager Configuration -->
+
+ <content class=".manager.TutorialManager">
+ <implements
+ interface="zope.app.annotation.interfaces.IAttributeAnnotatable"
+ />
+ <require
+ permission="zope.View"
+ interface="zope.app.container.interfaces.IReadContainer"
+ />
+ </content>
+
<!-- ++tutorials++ Namespace Registration -->

<view
name="tutorials" type="*"
provides="zope.app.traversing.interfaces.ITraversable" for="*"
- factory=".tutorial.tutorialsNamespace"
+ factory=".manager.tutorialsNamespace"
/>

<adapter
name="tutorials"
provides="zope.app.traversing.interfaces.ITraversable" for="*"
- factory=".tutorial.tutorialsNamespace"
+ factory=".manager.tutorialsNamespace"
/>

+ <!-- Tutorial Session Configuration -->
+
+ <content class=".tutorial.TutorialSession">
+ <require
+ permission="zope.View"
+ interface=".interfaces.ITutorialSession"
+ />
+ </content>
+
+ <!-- Tutorial Session Manager Configuration -->
+
+ <content class=".tutorial.TutorialSessionManager">
+ <require
+ permission="zope.View"
+ interface=".interfaces.ITutorialSessionManager"
+ />
+ </content>
+
+ <!-- ++sessions++ Namespace Registration -->
+
+ <view
+ name="sessions" type="*"
+ for=".interfaces.ITutorial"
+ provides="zope.app.traversing.interfaces.ITraversable"
+ factory=".tutorial.sessionsNamespace"
+ />
+
+ <adapter
+ name="sessions"
+ for=".interfaces.ITutorial"
+ provides="zope.app.traversing.interfaces.ITraversable"
+ factory=".tutorial.sessionsNamespace"
+ />
+
<!-- Setup of initial tutorials -->

- <!--include file="tutorials.zcml" /-->
+ <include file="tutorials.zcml" />

<!-- Browser Configuration -->


Modified: zope.tutorial/trunk/interfaces.py
===================================================================
--- zope.tutorial/trunk/interfaces.py 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/interfaces.py 2005-11-21 03:46:47 UTC (rev 40291)
@@ -19,7 +19,7 @@

import zope.interface
import zope.schema
-from zope.app.container import interfaces
+from zope.app.container import interfaces, constraints


class ITutorialManager(interfaces.IReadContainer):
@@ -46,3 +46,38 @@
title=u'File Path',
description=u'Path to the file used for the tutorial',
required=True)
+
+
+class ITutorialSessionManager(interfaces.IContainer):
+ """Tutorial Session Manager
+
+ The tutorial sessoin manager keeps track of all sessions for a given
+ tutorial.
+ """
+ constraints.contains('.ITutorialSession')
+
+ def createSession():
+ """Create a session and return its name."""
+
+ def deleteSession(name):
+ """Delete a session for the given name."""
+
+
+class ITutorialSession(interfaces.IContained):
+ """Tutorial Session
+
+ The session keeps track of the state of the tutorial for the user.
+ """
+ constraints.containers(ITutorialSessionManager)
+
+ def initialize():
+ """Initialize the session."""
+
+ def getNextStep():
+ """Return the next step in the tutorial.
+
+ Can be text or an example.
+ """
+
+ def keepGoing():
+ """ """

Modified: zope.tutorial/trunk/manager.py
===================================================================
--- zope.tutorial/trunk/manager.py 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/manager.py 2005-11-21 03:46:47 UTC (rev 40291)
@@ -37,18 +37,15 @@
"""See zope.app.container.interfaces.IReadContainer"""
utility = zapi.queryUtility(interfaces.ITutorial, key, default)
if utility != default:
- location.locate(utility, self, key)
+ utility = location.LocationProxy(utility, self, key)
return utility

def items(self):
"""See zope.app.container.interfaces.IReadContainer"""
items = list(zapi.getUtilitiesFor(interfaces.ITutorial))
items.sort()
- utils = []
- for key, value in items:
- location.locate(value, self, key)
- utils.append((key, value))
- return utils
+ return [(name, location.LocationProxy(tutorial, self, name))
+ for name, tutorial in items]


class tutorialsNamespace(object):

Added: zope.tutorial/trunk/runner.py
===================================================================
--- zope.tutorial/trunk/runner.py 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/runner.py 2005-11-21 03:46:47 UTC (rev 40291)
@@ -0,0 +1,40 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Simple Text Controller implementation.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+from zope.testing import doctest
+
+
+class PermissiveOutputChecker(object):
+
+ def check_output(self, want, got, optionflags):
+ return True
+
+
+class ExampleRunner(doctest.DocTestRunner):
+ """Example Runner"""
+
+ def __init__(self, globs, checker=None, verbose=None, optionflags=0):
+ if checker is None:
+ checker = PermissiveOutputChecker()
+ doctest.DocTestRunner.__init__(self, checker, verbose, optionflags)
+ self.globs = globs
+
+ def run(self, example, compileflags=None, out=None):
+ """ """
+ test = doctest.DocTest([example], self.globs, '', '', 0, '')
+ return doctest.DocTestRunner.run(self, test, clear_globs=False)


Property changes on: zope.tutorial/trunk/runner.py
___________________________________________________________________
Name: svn:eol-style
+ native

Added: zope.tutorial/trunk/sample_tutorial.txt
===================================================================
--- zope.tutorial/trunk/sample_tutorial.txt 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/sample_tutorial.txt 2005-11-21 03:46:47 UTC (rev 40291)
@@ -0,0 +1,16 @@
+Hello World
+===========
+
+Hi there.
+
+ >>> from zope.testbrowser import Browser
+ >>> browser = Browser()
+ >>> browser.open('http://localhost:8080/manage')
+ >>> browser.url
+ >>> browser.title
+ >>> browser.contents
+ >>> browser.reload()
+ >>> browser.getLink('Buddy Folder').click()
+ >>> browser.goBack()
+
+That's it!
\ No newline at end of file


Property changes on: zope.tutorial/trunk/sample_tutorial.txt
___________________________________________________________________
Name: svn:eol-style
+ native

Deleted: zope.tutorial/trunk/selenium.py
===================================================================
--- zope.tutorial/trunk/selenium.py 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/selenium.py 2005-11-21 03:46:47 UTC (rev 40291)
@@ -1,77 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2005 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Selenium-based Test Browser
-
-$Id$
-"""
-__docformat__ = "reStructuredText"
-import zope.interface
-
-#class SeleniumBrowser(SetattrErrorsMixin):
-# """A web user agent."""
-# zope.interface.implements(interfaces.IBrowser)
-#
-# _contents = None
-# _counter = 0
-#
-# def __init__(self, url=None, mech_browser=None):
-# if mech_browser is None:
-# mech_browser = mechanize.Browser()
-# self.mech_browser = mech_browser
-# if url is not None:
-# self.open(url)
-# self.timer = PystoneTimer()
-# self._enable_setattr_errors = True
-#
-# @property
-# def url(self):
-# """See zope.testbrowser.interfaces.IBrowser"""
-# return self.mech_browser.geturl()
-#
-# @property
-# def isHtml(self):
-# """See zope.testbrowser.interfaces.IBrowser"""
-# return self.mech_browser.viewing_html()
-#
-# @property
-# def title(self):
-# """See zope.testbrowser.interfaces.IBrowser"""
-# return self.mech_browser.title()
-#
-# @property
-# def contents(self):
-# """See zope.testbrowser.interfaces.IBrowser"""
-# if self._contents is not None:
-# return self._contents
-# response = self.mech_browser.response()
-# old_location = response.tell()
-# response.seek(0)
-# for line in iter(lambda: response.readline().strip(), ''):
-# pass
-# self._contents = response.read()
-# response.seek(old_location)
-# return self._contents
-#
-# @property
-# def headers(self):
-# """See zope.testbrowser.interfaces.IBrowser"""
-# return self.mech_browser.response().info()
-#
-# def open(self, url, data=None):
-# """See zope.testbrowser.interfaces.IBrowser"""
-# self._start_timer()
-# self.mech_browser.open(url, data)
-# self._stop_timer()
-# self._changed()
-#

Modified: zope.tutorial/trunk/session.txt
===================================================================
--- zope.tutorial/trunk/session.txt 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/session.txt 2005-11-21 03:46:47 UTC (rev 40291)
@@ -20,8 +20,8 @@
...
... And now a variable assignment with a return value:
...
- ... >>> id = 5
- ... >>> id
+ ... >>> num = 5
+ ... >>> num
... 5
...
... That's it!
@@ -68,10 +68,45 @@
<BLANKLINE>
And now a variable assignment with a return value:
<BLANKLINE>
- Py: id = 5
- Py: id
+ Py: num = 5
+ Py: num
5
<BLANKLINE>
That's it!
<BLANKLINE>
---------- The End ----------
+
+Next let's try a little bit more interesting. There is also a CLI controller
+that actually executes the examples:
+
+ >>> session = tutorial.TutorialSession(sample)
+ >>> session.initialize()
+
+ >>> controller = cli.ExecutingCLIController(session)
+ >>> controller.PYTHON_PROMPT = 'Py: '
+
+ >>> def run():
+ ... controller.start()
+ ... while controller.running:
+ ... controller.doNextStep()
+
+ >>> run()
+ Starting Tutorial: Sample Documentation
+ <BLANKLINE>
+ Sample Documentation
+ ====================
+ <BLANKLINE>
+ Here is a simple print statement:
+ <BLANKLINE>
+ Py: print 'sample'
+ sample
+ <BLANKLINE>
+ And now a variable assignment with a return value:
+ <BLANKLINE>
+ Py: num = 5
+ Py: num
+ 5
+ <BLANKLINE>
+ That's it!
+ <BLANKLINE>
+ ---------- The End ----------

Added: zope.tutorial/trunk/testbrowser.py
===================================================================
--- zope.tutorial/trunk/testbrowser.py 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/testbrowser.py 2005-11-21 03:46:47 UTC (rev 40291)
@@ -0,0 +1,227 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test Browser implementation that works with the Tutorial Runner
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import time
+import zope.interface
+from zope import testbrowser
+
+
+NORESULT = object()
+NOACTION = {'action': 'nullAction', 'params': ()}
+
+# TODO: Make this user specific later; this should be really stored in the
+# session, but the test browser does not know about the session :-(
+class State(object):
+ __slots__ = ('result', 'action')
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.result = NORESULT
+ self.action = NOACTION
+
+ def hasAction(self):
+ return self.action is not NOACTION
+
+ def hasResult(self):
+ return self.result is not NORESULT
+
+ def executeAction(self, action, *args):
+ self.result = None
+ self.action = {'action': action, 'params': args}
+ # wait for the answer to come in
+ while self.result is NORESULT:
+ time.sleep(0.5)
+ return self.result
+
+ def __getattr__(self, name):
+ def action(*args):
+ return self.executeAction(name, *args)
+ return action
+
+State = State()
+
+
+class Browser(testbrowser.browser.SetattrErrorsMixin):
+ """ """
+ zope.interface.implements(testbrowser.interfaces.IBrowser)
+
+ _contents = None
+ _counter = 0
+
+ def __init__(self, url=None):
+ self.timer = testbrowser.browser.PystoneTimer()
+ if url:
+ self.open(url)
+
+ @property
+ def url(self):
+ """See zope.testbrowser.interfaces.IBrowser"""
+ return State.getUrl()
+
+ @property
+ def isHtml(self):
+ """See zope.testbrowser.interfaces.IBrowser"""
+ # TODO: It is always HTML for now ;-)
+ return True
+
+ @property
+ def title(self):
+ """See zope.testbrowser.interfaces.IBrowser"""
+ return State.getTitle()
+
+ @property
+ def contents(self):
+ """See zope.testbrowser.interfaces.IBrowser"""
+ if self._contents is None:
+ self._contents = State.getContent()
+ return self._contents
+
+ @property
+ def headers(self):
+ """See zope.testbrowser.interfaces.IBrowser"""
+ # TODO: How do we get those? Can we get them at all?
+ return []
+
+ # See zope.testbrowser.interfaces.IBrowser
+ # TODO: We cannot copy this functionality here.
+ handleErrors = False
+
+ def open(self, url, data=None):
+ """See zope.testbrowser.interfaces.IBrowser"""
+ self._start_timer()
+ State.openUrl(url, data)
+ self._stop_timer()
+ self._changed()
+
+ def _start_timer(self):
+ self.timer.start()
+
+ def _stop_timer(self):
+ self.timer.stop()
+
+ @property
+ def last_request_pystones(self):
+ return self.timer.elapsed_pystones
+
+ @property
+ def last_request_seconds(self):
+ return self.timer.elapsed_seconds
+
+ def reload(self):
+ """See zope.testbrowser.interfaces.IBrowser"""
+ self._start_timer()
+ State.reload()
+ self._stop_timer()
+ self._changed()
+
+ def goBack(self, count=1):
+ """See zope.testbrowser.interfaces.IBrowser"""
+ self._start_timer()
+ State.goBack(count)
+ self._stop_timer()
+ self._changed()
+
+ def addHeader(self, key, value):
+ """See zope.testbrowser.interfaces.IBrowser"""
+ # TODO: How can this be done?
+ #self.mech_browser.addheaders.append( (key, value) )
+
+ def getLink(self, text=None, url=None, id=None):
+ return Link(self, text, url, id)
+
+ def getControl(self, label=None, name=None, index=None):
+ """See zope.testbrowser.interfaces.IBrowser"""
+ return Control(self, label, name, index)
+
+ def getForm(self, id=None, name=None, action=None, index=None):
+ zeroOrOne([id, name, action], '"id", "name", and "action"')
+ if index is None and not any([id, name, action]):
+ raise ValueError(
+ 'if no other arguments are given, index is required.')
+
+ matching_forms = []
+ for form in self.mech_browser.forms():
+ if ((id is not None and form.attrs.get('id') == id)
+ or (name is not None and form.name == name)
+ or (action is not None and re.search(action, str(form.action)))
+ or id == name == action == None):
+ matching_forms.append(form)
+
+ form = disambiguate(matching_forms, '', index)
+ self.mech_browser.form = form
+ return Form(self, form)
+
+ def _changed(self):
+ self._counter += 1
+ self._contents = None
+
+
+class Link(object):
+ zope.interface.implements(testbrowser.interfaces.ILink)
+
+ def __init__(self, browser, text=None, url=None, id=None):
+ self.browser = browser
+ self._text = text
+ self._url = url
+ self._id = id
+
+ self._info = None
+
+ def click(self):
+ return State.executeAction('clickLink', self._text, self._url, self._id)
+
+ def getInfo(self):
+ if self._info is None:
+ self._info = State.executeAction(
+ 'getLinkInfo', self._text, self._url, self._id)
+ return self._info
+
+ @property
+ def url(self):
+ return self.getInfo()['href']
+
+ @property
+ def text(self):
+ return self.getInfo()['text']
+
+ @property
+ def tag(self):
+ return self.getInfo()['tag']
+
+ @property
+ def attrs(self):
+ return self._info
+
+ def __repr__(self):
+ return "<%s text=%r url=%r id=%s>" % (
+ self.__class__.__name__, self._text, self._url, self._id)
+
+
+class Control(object):
+
+ def __init__(self, browser, label=None, name=None, index=None):
+ self.browser = browser
+ self._label = label
+ self._name = name
+ self._index = index
+
+ def click(self):
+ return State.executeAction('clickControl', self._text, self._url, self._id)


Property changes on: zope.tutorial/trunk/testbrowser.py
___________________________________________________________________
Name: svn:eol-style
+ native

Modified: zope.tutorial/trunk/tutorial.py
===================================================================
--- zope.tutorial/trunk/tutorial.py 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/tutorial.py 2005-11-21 03:46:47 UTC (rev 40291)
@@ -22,10 +22,19 @@
import types
import zope.component
import zope.interface
+import zope.proxy
+from zope.app import annotation
+from zope.app import zapi
+from zope.app.component import hooks
+from zope.app.container import btree
+from zope.app.location import location

from zope.tutorial import interfaces


+SessionManagerKey = 'zope.tutorial.SessionManager'
+
+
class Tutorial(object):
"""Tutorial"""
zope.interface.implements(interfaces.ITutorial)
@@ -39,16 +48,17 @@
self.__class__.__name__, self.title, os.path.split(self.path)[-1])


-class TutorialSession(persistent.Persistent):
+class TutorialSession(persistent.Persistent, location.Location):
"""Tutorial Session"""

zope.component.adapts(interfaces.ITutorial)
- #zope.interface.implements(interfaces.ITutorialSession)
+ zope.interface.implements(interfaces.ITutorialSession)

+ locked = False
+
def __init__(self, tutorial):
self.tutorial = tutorial

-
def initialize(self):
"""See interfaces.ITutorialSession"""
text = open(self.tutorial.path, 'r').read()
@@ -60,14 +70,65 @@
part.strip())]
# Create a parts stack
self.parts.reverse()
+ self.current = None

# Set some runtime variables
self.globs = {}

-
def getNextStep(self):
"""See interfaces.ITutorialSession"""
+ if self.locked:
+ return None
try:
- return self.parts.pop()
+ self.current = self.parts.pop()
except IndexError:
+ self.current = None
return None
+
+ return self.current
+
+ def keepGoing(self):
+ return type(self.parts[-1]) == type(self.current)
+
+
+class TutorialSessionManager(btree.BTreeContainer):
+ """A session manager based on BTrees."""
+ zope.component.adapter(interfaces.ITutorial)
+ zope.interface.implements(interfaces.ITutorialSessionManager)
+
+ def __init__(self):
+ super(TutorialSessionManager, self).__init__()
+ self.__counter = 0
+
+ def createSession(self):
+ name = unicode(self.__counter)
+ self[name] = TutorialSession(zapi.getParent(self))
+ self.__counter += 1;
+ return name
+
+ def deleteSession(self, name):
+ del self[name]
+
+
+class sessionsNamespace(object):
+ """Used to traverse the `++sessions++` namespace"""
+
+ def __init__(self, ob=None, request=None):
+ site = hooks.getSite()
+ annotations = annotation.interfaces.IAnnotations(site)
+ manager = annotations.get(SessionManagerKey)
+
+ if manager is None:
+ manager = TutorialSessionManager()
+ tutorial = zope.proxy.removeAllProxies(ob)
+ location.locate(manager, tutorial, '++sessions++')
+ annotations[SessionManagerKey] = manager
+
+ self.sessionManager = manager
+
+
+ def traverse(self, name, ignore=None):
+ if name == '':
+ return self.sessionManager
+ else:
+ return self.sessionManager[name]

Modified: zope.tutorial/trunk/tutorials.zcml
===================================================================
--- zope.tutorial/trunk/tutorials.zcml 2005-11-21 03:29:06 UTC (rev 40290)
+++ zope.tutorial/trunk/tutorials.zcml 2005-11-21 03:46:47 UTC (rev 40291)
@@ -2,11 +2,16 @@
xmlns="http://namespaces.zope.org/zope"
i18n_domain="zope">

+ <tutorial
+ name="sample_tutorial"
+ title="Sample Tutorial"
+ path="sample_tutorial.txt" />
+
<configure package="zope.testbrowser">

<tutorial
name="testbrowser"
- title="Test Browser Features"
+ title="The Test Browser"
path="README.txt" />

</configure>

_______________________________________________
Zope-CVS maillist - Zope-CVS@zope.org
http://mail.zope.org/mailman/listinfo/zope-cvs

Zope CVS instructions: http://dev.zope.org/CVS