Index: main/waeup.kofa/branches/uli-scores-upload/src/waeup/kofa/students/tests/test_browser.py
===================================================================
--- main/waeup.kofa/branches/uli-scores-upload/src/waeup/kofa/students/tests/test_browser.py	(revision 13917)
+++ main/waeup.kofa/branches/uli-scores-upload/src/waeup/kofa/students/tests/test_browser.py	(revision 13918)
@@ -45,5 +45,5 @@
 from waeup.kofa.university.faculty import Faculty
 from waeup.kofa.university.department import Department
-from waeup.kofa.interfaces import IUserAccount, IJobManager
+from waeup.kofa.interfaces import IUserAccount, IJobManager, VALIDATED, CREATED
 from waeup.kofa.authentication import LocalRoleSetEvent
 from waeup.kofa.hostels.hostel import Hostel, Bed, NOT_OCCUPIED
@@ -55,4 +55,5 @@
 SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
 SAMPLE_IMAGE_BMP = os.path.join(os.path.dirname(__file__), 'test_image.bmp')
+URL_LECTURER_LANDING = 'http://localhost/app/my_courses'
 
 
@@ -3913,99 +3914,163 @@
             name="form.password").value = 'mrslecturersecret'
         self.browser.getControl("Login").click()
-
-    def test_handle_courses_by_lecturer(self):
+        # Store reused urls/paths
+        self.course_url = (
+            'http://localhost/app/faculties/fac1/dep1/courses/COURSE1')
+        self.edit_scores_url = '%s/edit_scores' % self.course_url
+        # Set standard parameters
+        self.app['configuration'].current_academic_session = 2004
+        self.app['faculties']['fac1']['dep1'].score_editing_disabled = False
+        IWorkflowState(self.student).setState(VALIDATED)
+
+    @property
+    def stud_log_path(self):
+        return os.path.join(
+            self.app['datacenter'].storage, 'logs', 'students.log')
+
+    def test_lecturer_lands_on_landing_page(self):
+        # lecturers can login and will be led to landing page.
         self.login_as_lecturer()
         self.assertMatches('...You logged in...', self.browser.contents)
-        # Lecturer is on landing page
-        self.assertTrue('(<span>COURSE1</span>)' in self.browser.contents)
-        # Lecturer can click the 'My Roles' link.
+        self.assertEqual(self.browser.url, URL_LECTURER_LANDING)
+
+    def test_my_roles_link_works(self):
+        # lecturers can see their roles
+        self.login_as_lecturer()
         self.browser.getLink("My Roles").click()
-        self.assertMatches(
-            '...<div>Academics Officer (view only)</div>...',
-            self.browser.contents)
-        self.assertMatches(
-            '...<a href="http://localhost/app/'
-            'faculties/fac1/dep1/courses/COURSE1">...',
-            self.browser.contents)
-        self.assertFalse('(<span>COURSE1</span>)' in self.browser.contents)
-        # Lecturer can click the 'My Courses' link ...
+        self.assertTrue(
+            "<div>Academics Officer (view only)</div>"
+            in self.browser.contents)
+        self.assertTrue(
+            '<a href="%s">' % self.course_url in self.browser.contents)
+
+    def test_my_roles_page_contains_backlink(self):
+        # we can get back from 'My Roles' view to landing page
+        self.login_as_lecturer()
+        self.browser.getLink("My Roles").click()
         self.browser.getLink("My Courses").click()
-        # ... and is is back on landing page
-        self.assertTrue('(<span>COURSE1</span>)' in self.browser.contents)
-        # The lecturer can go to her course ...
+        self.assertEqual(self.browser.url, URL_LECTURER_LANDING)
+
+    def test_lecturers_can_reach_their_courses(self):
+        # lecturers get links to their courses on the landing page
+        self.login_as_lecturer()
         self.browser.getLink("COURSE1").click()
+        self.assertEqual(self.browser.url, self.course_url)
+
+    def test_lecturers_student_access_is_restricted(self):
+        # lecturers are not able to change other student data
+        self.login_as_lecturer()
         # Lecturers can neither filter students ...
         self.assertRaises(
-            Unauthorized, self.browser.open,
-            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/students")
+            Unauthorized, self.browser.open, '%s/students' % self.course_url)
         # ... nor access the student ...
         self.assertRaises(
             Unauthorized, self.browser.open, self.student_path)
-        # ... nor the respective course ticket since
-        # editing course tickets by lecturers is not feasible.
+        # ... nor the respective course ticket since editing course
+        # tickets by lecturers is not feasible.
         self.assertTrue('COURSE1' in self.student['studycourse']['100'].keys())
         course_ticket_path = self.student_path + '/studycourse/100/COURSE1'
         self.assertRaises(
             Unauthorized, self.browser.open, course_ticket_path)
-        # Course results can be batch edited via the edit_courses view.
+
+    def test_score_editing_requires_department_permit(self):
+        # we get a warning if we try to update score while we are not allowed
+        self.login_as_lecturer()
         self.app['faculties']['fac1']['dep1'].score_editing_disabled = True
-        self.browser.open(
-            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1")
+        self.browser.open(self.course_url)
         self.browser.getLink("Update scores").click()
         self.assertTrue('Score editing disabled' in self.browser.contents)
         self.app['faculties']['fac1']['dep1'].score_editing_disabled = False
+        self.browser.open(self.course_url)
         self.browser.getLink("Update scores").click()
-        self.assertTrue(
-            'Current academic session not set' in self.browser.contents)
+        self.assertFalse('Score editing disabled' in self.browser.contents)
+
+    def test_score_editing_requires_validated_students(self):
+        # we can edit only scores of students whose courses have been
+        # validated.
+        self.login_as_lecturer()
+        # set invalid student state
+        IWorkflowState(self.student).setState(CREATED)
+        self.browser.open(self.edit_scores_url)
+        self.assertRaises(
+            LookupError, self.browser.getControl, name="scores")
+        # set valid student state
+        IWorkflowState(self.student).setState(VALIDATED)
+        self.browser.open(self.edit_scores_url)
+        self.assertTrue(self.browser.getControl(name="scores") is not None)
+
+    def test_score_editing_offers_only_current_scores(self):
+        # only scores from current academic session can be edited
+        self.login_as_lecturer()
+        IWorkflowState(self.student).setState('courses validated')
+        # with no academic session set
+        self.app['configuration'].current_academic_session = None
+        self.browser.open(self.edit_scores_url)
+        self.assertRaises(
+            LookupError, self.browser.getControl, name="scores")
+        # with wrong academic session set
+        self.app['configuration'].current_academic_session = 1999
+        self.browser.open(self.edit_scores_url)
+        self.assertRaises(
+            LookupError, self.browser.getControl, name="scores")
+        # with right academic session set
         self.app['configuration'].current_academic_session = 2004
-        self.browser.getLink("Update scores").click()
-        self.assertFalse(
-            '<input type="text" name="scores" class="span1" />'
-            in self.browser.contents)
-        IWorkflowState(self.student).setState('courses validated')
-        # Student must be in state 'courses validated'.
-        self.browser.open(
-            'http://localhost/app/faculties/fac1/'
-            'dep1/courses/COURSE1/edit_scores')
-        self.assertTrue(
-            'input type="text" name="scores"' in self.browser.contents)
+        self.browser.reload()
+        self.assertTrue(self.browser.getControl(name="scores") is not None)
+
+    def test_score_editing_can_change_scores(self):
+        # we can really change scores via edit_scores view
+        self.login_as_lecturer()
+        self.assertEqual(
+            self.student['studycourse']['100']['COURSE1'].score, None)
+        self.browser.open(self.edit_scores_url)
         self.browser.getControl(name="scores", index=0).value = '55'
         self.browser.getControl("Update scores").click()
-        # New score has been set.
+        # the new value is stored in data
         self.assertEqual(
             self.student['studycourse']['100']['COURSE1'].score, 55)
-        # Score editing has been logged.
-        logfile = os.path.join(
-            self.app['datacenter'].storage, 'logs', 'students.log')
-        logcontent = open(logfile).read()
-        self.assertTrue('mrslecturer - students.browser.EditScoresPage - '
-            'K1000000 100/COURSE1 score updated (55)' in logcontent)
+        # the new value is displayed on page/prefilled in form
+        self.assertEqual(
+            self.browser.getControl(name="scores", index=0).value, '55')
+        # The change has been logged
+        with open(self.stud_log_path, 'r') as fd:
+            self.assertTrue(
+                'mrslecturer - students.browser.EditScoresPage - '
+                'K1000000 100/COURSE1 score updated (55)' in fd.read())
+
+    def test_scores_editing_scores_must_be_integers(self):
         # Non-integer scores won't be accepted.
-        self.browser.open(
-            'http://localhost/app/faculties/fac1/'
-            'dep1/courses/COURSE1/edit_scores')
-        self.assertTrue('value="55" />' in self.browser.contents)
+        self.login_as_lecturer()
+        self.browser.open(self.edit_scores_url)
         self.browser.getControl(name="scores", index=0).value = 'abc'
         self.browser.getControl("Update scores").click()
-        self.assertTrue('Error: Score(s) of Anna Tester have not be updated'
+        self.assertTrue(
+            'Error: Score(s) of Anna Tester have not be updated'
             in self.browser.contents)
-        # Scores can be removed.
-        self.browser.open(
-            'http://localhost/app/faculties/fac1/'
-            'dep1/courses/COURSE1/edit_scores')
+
+    def test_scores_editing_allows_score_removal(self):
+        # we can remove scores, once they were set
+        self.login_as_lecturer()
+        # without a prior value, we cannot remove
+        self.student['studycourse']['100']['COURSE1'].score = None
+        self.browser.open(self.edit_scores_url)
         self.browser.getControl(name="scores", index=0).value = ''
         self.browser.getControl("Update scores").click()
+        logcontent = open(self.stud_log_path, 'r').read()
+        self.assertFalse('COURSE1 score updated (None)' in logcontent)
+        # now retry with some value set
+        self.student['studycourse']['100']['COURSE1'].score = 55
+        self.browser.getControl(name="scores", index=0).value = ''
+        self.browser.getControl("Update scores").click()
+        logcontent = open(self.stud_log_path, 'r').read()
+        self.assertTrue('COURSE1 score updated (None)' in logcontent)
+
+    def test_lecturers_can_download_course_tickets(self):
+        # A course ticket slip can be downloaded
+        self.login_as_lecturer()
+        pdf_url = '%s/coursetickets.pdf' % self.course_url
+        self.browser.open(pdf_url)
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
         self.assertEqual(
-            self.student['studycourse']['100']['COURSE1'].score, None)
-        logcontent = open(logfile).read()
-        self.assertTrue('mrslecturer - students.browser.EditScoresPage - '
-            'K1000000 100/COURSE1 score updated (None)' in logcontent)
-        # A course ticket slip can be downloaded
-        self.browser.open(
-            'http://localhost/app/faculties/fac1/'
-            'dep1/courses/COURSE1/coursetickets.pdf')
-        self.assertEqual(self.browser.headers['Status'], '200 Ok')
-        self.assertEqual(self.browser.headers['Content-Type'],
-                         'application/pdf')
+            self.browser.headers['Content-Type'], 'application/pdf')
         path = os.path.join(samples_dir(), 'coursetickets.pdf')
         open(path, 'wb').write(self.browser.contents)
