Skip to content

Commit b4abee1

Browse files
committed
make .only sticky in watch mode
A second argument can be passed to Api#run(). If true the tests will be run in exclusive mode, regardless of whether exclusive tests are detected. The Api now remits the 'stats' event from the forks. The watcher keeps track of exclusive tests. If all test files that contained exclusive tests need to be rerun it runs them without forcing exclusive mode. This means the exclusivity is determined by the tests themselves. If a test file, containing exclusive tests, is not one of the files being rerun, it forces exclusive mode. This ensures only exclusive tests are run in the changed files, making .only sticky. If all test files that contained exclusive tests are removed, sticky mode is disabled. The same happens if there are no more exclusive tests after a run. Fixes #593.
1 parent e810ed4 commit b4abee1

File tree

4 files changed

+213
-31
lines changed

4 files changed

+213
-31
lines changed

api.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ Api.prototype._handleTeardown = function (data) {
106106
};
107107

108108
Api.prototype._handleStats = function (stats) {
109+
this.emit('stats', stats);
110+
109111
if (this.hasExclusive && !stats.hasExclusive) {
110112
return;
111113
}
@@ -165,10 +167,15 @@ Api.prototype._prefixTitle = function (file) {
165167
return prefix;
166168
};
167169

168-
Api.prototype.run = function (files) {
170+
Api.prototype.run = function (files, options) {
169171
var self = this;
170172

171173
this._reset();
174+
175+
if (options && options.runOnlyExclusive) {
176+
this.hasExclusive = true;
177+
}
178+
172179
return handlePaths(files, this.excludePatterns)
173180
.map(function (file) {
174181
return path.resolve(file);

lib/watcher.js

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,29 @@ function Watcher(logger, api, files, sources) {
3939
this.isTest = makeTestMatcher(files, api.excludePatterns);
4040
this.run = function (specificFiles) {
4141
logger.reset();
42-
this.busy = api.run(specificFiles || files).then(function () {
42+
43+
var runOnlyExclusive = false;
44+
if (specificFiles) {
45+
var exclusiveFiles = specificFiles.filter(function (file) {
46+
return this.filesWithExclusiveTests.indexOf(file) !== -1;
47+
}, this);
48+
49+
runOnlyExclusive = exclusiveFiles.length !== this.filesWithExclusiveTests.length;
50+
}
51+
52+
this.busy = api.run(specificFiles || files, {
53+
runOnlyExclusive: runOnlyExclusive
54+
}).then(function () {
4355
logger.finish();
4456
}, rethrowAsync);
4557
};
4658

4759
this.testDependencies = [];
4860
this.trackTestDependencies(api, sources);
4961

62+
this.filesWithExclusiveTests = [];
63+
this.trackExclusivity(api);
64+
5065
this.dirtyStates = {};
5166
this.watchFiles(files, sources);
5267
this.rerunAll();
@@ -85,12 +100,6 @@ Watcher.prototype.trackTestDependencies = function (api, sources) {
85100
});
86101
};
87102

88-
Watcher.prototype.removeUnlinkedTestDependencies = function (unlinkedTests) {
89-
unlinkedTests.forEach(function (testFile) {
90-
this.updateTestDependencies(testFile, []);
91-
}, this);
92-
};
93-
94103
Watcher.prototype.updateTestDependencies = function (file, sources) {
95104
if (sources.length === 0) {
96105
this.testDependencies = this.testDependencies.filter(function (dep) {
@@ -113,6 +122,30 @@ Watcher.prototype.updateTestDependencies = function (file, sources) {
113122
}
114123
};
115124

125+
Watcher.prototype.trackExclusivity = function (api) {
126+
var self = this;
127+
api.on('stats', function (stats) {
128+
self.updateExclusivity(stats.file, stats.hasExclusive);
129+
});
130+
};
131+
132+
Watcher.prototype.updateExclusivity = function (file, hasExclusiveTests) {
133+
var index = this.filesWithExclusiveTests.indexOf(file);
134+
135+
if (hasExclusiveTests && index === -1) {
136+
this.filesWithExclusiveTests.push(file);
137+
} else if (!hasExclusiveTests && index !== -1) {
138+
this.filesWithExclusiveTests.splice(index, 1);
139+
}
140+
};
141+
142+
Watcher.prototype.cleanUnlinkedTests = function (unlinkedTests) {
143+
unlinkedTests.forEach(function (testFile) {
144+
this.updateTestDependencies(testFile, []);
145+
this.updateExclusivity(testFile, false);
146+
}, this);
147+
};
148+
116149
Watcher.prototype.observeStdin = function (stdin) {
117150
var self = this;
118151

@@ -153,7 +186,7 @@ Watcher.prototype.runAfterChanges = function () {
153186
});
154187
var unlinkedTests = diff(dirtyTests, addedOrChangedTests);
155188

156-
this.removeUnlinkedTestDependencies(unlinkedTests);
189+
this.cleanUnlinkedTests(unlinkedTests);
157190
// No need to rerun tests if the only change is that tests were deleted.
158191
if (unlinkedTests.length === dirtyPaths.length) {
159192
return;

test/api.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,21 @@ test('test file with exclusive tests causes non-exclusive tests in other files t
621621
});
622622
});
623623

624+
test('test files can be forced to run in exclusive mode', function (t) {
625+
t.plan(4);
626+
627+
var api = new Api();
628+
return api.run(
629+
[path.join(__dirname, 'fixture/es2015.js')],
630+
{runOnlyExclusive: true}
631+
).then(function () {
632+
t.ok(api.hasExclusive);
633+
t.is(api.testCount, 0);
634+
t.is(api.passCount, 0);
635+
t.is(api.failCount, 0);
636+
});
637+
});
638+
624639
test('resets state before running', function (t) {
625640
t.plan(2);
626641

@@ -666,6 +681,26 @@ test('emits dependencies for test files', function (t) {
666681
result.catch(function () {});
667682
});
668683

684+
test('emits stats for test files', function (t) {
685+
t.plan(2);
686+
687+
var api = new Api();
688+
api.on('stats', function (stats) {
689+
if (stats.file === path.normalize('test/fixture/exclusive.js')) {
690+
t.is(stats.hasExclusive, true);
691+
} else if (stats.file === path.normalize('test/fixture/generators.js')) {
692+
t.is(stats.hasExclusive, false);
693+
} else {
694+
t.ok(false);
695+
}
696+
});
697+
698+
return api.run([
699+
'test/fixture/exclusive.js',
700+
'test/fixture/generators.js'
701+
]);
702+
});
703+
669704
test('verify test count', function (t) {
670705
t.plan(8);
671706

0 commit comments

Comments
 (0)