001: // Copyright © 2002-2005 Canoo Engineering AG, Switzerland.
002: package com.canoo.webtest.steps.verify;
003:
004: import java.io.IOException;
005: import java.net.HttpURLConnection;
006: import java.net.MalformedURLException;
007: import java.net.URL;
008: import java.util.Collections;
009: import java.util.Map;
010: import java.util.Set;
011:
012: import com.canoo.webtest.self.ContextStub;
013: import com.canoo.webtest.self.ThrowAssert;
014: import com.canoo.webtest.steps.BaseStepTestCase;
015: import com.canoo.webtest.steps.Step;
016: import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
017: import com.gargoylesoftware.htmlunit.MockWebConnection;
018: import com.gargoylesoftware.htmlunit.Page;
019: import com.gargoylesoftware.htmlunit.ScriptException;
020: import com.gargoylesoftware.htmlunit.SubmitMethod;
021: import com.gargoylesoftware.htmlunit.WebClient;
022: import com.gargoylesoftware.htmlunit.WebResponse;
023: import com.gargoylesoftware.htmlunit.WebResponseData;
024: import com.gargoylesoftware.htmlunit.WebResponseImpl;
025: import com.gargoylesoftware.htmlunit.html.HtmlPage;
026:
027: /**
028: * @author Dierk König, Urs-Peter Häss
029: * @author Marc Guillemot, Paul King
030: */
031:
032: // todo: dk: test missing for already visited pages -> performance issue
033: // todo: dk: how about .doc .pdf links? -> stopHunting
034: // todo: StepFailedException for all Broken Links and processing does not stop
035: public class VerifyLinksTest extends BaseStepTestCase {
036: private static final String DUMMY_HREF = "any.htm";
037: private static final String DUMMY_LABEL = "Homepage";
038: private static final HtmlPage DUMMY_PAGE = getResponseForText(samplePageWithAnchor());
039:
040: private static final int THROW_NO_EXCEPTION = 0;
041: private static final int THROW_MALFORMEDURLEXCEPTION = 1;
042: private static final int THROW_IOEXCEPTION = 2;
043:
044: protected Step createStep() {
045: return new VerifyLinks();
046: }
047:
048: public static void testFindNoLinksOnEmptyPage() {
049: checkLinkCountText("", 0);
050: checkLinkCountText(wrapContent(""), 0);
051: }
052:
053: // public void pendingTestFindNoLinksOnNameAttribute() {
054: // checkLinkCountText(wrapContent("<A NAME='bla'>Content</A>"), 0);
055: // checkLinkCountText(wrapContent("<A NAME='bla'></A>"), 0);
056: // }
057:
058: public void testLinksValid() throws Exception {
059: checkLinkCountDummyPage("<a href='#me'>dummy</a>"
060: + "<a name='me' href='http://www.yahoo.com'>dummy</a>"
061: + "<a href='http://www.google.com'>dummy</a>"
062: + "<a href='/trafficlight.html'>dummy</a>", 4);
063: }
064:
065: public void testLinksBroken() throws Exception {
066: checkLinkCountDummyPage("<a href='notExist.jsp'>dummy</a>"
067: + "<a href='ftp://dummy.org'>dummy</a>"
068: + "<a href='crazy://badUrl.org'>dummy</a>", 1);
069: }
070:
071: // public void pendingTestClientSideImageMap() {
072: // checkLinkCountText(wrapContent("<MAP NAME=\"xxx\">" +
073: // "<AREA shape=\"rect\" Coords=\"572,5,605,50\" HREF=\"bla\">" +
074: // "<AREA shape=\"rect\" Coords=\"572,5,605,50\" HREF=\"bla\"></MAP>" +
075: // "<IMG USEMAP=\"#XXX\" SRC=\"x.gif\">"), 2);
076: // }
077:
078: public static void testFindOneLinkOnPage() {
079: checkLinkCountText(samplePageWithAnchor(), 1);
080: checkLinkCountText(wrapContent(anchor(DUMMY_HREF,
081: "<IMG src=\"bla\">")), 1);
082: checkLinkCountText(
083: wrapContent("<A CLASS=\"x\" HREF=\"home.htm\"><IMG source=\"bla\"></A>"),
084: 1);
085: checkLinkCountText(
086: wrapContent("<A HREF=\"home.htm\" TARGET=\"FRAME\">Homepage</A>"),
087: 1);
088: checkLinkCountText(wrapContent(anchor(DUMMY_HREF, DUMMY_LABEL)
089: + anchor(DUMMY_HREF, DUMMY_LABEL)), 1);
090: }
091:
092: public static void testIgnoreSpecialLinks() {
093: checkLinkCountText(wrapContent(anchor(
094: "ftp://ftp.whateveritis.com", DUMMY_LABEL)), 0);
095: checkLinkCountText(wrapContent(anchor(
096: "mailto:feedback-online@canoo.com", "Feedback")), 0);
097: checkLinkCountText(wrapContent(anchor("www.ftp.com", "bla")), 1);
098: }
099:
100: public static void testRelativeHrefExpansion()
101: throws MalformedURLException {
102: Set foundGoodLinks = getGoodLinks(samplePageWithAnchor());
103: assertTrue(foundGoodLinks.contains(new URL(
104: ContextStub.SOME_BASE_URL + "/" + DUMMY_HREF)));
105: }
106:
107: private static class VerifyLinksExposeVisitsStub extends
108: VerifyLinks {
109: public void checkVisits(WebClient webClient, HtmlPage response) {
110: super .checkVisits(webClient, response);
111: }
112: }
113:
114: public static void testBrokenLinkDoesNotInterrupt() {
115: WebClient client = new WebClient();
116: VerifyLinks step = new VerifyLinksExposeVisitsStub();
117: HtmlPage page = getResponseForText(wrapContent(anchor("a", "x")
118: + anchor("b", "y")));
119: step.checkVisits(client, page);
120: assertEquals(2, step.getFailedVisits().size());
121: }
122:
123: private static Set getGoodLinks(final String text) {
124: HtmlPage response = getResponseForText(text);
125: return VerifyLinks.getGoodLinks(response);
126: }
127:
128: private static HtmlPage getResponseForText(final String text) {
129: ContextStub context = new ContextStub(text);
130: return (HtmlPage) context.getCurrentResponse();
131: }
132:
133: public static void testSameHRefNotToBeFollowedTwice() {
134: WebClient client = new WebClient();
135: VerifyLinks step = new VerifyLinksExposeVisitsStub();
136: String href = "a";
137: HtmlPage page = getResponseForText(wrapContent(anchor(href,
138: "LabelX")
139: + anchor(href, "LabelY")));
140: step.checkVisits(client, page);
141: assertEquals(1, step.getFailedVisits().size());
142: }
143:
144: private static class VerifyLinksExposeDepthStub extends VerifyLinks {
145: private int fCount;
146:
147: protected void visit(HtmlPage page, URL ignoreUrl,
148: WebClient client) {
149: fCount++;
150: followRecursively(page, client);
151: }
152:
153: protected boolean stopHunting(HtmlPage htmlPage) {
154: return false;
155: }
156: }
157:
158: public static void testDepth() {
159: assertDepth(0);
160: assertDepth(1);
161: assertDepth(10);
162: }
163:
164: private static void assertDepth(int depth) {
165: VerifyLinksExposeDepthStub verifyLinks = new VerifyLinksExposeDepthStub();
166: verifyLinks.setDepth(Integer.toString(depth));
167: verifyLinks.verifyProperties();
168: verifyLinks.followRecursively(DUMMY_PAGE, new WebClient());
169: assertEquals(depth, verifyLinks.fCount);
170: }
171:
172: public void testDepthWithBrokenUrl() {
173: WebClient client = new WebClient();
174: VerifyLinks verifyLinks = (VerifyLinks) getStep();
175: HtmlPage page = getResponseForText(wrapContent(anchor(
176: "youwillnotfindthis", "x")));
177: verifyLinks.setDepth("2");
178: verifyLinks.verifyProperties();
179: verifyLinks.checkVisits(client, page);
180: boolean containsExactlyOneSemicolon = verifyLinks
181: .brokenLinksToString().indexOf(";") == verifyLinks
182: .brokenLinksToString().lastIndexOf(";")
183: && verifyLinks.brokenLinksToString().indexOf(";") != -1;
184: assertTrue(containsExactlyOneSemicolon);
185: }
186:
187: private static class VerifyLinksExposeForeignHostStub extends
188: VerifyLinks {
189: private boolean fIsforeignHost = true;
190:
191: protected boolean isForeignHost(URL ignore) {
192: return fIsforeignHost;
193: }
194: }
195:
196: public static void testOnSiteOnly() {
197: VerifyLinksExposeForeignHostStub verifyLinks = new VerifyLinksExposeForeignHostStub();
198: verifyLinks.setOnsiteonly(true);
199: verifyLinks.verifyProperties();
200: assertTrue("onsiteonly and foreign host: stop", verifyLinks
201: .stopHunting(DUMMY_PAGE));
202: verifyLinks.fIsforeignHost = false;
203: assertTrue("onsiteonly and same host: don't stop", !verifyLinks
204: .stopHunting(DUMMY_PAGE));
205: verifyLinks.fIsforeignHost = true;
206: verifyLinks.setOnsiteonly(false);
207: verifyLinks.verifyProperties();
208: assertTrue("not onsiteonly and foreign host: don't stop",
209: !verifyLinks.stopHunting(DUMMY_PAGE));
210: verifyLinks.fIsforeignHost = false;
211: assertTrue("not onsiteonly and same host: don't stop",
212: !verifyLinks.stopHunting(DUMMY_PAGE));
213: }
214:
215: private static void checkLinkCountText(final String text,
216: final int expectedLinkCount) {
217: ContextStub context = new ContextStub(text);
218: assertEquals(expectedLinkCount, VerifyLinks
219: .getLinkCount((HtmlPage) context.getCurrentResponse()));
220: }
221:
222: private void checkLinkCountDummyPage(final String content,
223: final int expectedLinkCount) {
224: final ContextStub context = new ContextStub();
225: final String htmlContent = wrapContent(content);
226: final HtmlPage page = getDummyPage(htmlContent);
227: context.saveResponseAsCurrent(page);
228: assertEquals(expectedLinkCount, VerifyLinks.getLinkCount(page));
229: }
230:
231: public void testNbLinksInReport() throws Exception {
232: final String content = "<a href='page2.html'>dummy</a>"
233: + "<a href='page3.html'>dummy</a>"
234: + "<a href='/trafficlight.html'>dummy</a>"
235: + "<a href='https://foo/secure.html'>secure</a>";
236:
237: final MockWebConnection webConnection = (MockWebConnection) getContext()
238: .getWebClient().getWebConnection();
239:
240: webConnection.setDefaultResponse("<html></html>");
241: final URL startUrl = new URL("http://www.foo.foo");
242: webConnection.setResponse(startUrl, wrapContent(content));
243: getContext().getWebClient().getPage(startUrl);
244:
245: final VerifyLinks step = (VerifyLinks) getStep();
246: step.setDepth("5");
247: step.execute();
248: final Map properties = step.getParameterDictionary();
249: assertEquals("4", properties.get("-> valid links"));
250:
251: }
252:
253: public void testNotBrokenStateRequest() {
254: assertNotBrokenStateWithHttpReturnCode(399, true);
255: assertNotBrokenStateWithHttpReturnCode(400, false);
256: assertNotBrokenStateWithHttpReturnCode(401, false);
257: assertNotBrokenStateWithHttpReturnCode(
258: HttpURLConnection.HTTP_BAD_GATEWAY, false);
259: assertNotBrokenStateWithHttpReturnCode(
260: HttpURLConnection.HTTP_BAD_REQUEST, false);
261: assertNotBrokenStateWithHttpReturnCode(
262: HttpURLConnection.HTTP_NOT_FOUND, false);
263: assertNotBrokenStateWithHttpReturnCode(
264: HttpURLConnection.HTTP_OK, true);
265: }
266:
267: public void testNotBrokenStateWithException() {
268: assertNotBrokenStateWithException(THROW_MALFORMEDURLEXCEPTION,
269: false);
270: assertNotBrokenStateWithException(THROW_IOEXCEPTION, false);
271: }
272:
273: private void assertNotBrokenStateWithHttpReturnCode(int returncode,
274: boolean expected) {
275: assertNotBrokenState(returncode, THROW_NO_EXCEPTION, expected);
276: }
277:
278: private void assertNotBrokenStateWithException(int exceptionCode,
279: boolean expected) {
280: assertNotBrokenState(0, exceptionCode, expected);
281: }
282:
283: private void assertNotBrokenState(int returncode,
284: int exceptionCode, boolean expected) {
285: final VerifyLinks step = (VerifyLinks) createAndConfigureStep();
286: final HtmlPage page = getResponseForText(samplePageWithAnchor());
287: final WebClient badClient = makeClient(returncode,
288: exceptionCode);
289: getContext().setWebClient(badClient);
290: step.checkVisits(badClient, page);
291: assertEquals(expected, step.getFailedVisits().size() == 0);
292: }
293:
294: private static WebClient makeClient(final int httpReturnCode,
295: final int exceptionCode) {
296: return new WebClient() {
297: public Page getPage(final URL url) throws IOException,
298: FailingHttpStatusCodeException {
299: switch (exceptionCode) {
300: case THROW_MALFORMEDURLEXCEPTION:
301: throw new MalformedURLException();
302: case THROW_IOEXCEPTION:
303: throw new IOException();
304: default:
305: }
306: if (httpReturnCode < 400)
307: return super .getPage(WebClient.URL_ABOUT_BLANK);
308: final WebResponseData responseData = new WebResponseData(
309: new byte[] {}, httpReturnCode, "blah",
310: Collections.EMPTY_LIST);
311: WebResponse webResponse = new WebResponseImpl(
312: responseData, url, SubmitMethod.GET, 1);
313: throw new FailingHttpStatusCodeException(webResponse);
314: }
315: };
316: }
317:
318: private static String samplePageWithAnchor() {
319: return wrapContent(anchor(DUMMY_HREF, DUMMY_LABEL));
320: }
321:
322: private static String anchor(String href, String label) {
323: return "<A HREF=\"" + href + "\">" + label + "</A>";
324: }
325:
326: private static String wrapContent(String content) {
327: return "<html><head><title>foo</title></head><body>" + content
328: + "</body></html>";
329: }
330:
331: public void testJSErrorsOnForeignPages() throws Exception {
332: final String htmlContent = wrapContent("<a href='http://webtest.canoo.com/jserror.html'>other</a>");
333: final HtmlPage page = getDummyPage(htmlContent);
334: getContext().saveResponseAsCurrent(page);
335:
336: final WebClient webClient = page.getWebClient();
337: final MockWebConnection mockConnection = new MockWebConnection(
338: webClient);
339: mockConnection.setResponse(new URL(
340: "http://webtest.canoo.com/jserror.html"),
341: "<html><body onload='alert('></body></html>");
342: webClient.setWebConnection(mockConnection);
343:
344: final VerifyLinks step = (VerifyLinks) getStep();
345:
346: // throws exception as default is false
347: ThrowAssert.assertThrows(ScriptException.class,
348: getExecuteStepTestBlock());
349:
350: // ignore exceptions
351: step.setIgnoreForeignJSErrors(true);
352: executeStep(step);
353: }
354:
355: }
|