adds test coverage reporting to Editor Draft
authorjaubourg <j@ubourg.net>
Tue, 30 Oct 2012 15:05:00 +0100
changeset 74 8b0a8c366782
parent 73 7459d0f53b0e
child 75 89bd8f596677
adds test coverage reporting to Editor Draft
Overview.src.html
coverage/coverage.css
coverage/coverage.js
--- a/Overview.src.html	Tue Oct 30 15:03:45 2012 +0100
+++ b/Overview.src.html	Tue Oct 30 15:05:00 2012 +0100
@@ -31,6 +31,14 @@
    table td, table th { border-left:solid; border-right:solid; border-bottom:solid thin; vertical-align:top; padding:0.2em }
   </style>
   <link rel="stylesheet" href="http://www.w3.org/StyleSheets/TR/W3C-[STATUS]">
+  <link class="dontpublish" href="coverage/coverage.css" rel="stylesheet">
+  <script class="dontpublish" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
+  <script class="dontpublish" src="coverage/coverage.js"></script>
+  <script class="dontpublish">
+  	showCoverage({
+  		suite: "xhr-ms xhr-Ms2ger xhr-opera",
+	});
+  </script>
  </head>
  <body>
   <div class="head">
@@ -165,7 +173,7 @@
 
 
 
-  <h2 id="introduction">Introduction</h2>
+  <h2 class="no-test" id="introduction">Introduction</h2>
 
   <p><em>This section is non-normative.</em></p>
 
@@ -235,7 +243,7 @@
   </div>
 
 
-<h3>Specification history</h3>
+<h3 class="no-test">Specification history</h3>
 
 <p>The <code>XMLHttpRequest</code> object was initially defined as part of
 the WHATWG's HTML effort. (Long after Microsoft shipped an implementation.)
@@ -257,7 +265,7 @@
 
 
 
-<h2 id="conformance">Conformance</h2>
+<h2 class="no-test" id="conformance">Conformance</h2>
 
 <p>All diagrams, examples, and notes in this specification are
 non-normative, as are all sections explicitly marked non-normative.
@@ -271,7 +279,7 @@
 <span data-anolis-ref>RFC2119</span>
 
 
-  <h3 id="dependencies">Dependencies</h3>
+  <h3 class="no-test" id="dependencies">Dependencies</h3>
 
   <p>This specification relies on several underlying specifications.</p>
 
@@ -336,7 +344,7 @@
   </dl>
 
 
-  <h3 id="extensibility">Extensibility</h3>
+  <h3 class="no-test" id="extensibility">Extensibility</h3>
 
   <p>User agents, Working Groups, and other interested parties are
   <em>strongly encouraged</em> to discuss new features on a relevant public
@@ -349,7 +357,7 @@
 
 
 
-  <h2 id="terminology">Terminology</h2>
+  <h2 class="no-test" id="terminology">Terminology</h2>
 
 
   <p>The term <dfn>user credentials</dfn> for the purposes of this
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/coverage/coverage.css	Tue Oct 30 15:05:00 2012 +0100
@@ -0,0 +1,63 @@
+div.test-coverage {
+	border-radius: 12px;
+	text-align: right;
+	padding: 18px;
+	margin: 0 0 24px 0;
+	background: #CCE;
+	font-size: 0.9em;
+	font-weight: bold;
+}
+div.test-coverage h6 {
+	display: inline;
+	margin: 0;
+	padding: 0;
+	float: left;
+	font-size: 1em;
+	text-transform: uppercase;
+	font-weight: bold;
+}
+div.test-coverage-none {
+	background: red;
+	color: white;
+}
+div.test-coverage table {
+	border-collapse:collapse;
+	font-size: 0.75em;
+	display: none;
+	text-align: left;
+	font-weight: normal;
+	margin: 12px;
+	border: 4px solid black;
+	padding: 8px;
+}
+div.test-coverage table tr {
+	border: none;
+}
+div.test-coverage table tr > * {
+	padding: 8px;
+	border: 3px solid black;
+}
+div.test-coverage table tr th {
+	text-align: center;
+}
+div.test-coverage table tr th.test-results-fail {
+	background: red;
+	color: white;
+}
+div.test-coverage table tr th.test-results-uncertain {
+	background: yellow;
+	font-style: italic;
+}
+div.test-coverage table tr th.test-results-pass {
+	background: green;
+}
+div.test-coverage a, div.test-coverage a:active, div.test-coverage a:visited {
+	color: black;
+	text-decoration: none;
+	background: transparent;
+}
+div.test-coverage a:hover {
+	color: black;
+	text-decoration: underline;
+	background: transparent;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/coverage/coverage.js	Tue Oct 30 15:05:00 2012 +0100
@@ -0,0 +1,237 @@
+var showCoverage = (function( $ ) {
+
+var handleOptions = (function() {
+		var defaultOptions = {
+				suite: "",
+				exclude: ".no-num, .no-test, .no-toc"
+			};
+		return function( options ) {
+			options = $.extend( {}, defaultOptions, options );
+			if ( !$.isArray( options.suite ) ) {
+				options.suite = ( ( options.suite || "" ) + "" ).split( /\s+/ );
+			}
+			return options;
+		};
+	})();
+
+function buildHierarchy( headers ) {
+	var hierarchy = {},
+		list = [];
+	headers.each(function() {
+		var level = list.length,
+			newLevel = this.tagName.replace( /^h/i, "" );
+		if ( newLevel <= level ) {
+			for( var i = newLevel; i <= level; i++ ) {
+				list.pop();
+			}
+			hierarchy[ this.id ] = list.slice();
+		} else {
+			for ( var i = level; i < newLevel - 1; i++ ) {
+				list.push( undefined );
+			}
+			list.push( this.id );
+		}
+	});
+	return hierarchy;
+}
+
+function testHeader( tests, engines, orphans ) {
+	var count = 0,
+		orphanCount = 0,
+		states = {},
+		table = $("<table>").addClass("test-switch"),
+		enginesHeaders = $("<tr>"),
+		names, tmp;
+	$.each( engines, function( engine ) {
+		enginesHeaders.append( $("<th>").text( engine ) );
+	});
+	table.append(
+		enginesHeaders
+		.prepend("<th></th><th>ID</th><th>TITLE</th>" )
+	);
+	function addLine( test, isOrphan ) {
+		var tr = $("<tr>")
+				.append( $("<th>").text( isOrphan ? "??" : "" ) )
+				.append( $("<td>").text(test.suite + "/" + test.id) )
+				.append( $("<td>").text(test.title) )
+				.appendTo( table ),
+			state;
+		$.each( engines, function( engine ) {
+			var testResult = test.results && test.results[ engine ] || "not-ran";
+			if ( testResult ) {
+				if ( state === undefined ) {
+					state = testResult;
+				} else if ( state !== testResult ) {
+					state = "inconsistent";
+				}
+			}
+			tr.append(
+				$("<th>")
+				.text( testResult )
+				.addClass( testResult ? "test-results-" + testResult : "" )
+			);
+		});
+		state = state
+		if ( !states[ state ] ) {
+			states[ state ] = 1;
+		} else {
+			states[ state ]++;
+		}
+	}
+	$.each( tests || [], function( _, test ) {
+		count++;
+		addLine( test );
+	});
+	$.each( orphans || [], function( _, test ) {
+		orphanCount++;
+		addLine( test, true );
+	});
+	if ( orphanCount > 0 ) {
+		states.orphan = orphanCount;
+	}
+	if ( count || orphanCount ) {
+		names = [];
+		$.each( states, function( state ) {
+			names.push( state );
+		});
+		names.sort();
+		tmp = [];
+		$.each( names, function( _, state ) {
+			tmp.push( state + ": " + states[ state ] );
+		});
+		tmp = tmp.join(", ");
+		return $("<div>")
+			.addClass("test-coverage")
+			.append("<h6>Test Coverage</h6>")
+			.append("<span class='test-switch' style='display:none'>&uarr;</span><span class='test-switch'>&darr;</span>&nbsp;")
+			.append(
+				$("<a href='#'>")
+					.addClass( count ? "show-test" : "" )
+					.text(
+						count + " test" + ( count > 1 ? "s" : "" ) +
+						" ( " + tmp + " )" 
+					)
+			)
+			.append( table );
+	}
+	return $("<div>")
+		.addClass("test-coverage")
+		.addClass("test-coverage-none")
+		.text("NO TEST")
+		.append("<h6>No Test Coverage</h6>");
+}
+
+function whenMap( array, mapCallback ) {
+	return $.when.apply( $, $.map(array,mapCallback) );
+}
+
+return function ( options ) {
+	options = handleOptions( options );
+	var testsForSection = {},
+		tests = {},
+		orphans = {},
+		engines = {},
+		headers, hierarchy;
+	function addToSection( idSection, TestOrTestsMap ) {
+		var map, id;
+		if ( idSection && TestOrTestsMap ) {
+			if ( TestOrTestsMap.id ) {
+				map = {};
+				map[ TestOrTestsMap.id ] = TestOrTestsMap;
+			} else {
+				map = TestOrTestsMap;
+			}
+			if ( !testsForSection[ idSection ] ) {
+				testsForSection[ idSection ] = {};
+			}
+			$.extend( testsForSection[ idSection ], map );
+			for ( id in map ) {
+				if ( id in orphans ) {
+					delete orphans[ id ];
+				}
+			}
+		}
+	}
+	function dataForTest( id, data ) {
+		if ( !tests[ id ] ) {
+			tests[ id ] = orphans[ id ] = data;
+		} else {
+			$.extend( tests[ id ], data );
+		}
+	}
+	$(function() {
+		headers = $("h1, h2, h3, h4, h5, h6");
+		if ( options.exclude ) {
+			headers = headers.not( options.exclude );
+		}
+		hierarchy = buildHierarchy( headers );
+	});
+	$.when(
+		whenMap( options.suite, function( testCase ) {
+			return $.ajax( "http://w3c-test.org/framework/api/test-cases/" + testCase, {
+				dataType: "json"
+			}).done(function( data ) {
+				$.each( data, function( _, test ) {
+					dataForTest( test.id, {
+						id: test.id,
+						title: test.title,
+						suite: testCase
+					});
+					$.each( test && test.specURIs || [], function( _, specURIs ) {
+						addToSection( specURIs.uri.split("#")[ 1 ], tests[ test.id ] );
+					});
+				});
+			}).fail(function( error ) {
+				console.log( "Problem loading test case '" + testCase + "'" );
+			}).then( null, function() {
+				return $.Deferred().resolve();
+			})
+		}),
+		whenMap( options.suite, function( testCase ) {
+			return $.ajax( "http://w3c-test.org/framework/api/result/" + testCase, {
+				dataType: "json"
+			}).done(function( data ) {
+				$.each( data, function( _, test ) {
+					var results = {};
+					$.each( test.results_by_engine, function( engine, map ) {
+						if ( !engines[ engine ] ) {
+							engines[ engine ] = true;
+						}
+						for( var key in map ) {
+							results[ engine ] = map[ key ].result;
+							return;
+						}
+					});
+					dataForTest( test.id, {
+						id: test.id,
+						title: test.title,
+						results: results,
+						suite: testCase
+					});
+				});
+			}).fail(function( error ) {
+				console.log( "Problem loading result '" + testCase + "'" );
+			}).then( null, function() {
+				return $.Deferred().resolve();
+			})
+		})
+	).done(function() {
+		$(function() {
+			$.each( testsForSection, function( id, tests ) {
+				$.each( hierarchy[ id ] || [], function( _, idParent ) {
+					addToSection( idParent, tests );
+				});
+			});
+			headers.each(function() {
+				$( this ).after( testHeader(testsForSection[this.id],engines,orphans) );
+				orphans = undefined;
+			});
+			$("body").on( "click", "div.test-coverage a", function( e ) {
+				e.preventDefault();
+				$( this ).parent("div.test-coverage").find(".test-switch").toggle();
+			});
+	  	});
+	});
+}
+
+})( jQuery );