1: [TestFixture]
2: public class JavaScriptTester
3: {
4: private IE _ie;
5:
6: [TestFixtureSetUp]
7: public void TestFixtureSetUp()
8: {
9: _ie = TestBrowser.GetInternetExplorer();
10: }
11:
12: [IterativeTest("RunQUnitTests")]
13: public void QUnit(object current)
14: {
15: ((QUnitTest)current).ShouldPass();
16: }
17:
18: public IEnumerable RunQUnitTests()
19: {
20: return new[]
21: {
22: "ContextMenuTester.htm",
23: "FilterControlTester.htm",
24: "RepeaterControlTester.htm",
25: "FinderTester.htm",
26: "ConsoleScriptTester.htm",
27: "tageditortester.htm",
28: "CrudFormTester.htm"
29: }.SelectMany(page => GetQUnitTestResults(page));
30: }
31:
32:
33: public IEnumerable<QUnitTest> GetQUnitTestResults(string testPage)
34: {
35: TestFixtureSetUp();
36: _ie.GoTo(string.Format("http://localhost/Content/scripts/tests/{0}", testPage));
37: _ie.WaitForComplete(5);
38:
39: return grabTestResultsFromWebPage(testPage);
40: }
41:
42: public IEnumerable<QUnitTest> grabTestResultsFromWebPage(string testPage)
43: {
44: // BEWARE: This logic is tightly coupled to the structure of the HTML generated by the QUnit testrunner
45: // Also, this could probably be greatly simplified with a couple well-crafted xpath expressions
46: var testOL = _ie.Elements.Filter(Find.ById("tests"))[0] as ElementsContainer;
47: if (testOL == null) yield break;
48: var documentRoot = XDocument.Load(new StringReader(makeXHtml(testOL.OuterHtml))).Root;
49: if (documentRoot == null) yield break;
50:
51: foreach (var listItem in documentRoot.Elements())
52: {
53: var testName = listItem.Elements().First( x => x.Name.Is("strong")).Value;
54: var resultClass = listItem.Attributes().First(x => x.Name.Is("class")).Value;
55: var failedAssert = String.Empty;
56: if (resultClass == "fail")
57: {
58: var specificAssertFailureListItem = listItem.Elements()
59: .First(x => x.Name.Is("ol")).Elements()
60: .First(x => x.Name.Is("li") && x.Attributes().First(a=> a.Name.Is("class")).Value == "fail");
61: if (specificAssertFailureListItem != null)
62: {
63: failedAssert = specificAssertFailureListItem.Value;
64: }
65: }
66:
67: yield return new QUnitTest
68: {
69: FileName = testPage,
70: TestName = removeAssertCounts(testName),
71: Result = resultClass, Message = failedAssert
72: };
73: }
74:
75: }
76:
77: private static string makeXHtml(string html)
78: {
79: return html.Replace("class=pass", "class=\"pass\"")
80: .Replace("class=fail", "class=\"fail\"")
81: .Replace("id=tests", "id=\"tests\"");
82: }
83:
84:
85: private static string removeAssertCounts(string fullTagText)
86: {
87: if (fullTagText == null) return String.Empty;
88: int parenPosition = fullTagText.IndexOf('(');
89: if (parenPosition > 0)
90: {
91: return fullTagText.Substring(0, parenPosition - 1);
92: }
93: return fullTagText;
94: }
95: }
96:
97: public class QUnitTest
98: {
99: public string FileName { get; set; }
100: public string TestName { get; set; }
101: public string Result { get; set; }
102: public string Message { get; set; }
103:
104: public override string ToString()
105: {
106: return string.Format("[{0}] {1}", FileName, TestName);
107: }
108: }
109:
110: public static class QUnitTestHelpers
111: {
112: public static void ShouldPass(this QUnitTest theTest)
113: {
114: Assert.That(theTest.Result.Split(' '), Has.Member("pass"), theTest.Message);
115: }
116:
117: public static bool Is(this XName xname, string name)
118: {
119: return xname.ToString().Equals(name, StringComparison.OrdinalIgnoreCase);
120: }
121: }