diff --git a/plone/app/discussion/testing.py b/plone/app/discussion/testing.py
new file mode 100644
index 0000000..07fc690
--- /dev/null
+++ b/plone/app/discussion/testing.py
@@ -0,0 +1,67 @@
+from Products.CMFCore.utils import getToolByName
+
+from plone.app.testing import PloneSandboxLayer
+from plone.app.testing import applyProfile
+from plone.app.testing import PLONE_FIXTURE
+from plone.app.testing import IntegrationTesting
+from plone.app.testing import FunctionalTesting
+
+from plone.testing import z2
+
+from zope.configuration import xmlconfig
+
+class PloneAppDiscussion(PloneSandboxLayer):
+
+ defaultBases = (PLONE_FIXTURE,)
+
+ USER_NAME = 'johndoe'
+ USER_PASSWORD = 'secret'
+ USER_WITH_FULLNAME_NAME = 'jim'
+ USER_WITH_FULLNAME_FULLNAME = 'Jim Fulton'
+ USER_WITH_FULLNAME_PASSWORD = 'secret'
+ MANAGER_USER_NAME = 'manager'
+ MANAGER_USER_PASSWORD = 'secret'
+
+ def setUpZope(self, app, configurationContext):
+ # Load ZCML
+ import plone.app.discussion
+ xmlconfig.file('configure.zcml',
+ plone.app.discussion,
+ context=configurationContext)
+
+ def setUpPloneSite(self, portal):
+ # Install into Plone site using portal_setup
+ applyProfile(portal, 'plone.app.discussion:default')
+
+ # Creates some users
+ acl_users = getToolByName(portal, 'acl_users')
+ acl_users.userFolderAddUser(
+ self.USER_NAME,
+ self.USER_PASSWORD,
+ ['Member'],
+ [],
+ )
+ acl_users.userFolderAddUser(
+ self.USER_WITH_FULLNAME_NAME,
+ self.USER_WITH_FULLNAME_PASSWORD,
+ ['Member'],
+ [],
+ )
+ mtool = getToolByName(portal, 'portal_membership', None)
+ mtool.addMember('jim', 'Jim', ['Member'], [])
+ mtool.getMemberById('jim').setMemberProperties({"fullname": 'Jim Fulton'})
+
+ acl_users.userFolderAddUser(
+ self.MANAGER_USER_NAME,
+ self.MANAGER_USER_PASSWORD,
+ ['Manager'],
+ [],
+ )
+
+PLONE_APP_DISCUSSION_FIXTURE = PloneAppDiscussion()
+PLONE_APP_DISCUSSION_INTEGRATION_TESTING = IntegrationTesting(
+ bases=(PLONE_APP_DISCUSSION_FIXTURE,),
+ name="PloneAppDiscussion:Integration")
+PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING = FunctionalTesting(
+ bases=(PLONE_APP_DISCUSSION_FIXTURE,),
+ name="PloneAppDiscussion:Functional")
diff --git a/plone/app/discussion/tests/functional.txt b/plone/app/discussion/tests/functional.txt
new file mode 100644
index 0000000..607212a
--- /dev/null
+++ b/plone/app/discussion/tests/functional.txt
@@ -0,0 +1,143 @@
+======================
+ plone.app.discussion
+======================
+
+This is a functional test for plone.app.discussion.
+
+We use zope.testbrowser to simulate browser interaction in order to show
+how plone.app.discussion works.
+
+
+Setting up and logging in
+-------------------------
+
+First we have to set up some things and login.
+
+ >>> app = layer['app']
+ >>> from plone.testing.z2 import Browser
+ >>> browser = Browser(app)
+ >>> browser.handleErrors = False
+ >>> browser.addHeader('Authorization', 'Basic admin:secret')
+ >>> portal = layer['portal']
+ >>> portal_url = 'http://nohost/plone'
+
+By default, only HTTP error codes (e.g. 500 Server Side Error) are shown when an
+error occurs on the server. To see more details, set handleErrors to False:
+
+ >>> browser.handleErrors = False
+
+We also keep another testbrowser handy for testing how tiles are rendered if
+you're not logged in::
+
+ >>> unprivileged_browser = Browser(app)
+
+Add a test user
+
+ >>> from Products.CMFCore.utils import getToolByName
+ >>> mtool = getToolByName(portal, 'portal_membership', None)
+ >>> mtool.addMember('jim', 'Jim', ['Member'], [])
+ >>> mtool.getMemberById('jim').setMemberProperties({"fullname": 'Jim Fulton'})
+
+Create a public page with comments allowed.
+
+ >>> browser.open(portal_url)
+ >>> browser.getLink(id='document').click()
+ >>> browser.getControl(name='title').value = "Doc1"
+ >>> browser.getControl(name='allowDiscussion:boolean').value = True
+ >>> browser.getControl(name='form.button.save').click()
+
+A>>> browser.open(portal_url + "/doc1")
+A>>> browser.getLink('Publish').click()
+ >>> urldoc1 = browser.url
+
+
+Check that the form has been properly submitted
+
+ >>> browser.url
+ 'http://nohost/plone/doc1'
+
+Comment Viewlet
+---------------
+
+Check that the old comments viewlet does not show up
+
+ >>> 'discussion_reply_form' in browser.contents
+ False
+
+Check that the comment form/viewlet shows up
+
+ >>> 'formfield-form-widgets-in_reply_to' in browser.contents
+ True
+
+ >>> 'formfield-form-widgets-text' in browser.contents
+ True
+
+
+Post a comment as admin
+-----------------------
+
+Login as admin.
+
+ >>> from plone.app.testing import setRoles
+ >>> from plone.app.testing import TEST_USER_NAME
+ >>> setRoles(portal, TEST_USER_NAME, ['Manager'])
+
+Post a comment as admin.
+
+ >>> browser.getControl(name='form.widgets.text').value = "Comment from admin"
+ >>> submit = browser.getControl(name='form.buttons.comment')
+ >>> submit.click()
+
+Check if comment has been added properly.
+
+ >>> 'admin' in browser.contents
+ True
+
+ >>> 'says' in browser.contents
+ True
+
+ >>> "Comment from admin" in browser.contents
+ True
+
+
+Post a comment as user
+----------------------
+
+Login and post comment as Jim
+
+A>>> self.failUnless("Jim says" in browser.contents)
+A>>> self.failUnless("Jim Lorem ipsum" in browser.contents)
+
+Post a comment as anonymous user
+--------------------------------
+
+Login and post comment as Anonymous
+
+ >>> unprivileged_browser.open(urldoc1)
+
+ >>> 'Log in to add comments' in unprivileged_browser.contents
+ True
+
+Enable anonymous comment
+
+ >>> browser.open(portal_url+'/@@discussion-settings')
+ >>> browser.getControl(name='form.widgets.anonymous_comments:list').value = [True]
+ >>> browser.getControl(name='form.buttons.save').click()
+
+Now we can post an anonymous comment.
+
+ >>> unprivileged_browser.open(urldoc1)
+ >>> unprivileged_browser.getControl(name='form.widgets.text').value = "This is an anonymous comment"
+ >>> unprivileged_browser.getControl(name='form.buttons.comment').click()
+
+ >>> 'Anonymous' in unprivileged_browser.contents
+ True
+
+ >>> 'says' in unprivileged_browser.contents
+ True
+
+ >>> 'This is an anonymous comment' in unprivileged_browser.contents
+ True
+
+A>>> interact( locals() )
+A>>> open('/tmp/testbrowser.html', 'w').write(unprivileged_browser.contents)
\ No newline at end of file
diff --git a/plone/app/discussion/tests/test_functional.py b/plone/app/discussion/tests/test_functional.py
new file mode 100644
index 0000000..ed21ba4
--- /dev/null
+++ b/plone/app/discussion/tests/test_functional.py
@@ -0,0 +1,27 @@
+import doctest
+import unittest2 as unittest
+import pprint
+import interlude
+
+from plone.testing import layered
+
+from plone.app.discussion.testing import \
+ PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING
+
+optionflags = (doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.REPORT_ONLY_FIRST_FAILURE)
+normal_testfiles = [
+ 'functional.txt',
+]
+
+def test_suite():
+ suite = unittest.TestSuite()
+ suite.addTests([
+ layered(doctest.DocFileSuite(test ,
+ optionflags=optionflags,
+ globs={'interact': interlude.interact,
+ 'pprint': pprint.pprint,
+ }
+ ),
+ layer=PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING)
+ for test in normal_testfiles])
+ return suite
diff --git a/setup.py b/setup.py
index a5e892b..fd449fb 100644
--- a/setup.py
+++ b/setup.py
@@ -39,6 +39,12 @@ setup(name='plone.app.discussion',
'zope.lifecycleevent',
'zope.site',
],
+ extras_require = {
+ 'test': [
+ 'plone.app.testing',
+ 'interlude',
+ ]
+ },
entry_points="""
[z3c.autoinclude.plugin]
target = plone