Index: TestHarness4LAMS2/readme.txt =================================================================== diff -u -r37d07da894922569d79ce4f517c45f8cbed4c452 -rc6ee15a5f86cbe315af4e6c2a933275fb8a1d41e --- TestHarness4LAMS2/readme.txt (.../readme.txt) (revision 37d07da894922569d79ce4f517c45f8cbed4c452) +++ TestHarness4LAMS2/readme.txt (.../readme.txt) (revision c6ee15a5f86cbe315af4e6c2a933275fb8a1d41e) @@ -24,20 +24,36 @@ TODO list: -1. Add support for any kind of activitis. +1. Add support for any kind of activities. Currently only support activities which consists of pages only with only one - "standard" form whose submit are not triggered by javascript. + "standard" form whose javascript submissions can be tweaked to work even if the + onSubmit method isn't called, and for activities (such as Forum) that have + specific coded support for their pages, or for a page with a simple single redirect. + These activities include: - 1) noticeboard - 2) grouping - 3) notebook - 4) multiple choice + * Forum (includes adding a single posting) + * Grouping + * Multiple Choice - all questions on one page only. + * Notebook + * Noticeboard + * Q and A - all questions on one page only. - Not supported are parallel activities and activities which has pages without - forms (such as Share Resources) or forms whose submission are triggered by - javascript or with multiple forms ( such as Chat and Optional) + Note: they work for the default content. Changing settings on the advanced tab + may give problems. + + Not supported: + * Parallel activities + * Chat, Optional - Multiple forms not supported. + * Share Resources - no form + * Scribe is untested + * Submit Files - needs custom code to distinguish between upload and finish buttons + * Survey + * Vote - the javascript submission fails to pass the correct method name. This + requires tweaking the pages to set the dispatch method to the appropriate method + name by default. + 2. Generate formal test report document Index: TestHarness4LAMS2/src/org/lamsfoundation/testharness/Call.java =================================================================== diff -u -r08950e1090443c3423a3d1c587416a2fccd8bbdf -rc6ee15a5f86cbe315af4e6c2a933275fb8a1d41e --- TestHarness4LAMS2/src/org/lamsfoundation/testharness/Call.java (.../Call.java) (revision 08950e1090443c3423a3d1c587416a2fccd8bbdf) +++ TestHarness4LAMS2/src/org/lamsfoundation/testharness/Call.java (.../Call.java) (revision c6ee15a5f86cbe315af4e6c2a933275fb8a1d41e) @@ -240,10 +240,15 @@ } private String getAbsoluteURL(String url){ - if(test.getTestSuite().getHttpPort()!=80) - return HTTP+test.getTestSuite().getTargetServer()+COLON+test.getTestSuite().getHttpPort()+test.getTestSuite().getContextRoot()+url; + if(url.startsWith(HTTP)) + return url; + + String withSlash = url.startsWith("/") ? url : "/" + url; + String context = url.startsWith(test.getTestSuite().getContextRoot()) ? "" : test.getTestSuite().getContextRoot(); + if(test.getTestSuite().getHttpPort()!=80) + return HTTP+test.getTestSuite().getTargetServer()+COLON+test.getTestSuite().getHttpPort()+context+withSlash; else - return HTTP+test.getTestSuite().getTargetServer()+test.getTestSuite().getContextRoot()+url; + return HTTP+test.getTestSuite().getTargetServer()+context+withSlash; } protected static class CallRecord { Index: TestHarness4LAMS2/src/org/lamsfoundation/testharness/admin/MockAdmin.java =================================================================== diff -u -rf53fb5e1f8ffeca4ab1ff6d8adb6b07094bbd468 -rc6ee15a5f86cbe315af4e6c2a933275fb8a1d41e --- TestHarness4LAMS2/src/org/lamsfoundation/testharness/admin/MockAdmin.java (.../MockAdmin.java) (revision f53fb5e1f8ffeca4ab1ff6d8adb6b07094bbd468) +++ TestHarness4LAMS2/src/org/lamsfoundation/testharness/admin/MockAdmin.java (.../MockAdmin.java) (revision c6ee15a5f86cbe315af4e6c2a933275fb8a1d41e) @@ -56,7 +56,6 @@ private static final String COURSE_FORM_FLAG = "OrganisationForm"; private static final String COURSE_NAME = "name"; - private static final String COURSE_ID_START_FLAG = "orgId="; private static final String COURSE_ID_PATTERN = "%orgId%"; private static final String USER_FORM_FLAG = "UserForm"; private static final String LOGIN = "login"; @@ -72,6 +71,7 @@ private static final String LEARNER_ROLE = "5"; private static final String USER_ID_START_FLAG = "userId="; private static final char USER_ID_END_FLAG = '&'; + private static final String LOGIN_TAKEN_ERROR ="Login is already taken."; public MockAdmin(AbstractTest test, String username, String password, String userId) { @@ -99,13 +99,8 @@ WebTable table = tables[0]; String idAsString = null; for (int i = table.getRowCount()-1; i >= 0; i--){ - if(table.getCellAsText(i,0).indexOf(courseName)!=-1){//found the organisation created just now - TableCell cell = table.getTableCell(i+1,1); - WebLink link = cell.getLinks()[0]; - String cellText = link.getAttribute("href"); - log.debug(cellText); - int startIndex = cellText.indexOf(COURSE_ID_START_FLAG); - idAsString = cellText.substring(startIndex+COURSE_ID_START_FLAG.length()); + if(table.getCellAsText(i,1).indexOf(courseName)!=-1){//found the organisation created just now + idAsString = table.getCellAsText(i,0); break; } } @@ -152,24 +147,27 @@ resp = (WebResponse)new Call(wc, test, username + " submit user creation form",fillForm(resp,0,params)).execute(); // add the roles + if ( resp.getText().indexOf(LOGIN_TAKEN_ERROR)!=-1) { + throw new TestHarnessException("Login "+name+" already taken."); + } + log.info(username+" adding roles to user "+name); params = new HashMap(); params.put(ROLES,users[i].roles); resp = (WebResponse)new Call(wc, test, username + " submit user rolesform",fillForm(resp,0,params)).execute(); WebTable[] tables = resp.getTables(); - if((tables==null)||(tables.length==0)){ + if((tables==null)||(tables.length<2)){ log.debug(resp.getText()); throw new TestHarnessException(username + " failed to get an user table after submitting user role form"); } - WebTable table = tables[0]; + WebTable table = tables[1]; String idAsString = null; for(int j = table.getRowCount()-1; j >= 0; j--){ log.debug("1:"+table.getCellAsText(j,0)); log.debug("4:"+table.getCellAsText(j,4)); - log.debug("5:"+table.getCellAsText(j,5)); if(table.getCellAsText(j,0).indexOf(name)!=-1){ - TableCell cell = table.getTableCell(j,5); + TableCell cell = table.getTableCell(j,4); WebLink link = cell.getLinks()[0]; String cellText = link.getAttribute("href"); int startIndex = cellText.indexOf(USER_ID_START_FLAG); Index: TestHarness4LAMS2/src/org/lamsfoundation/testharness/learner/MockLearner.java =================================================================== diff -u -r08950e1090443c3423a3d1c587416a2fccd8bbdf -rc6ee15a5f86cbe315af4e6c2a933275fb8a1d41e --- TestHarness4LAMS2/src/org/lamsfoundation/testharness/learner/MockLearner.java (.../MockLearner.java) (revision 08950e1090443c3423a3d1c587416a2fccd8bbdf) +++ TestHarness4LAMS2/src/org/lamsfoundation/testharness/learner/MockLearner.java (.../MockLearner.java) (revision c6ee15a5f86cbe315af4e6c2a933275fb8a1d41e) @@ -40,6 +40,7 @@ import com.allaire.wddx.WddxDeserializationException; import com.meterware.httpunit.Button; import com.meterware.httpunit.WebForm; +import com.meterware.httpunit.WebLink; import com.meterware.httpunit.WebResponse; /** @@ -79,6 +80,14 @@ private static final String ACTIVITY_FINISHED_FLAG = "passon.swf"; + private static final String FORUM_FINISH_SUBSTRING = "lafrum11/learning/finish.do"; + private static final String FORUM_VIEW_TOPIC_SUBSTRING = "lafrum11/learning/viewTopic.do"; + private static final String FORUM_REPLY_SUBSTRING = "lafrum11/learning/newReplyTopic.do"; + private static final String FORUM_VIEW_FORUM_SUBSTRING = "lafrum11/learning/viewForum.do"; + + private static final String LOAD_TOOL_ACTIVITY_SUBSTRING = "Load Tool Activity"; + + private boolean finished = false; /** @@ -188,7 +197,6 @@ */ private WebResponse takeActivity(String toolURL) { try { - delay(); WebResponse resp = (WebResponse) new Call(wc, test, "", toolURL).execute(); delay(); return handleActivity(resp); @@ -201,6 +209,10 @@ if (resp.getFrameNames().length == 2) { return handleParallelActivity(resp); } + + if ( resp.getText().indexOf("/lams/tool/laqa11/learning.do") != -1 ) + log.debug(resp.getText()); + WebResponse nextResp; WebForm[] forms = resp.getForms(); if ((forms != null) && (forms.length > 0)) { @@ -209,25 +221,156 @@ } else { nextResp = handlePageWithoutForms(resp); } - if (isAcitivityFinished(nextResp)) + if (isActivityFinished(nextResp)) return nextResp; else return handleActivity(nextResp); } - private boolean isAcitivityFinished(WebResponse resp) throws IOException { - return resp.getText().indexOf(ACTIVITY_FINISHED_FLAG) != -1; + private boolean isActivityFinished(WebResponse resp) throws IOException { + return resp != null && resp.getText().indexOf(ACTIVITY_FINISHED_FLAG) != -1; } - private WebResponse handlePageWithoutForms(WebResponse resp) { - // TODO implement me + private WebResponse handlePageWithoutForms(WebResponse resp) throws SAXException, IOException { + + String asText = resp.getText(); + + log.debug(asText); + // Is this a Forum activity? + if ( asText.indexOf(FORUM_FINISH_SUBSTRING) != -1 ) + return handleForum(resp); + + else if ( asText.indexOf(LOAD_TOOL_ACTIVITY_SUBSTRING) != -1 ) + return handleLoadToolActivity(asText); + + else + return findAnAbsoluteURLOnPage(asText); + + + } + + private WebResponse handleLoadToolActivity(String asText) throws SAXException, IOException { + + String toolURL = TestUtil.extractString(asText, NEXT_URL_START_FLAG, NEXT_URL_END_FLAG); + return (WebResponse) new Call(wc, test, "Redirect to tool page", toolURL).execute(); + } + + private WebResponse findAnAbsoluteURLOnPage(String respAsText) throws SAXException, IOException { + // Probably safest to get the last http address on the page, make sure we don't accidently + // go to http://www.w3c.org/ + int index = respAsText.lastIndexOf("\"http"); + while ( index != -1) { + int indexEnd = respAsText.indexOf("\"",index+1); + if ( indexEnd != -1 ) { + String httpString = respAsText.substring(index+1, indexEnd); + if ( httpString.indexOf("www.w3.org") == -1 && ! httpString.endsWith(".js") && ! httpString.endsWith(".css")) { + log.debug("Forwarding to discovered link "+httpString); + return (WebResponse) new Call(wc, test, "", httpString).execute(); + } + } + index = index > 0 ? respAsText.lastIndexOf("\"http",index-1) : -1; + } + throw new TestHarnessException("Unable to find a link to go to on page"+respAsText); + } + + private WebResponse handleForum(WebResponse resp) throws SAXException, IOException { + + WebResponse replyResponse = null; + + String replyURL = findURLInAHREF(resp,FORUM_VIEW_TOPIC_SUBSTRING); + if ( replyURL != null ) { + log.debug("Accessing the forum view topic screen using "+replyURL); + replyResponse = handleForumReply(replyURL); + } + + if ( replyResponse == null ) { + throw new TestHarnessException("No links found on the main forum page, or unable to do reply. "+resp.getText()); + } + + String finishURL = findURLInLocationHref(replyResponse, FORUM_FINISH_SUBSTRING); + if ( finishURL != null ) { + log.debug("Ending forum using url "+finishURL); + return (WebResponse) new Call(wc, test, "Finish Forum", finishURL).execute(); + } + + throw new TestHarnessException("Unable to finish the forum. No finish link found. "+replyResponse.getText()); + } + + /** + * @param link + * @return + * @throws SAXException + * @throws IOException + */ + private WebResponse handleForumReply(String url) throws SAXException, IOException { + WebResponse resp= (WebResponse) new Call(wc, test, "View Topic Forum", url).execute(); + String replyURL = findURLInAHREF(resp,FORUM_REPLY_SUBSTRING); + if ( replyURL != null ) { + resp = (WebResponse) new Call(wc, test, "Reply Topic Forum", replyURL).execute(); + WebForm[] forms = resp.getForms(); + if ((forms != null) && (forms.length > 0)) { + resp = handlePageWithForms(forms); + } else { + throw new TestHarnessException("No form found on the reply topic page - unable to do reply. "+resp.getText()); + } + } else { + throw new TestHarnessException("No reply URL found - unable to do reply. "+resp.getText()); + } + + // now we are back on the topic page, so go back to the main forum page. + String returnToForumURL = findURLInLocationHref(resp, FORUM_VIEW_FORUM_SUBSTRING); + if ( returnToForumURL != null ) { + log.debug("Returning to forum page "+returnToForumURL); + return (WebResponse) new Call(wc, test, "Return to Forum", returnToForumURL).execute(); + } + throw new TestHarnessException("No button back to forum page found - stuck while doing reply. "+resp.getText()); + } + + + private String findURLInAHREF(WebResponse resp, String linkSubstring) throws SAXException { + WebLink[] links = resp.getLinks(); + if ( links != null ) { + for ( WebLink link: links) { + log.debug("Checking link "+link.getURLString()); + if ( link.getURLString().indexOf(linkSubstring) != -1 ) + return link.getURLString(); + } + } return null; } + + private String findURLInLocationHref(WebResponse resp, String linkSubstring) throws SAXException, IOException { + String respAsText = resp.getText(); + String lowercaseRespAsText = respAsText.toLowerCase(); + int stringLength = lowercaseRespAsText.length(); + + int index = lowercaseRespAsText.indexOf("location.href"); + while ( index != -1) { + index++; + while ( index < stringLength && ! ( lowercaseRespAsText.charAt(index) == '\'' || lowercaseRespAsText.charAt(index) == '\"') ) { + index++; + } + if ( index < stringLength - 1 ) { + char quote = lowercaseRespAsText.charAt(index); + int indexEnd = lowercaseRespAsText.indexOf(quote,index+1); + String httpString = respAsText.substring(index+1, indexEnd); + log.debug("Discovered link "+httpString); + if ( httpString.indexOf(linkSubstring) != -1 ) { + log.debug("Matched to "+linkSubstring); + return httpString; + } + } + log.debug("Index was "+index); + index = index < stringLength && index > 0 ? lowercaseRespAsText.indexOf("location.href",index+1) : -1; + log.debug("New index is "+index); + } + return null; + } private WebResponse handleParallelActivity(WebResponse resp) { // TODO implement me - return null; + throw new TestHarnessException("Unable to handle parallel activities."); } private WebResponse handlePageWithForms(WebForm[] forms) throws IOException, SAXException { @@ -272,6 +415,7 @@ private String[] parseOutNextURLs(WebResponse resp) throws SAXException, IOException { String text = resp.getText(); + log.debug("Next URLS: "+text); String passonSwfURL = TestUtil.extractString(text, SWF_URL_START_FLAG, SWF_URL_END_FLAG); String toolURL = TestUtil.extractString(text, NEXT_URL_START_FLAG, NEXT_URL_END_FLAG); @@ -295,15 +439,25 @@ String text = composeArbitraryText(); form.setParameter(param, text); log.debug(username+ " input " + text + " for form field " + param); + } else if (form.isFileParameter(param)) { + File file = selectArbitraryFile(((LearnerTest) test).getFilesToUpload()); + form.setParameter(param, file); + log.debug(username + " uploaded file "+ file.getName()+" for form field "+param); } else if (form.isMultiValuedParameter(param)) { String[] values = chooseArbitraryValues(form.getOptionValues(param)); form.setParameter(param, values); log.debug(username+" set " + values.length + " value(s) for form field " + param); log.debug(values); - } else if (form.isFileParameter(param)) { - File file = selectArbitraryFile(((LearnerTest) test).getFilesToUpload()); - form.setParameter(param, file); - log.debug(username + " uploaded file "+ file.getName()+" for form field "+param); + } else { + log.debug(param+" may be a radio button. Current value is "+form.getParameterValue(param)); + if ( form.getParameterValue(param) == null ) { + String[] candidateValues = form.getOptionValues(param); + if ( candidateValues != null && candidateValues.length > 0 ) { + String value = candidateValues[TestUtil.generateRandomIndex(candidateValues.length)]; + log.debug("Setting param to "+value); + form.setParameter(param, value); + } + } } }else{ log.debug("disabled or hidden or readonly parameter "+param); @@ -319,6 +473,14 @@ return form; } + private String processButtons(Button[] buttons) { + StringBuffer buf = new StringBuffer(100); + for ( Button button : buttons ) { + buf.append(button.toString()); + buf.append("; "); + } + return buf.toString(); + } private Map> groupButtonsByName(Button[] btns, String buttonType ){ log.debug(btns.length); Index: TestHarness4LAMS2/src/org/lamsfoundation/testharness/monitor/MockMonitor.java =================================================================== diff -u -r08950e1090443c3423a3d1c587416a2fccd8bbdf -rc6ee15a5f86cbe315af4e6c2a933275fb8a1d41e --- TestHarness4LAMS2/src/org/lamsfoundation/testharness/monitor/MockMonitor.java (.../MockMonitor.java) (revision 08950e1090443c3423a3d1c587416a2fccd8bbdf) +++ TestHarness4LAMS2/src/org/lamsfoundation/testharness/monitor/MockMonitor.java (.../MockMonitor.java) (revision c6ee15a5f86cbe315af4e6c2a933275fb8a1d41e) @@ -84,6 +84,10 @@ public String initLesson(String initLessonURL, String ldId, String userId, String name) { try { delay(); + if ( userId == null ) { + throw new TestHarnessException("User id is missing. If you have set UserCreated (admin properties) to true, then you must set UserId in the monitor properties."); + } + log.debug("initLessonURL "+initLessonURL+" ldId "+ldId+" userId "+userId+" name "+name); String url = initLessonURL.replace(LDID_PATTERN, ldId).replace(USER_ID_PATTERN, userId).replace(LESSON_NAME_PATTERN, name); WebResponse resp = (WebResponse) new Call(wc, test, "Init Lesson", url).execute(); Hashtable hashtable = (Hashtable) TestUtil.deserialize(resp.getText());