diff --git a/spec/JUnitXmlReporterSpec.js b/spec/JUnitXmlReporterSpec.js
index b2a65d8..0953296 100644
--- a/spec/JUnitXmlReporterSpec.js
+++ b/spec/JUnitXmlReporterSpec.js
@@ -159,6 +159,34 @@ describe("JUnitXmlReporter", function(){
         });
     });
 
+    function assertTestsuitesTagAttributes(testSuitesTag, {disabled, errors, failures, tests} = {}) {
+        expect(testSuitesTag.getAttribute('disabled')).toBe(disabled);
+        expect(testSuitesTag.getAttribute('errors')).toBe(errors);
+        expect(testSuitesTag.getAttribute('failures')).toBe(failures);
+        expect(testSuitesTag.getAttribute('tests')).toBe(tests);
+    };
+
+
+    it("the testsuites tags should include a time attribute", function() {
+        var testSuitesTags = writeCalls[0].xmldoc.getElementsByTagName('testsuites');
+        expect(testSuitesTags.length).toBe(1);
+        var testSuitesTag =  testSuitesTags[0];
+        expect(testSuitesTag.getAttribute('time')).not.toBe('');
+    });
+
+    describe("no xml output generation", function() {
+        beforeEach(function() {
+            setupReporterWithOptions({consolidateAll:true});
+            triggerRunnerEvents();
+        });
+
+        it("testsuites tags should default disabled, errors, failures to 0 when undefined", function() {
+            assertTestsuitesTagAttributes(
+                writeCalls[0].xmldoc.getElementsByTagName('testsuites')[0],
+                {disabled: '0', errors: '0', failures: '0', tests: '1'});
+        });
+    });
+
     describe("generated xml output", function(){
         var subSuite, subSubSuite, siblingSuite;
         function itShouldIncludeXmlPreambleInAllFiles() {
@@ -184,9 +212,11 @@ describe("JUnitXmlReporter", function(){
             fakeSpec(subSuite, "should be one level down");
             fakeSpec(subSubSuite, "should be two levels down");
             var skipped = fakeSpec(subSubSuite, "should be skipped two levels down");
+            var disabled = fakeSpec(subSubSuite, "should be disabled two levels down");
             var failed = fakeSpec(subSubSuite, "should be failed two levels down");
             fakeSpec(siblingSuite, "should be a sibling of Parent");
             skipped.result.status = "pending";
+            disabled.result.status = "disabled";
             failed.result.status = "failed";
             failed.result.failedExpectations.push({
                 passed: false,
@@ -212,6 +242,11 @@ describe("JUnitXmlReporter", function(){
             it("should write a single file using filePrefix as the filename", function() {
                 expect(writeCalls[0].args[0]).toBe('results.xml');
             });
+            it("testsuites tags should include disabled, errors, failures, and tests (count) when defined", function() {
+                assertTestsuitesTagAttributes(
+                    writeCalls[0].xmldoc.getElementsByTagName('testsuites')[0],
+                    {disabled: '1', errors: '0', failures: '1', tests: '7'});
+            });
             itShouldHaveOneTestsuitesElementPerFile();
             itShouldIncludeXmlPreambleInAllFiles();
         });
@@ -231,6 +266,12 @@ describe("JUnitXmlReporter", function(){
                 expect(writeCalls[0].args[0]).toBe('results-ParentSuite.xml');
                 expect(writeCalls[1].args[0]).toBe('results-SiblingSuiteWithInvalidChars.xml');
             });
+            it("testsuites tags should include disabled, errors, failures, and tests (count) when defined", function() {
+                assertTestsuitesTagAttributes(writeCalls[0].xmldoc.getElementsByTagName('testsuites')[0],
+                    {disabled: '1', errors: '0', failures: '1', tests: '6'});
+                assertTestsuitesTagAttributes(writeCalls[1].xmldoc.getElementsByTagName('testsuites')[0],
+                    {disabled: '0', errors: '0', failures: '0', tests: '1'});
+            });
             itShouldHaveOneTestsuitesElementPerFile();
             itShouldIncludeXmlPreambleInAllFiles();
         });
@@ -300,7 +341,7 @@ describe("JUnitXmlReporter", function(){
             });
             it("should include total / failed / skipped counts for each suite (ignoring descendent results)", function() {
                 expect(suites[1].getAttribute('tests')).toBe('1');
-                expect(suites[2].getAttribute('tests')).toBe('3');
+                expect(suites[2].getAttribute('tests')).toBe('4');
                 expect(suites[2].getAttribute('skipped')).toBe('1');
                 expect(suites[2].getAttribute('failures')).toBe('1');
             });
@@ -346,7 +387,7 @@ describe("JUnitXmlReporter", function(){
             });
             it("should include specs in order", function() {
                 expect(specs[0].getAttribute('name')).toContain('should be a dummy');
-                expect(specs[4].getAttribute('name')).toBe('should be failed two levels down');
+                expect(specs[5].getAttribute('name')).toBe('should be failed two levels down');
             });
             it("should escape bad xml characters in spec description", function() {
                 expect(writeCalls[0].output).toContain("& < > " '");
@@ -355,15 +396,15 @@ describe("JUnitXmlReporter", function(){
                 expect(Number(specs[0].getAttribute('time'))).not.toEqual(NaN);
             });
             it("should include failed matcher name as the failure type", function() {
-                var failure = specs[4].getElementsByTagName('failure')[0];
+                var failure = specs[5].getElementsByTagName('failure')[0];
                 expect(failure.getAttribute('type')).toBe('toBe');
             });
             it("should include failure messages", function() {
-                var failure = specs[4].getElementsByTagName('failure')[0];
+                var failure = specs[5].getElementsByTagName('failure')[0];
                 expect(failure.getAttribute('message')).toBe('Expected true to be false.');
             });
             it("should include stack traces for failed specs (using CDATA to preserve special characters)", function() {
-                var failure = specs[4].getElementsByTagName('failure')[0];
+                var failure = specs[5].getElementsByTagName('failure')[0];
                 expect(failure.textContent).toContain('cool & can have "special" characters <3');
             });
             it("should include <skipped/> for skipped specs", function() {
diff --git a/src/junit_reporter.js b/src/junit_reporter.js
index 11c25ed..98e3da2 100644
--- a/src/junit_reporter.js
+++ b/src/junit_reporter.js
@@ -289,12 +289,18 @@
                 self.suiteDone(fakeFocusedSuite);
             }
             var output = '';
+            var testSuitesResults = { disabled: 0, errors: 0, failures: 0, tests: 0, time: 0 };
             for (var i = 0; i < suites.length; i++) {
                 output += self.getOrWriteNestedOutput(suites[i]);
+                // retrieve nested suite data to include in the testsuites tag
+                var suiteResults = self.getNestedSuiteData(suites[i]);
+                for (var key in suiteResults) {
+                    testSuitesResults[key] += suiteResults[key];
+                };
             }
             // if we have anything to write here, write out the consolidated file
             if (output) {
-                wrapOutputAndWriteFile(self.filePrefix, output);
+                wrapOutputAndWriteFile(self.filePrefix, output, testSuitesResults);
             }
             //log("Specs skipped but not reported (entire suite skipped or targeted to specific specs)", totalSpecsDefined - totalSpecsExecuted + totalSpecsDisabled);
 
@@ -306,6 +312,27 @@
             }
         };
 
+        self.formatSuiteData = function(suite) {
+            return {
+                disabled: suite._disabled || 0,
+                errors: 0,
+                failures: suite._failures || 0,
+                tests: suite._specs.length || 0,
+                time: (suite._endTime.getTime() - suite._startTime.getTime()) || 0
+            };
+        };
+
+        self.getNestedSuiteData = function (suite) {
+            var suiteResults = self.formatSuiteData(suite);
+            for (var i = 0; i < suite._suites.length; i++) {
+                var childSuiteResults = self.getNestedSuiteData(suite._suites[i]);
+                for (var key in suiteResults) {
+                    suiteResults[key] += childSuiteResults[key];
+                };
+            }
+            return suiteResults;
+        };
+
         self.getOrWriteNestedOutput = function(suite) {
             var output = suiteAsXml(suite);
             for (var i = 0; i < suite._suites.length; i++) {
@@ -315,7 +342,7 @@
                 return output;
             } else {
                 // if we aren't supposed to consolidate output, just write it now
-                wrapOutputAndWriteFile(generateFilename(suite), output);
+                wrapOutputAndWriteFile(generateFilename(suite), output, self.getNestedSuiteData(suite));
                 return '';
             }
         };
@@ -467,17 +494,20 @@
                 self.logEntries.splice(0, self.logEntries.length);
             }
         }
-
-        // To remove complexity and be more DRY about the silly preamble and <testsuites> element
-        var prefix = '<?xml version="1.0" encoding="UTF-8" ?>';
-        if (self.stylesheetPath) {
-            prefix += '\n<?xml-stylesheet type="text/xsl" href="' + self.stylesheetPath + '" ?>';
+        function getPrefix({disabled, errors, failures, tests, time} = {}) {
+            // To remove complexity and be more DRY about the silly preamble and <testsuites> element
+            var prefix = '<?xml version="1.0" encoding="UTF-8" ?>';
+            if (self.stylesheetPath) {
+                prefix += '\n<?xml-stylesheet type="text/xsl" href="' + self.stylesheetPath + '" ?>';
+            }
+            prefix += '\n<testsuites disabled="' + disabled + '" errors="' + errors + '" failures="' + failures + 
+                '" tests="' + tests + '" time="' + time + '">';
+            return prefix;
         }
-        prefix += '\n<testsuites>';
         var suffix = '\n</testsuites>';
-        function wrapOutputAndWriteFile(filename, text) {
+        function wrapOutputAndWriteFile(filename, text, testSuitesResults) {
             if (filename.substr(-4) !== '.xml') { filename += '.xml'; }
-            self.writeFile(filename, (prefix + text + suffix));
+            self.writeFile(filename, (getPrefix(testSuitesResults) + text + suffix));
         }
     };
 })(this);