feat(env): setSpecProperty/setSuiteProperty(key, value) to attach data to tests

Use setSpecProperty to attach key/value pairs to spec results that can be
picked up in specialized jasmine reporters.  Example use-cases
include:
  * Tagging specs with URLs or string-tokens referencing test-plan docs.
  * Recording performance information for blocks of JS.
Similarly setSuiteProperty attaches key/value pairs to suite results
This commit is contained in:
johnjbarton
2019-10-29 17:17:27 -07:00
parent f1eac6fb04
commit f90d9943fe
5 changed files with 124 additions and 3 deletions

View File

@@ -227,7 +227,8 @@ describe('Spec', function() {
passedExpectations: [],
deprecationWarnings: [],
pendingReason: '',
duration: jasmine.any(Number)
duration: jasmine.any(Number),
properties: null
},
'things'
);
@@ -299,6 +300,23 @@ describe('Spec', function() {
expect(duration).toBe(77000);
});
it('should report properties set during the test', function() {
var done = jasmine.createSpy('done callback'),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: jasmine.createSpy('spec body') },
catchExceptions: function() {
return false;
},
resultCallback: function() {},
queueRunnerFactory: function(attrs) {
attrs.onComplete();
}
});
spec.setSpecProperty('a', 4);
spec.execute(done);
expect(spec.result.properties).toEqual({ a: 4 });
});
it('#status returns passing by default', function() {
var spec = new jasmineUnderTest.Spec({
queueableFn: { fn: jasmine.createSpy('spec body') }

View File

@@ -1971,6 +1971,77 @@ describe("Env integration", function() {
env.execute();
});
it('reports test properties on specs', function(done) {
var env = new jasmineUnderTest.Env(),
reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']);
reporter.specDone.and.callFake(function(e) {
expect(e.properties).toEqual({a: 'Bee'});
done();
});
env.addReporter(reporter);
env.it('calls setSpecProperty', function() {
env.setSpecProperty('a', 'Bee')
});
env.execute();
});
it('throws an exception if you try to setSpecProperty outside of a spec', function (done) {
var env = new jasmineUnderTest.Env(),
exception;
env.describe("a suite", function () {
try {
env.setSpecProperty('a prop', 'val');
} catch(e) {
exception = e;
}
});
var assertions = function() {
expect(exception.message).toBe(`'setSpecProperty' was used when there was no current spec`);
done();
};
env.addReporter({jasmineDone: assertions});
env.execute();
});
it('reports test properties on suites', function(done) {
var env = new jasmineUnderTest.Env(),
reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']);
reporter.suiteDone.and.callFake(function(e) {
expect(e.properties).toEqual({b: 'Sweet'});
done();
});
env.addReporter(reporter);
env.describe('calls setSuiteProperty', function() {
env.beforeEach(() => {
env.setSuiteProperty('b', 'Sweet');
});
env.it('a passing spec', () => {
expect.nothing();
});
});
env.execute();
});
it('throws an exception if you try to setSuiteProperty outside of a suite', function (done) {
var env = new jasmineUnderTest.Env();
try {
env.setSuiteProperty('a', 'Bee');
} catch(e) {
expect(e.message).toBe(`'setSuiteProperty' was used when there was no current suite`);
done();
}
});
it("should associate errors thrown from async code with the correct runnable", function(done) {
var reporter = jasmine.createSpyObj('fakeReport', ['jasmineDone','suiteDone','specDone']);

View File

@@ -1124,6 +1124,24 @@ getJasmineRequireObj().Env = function(j$) {
return spec;
};
this.setSpecProperty = function(key, value) {
if (!currentRunnable() || currentRunnable() == currentSuite()) {
throw new Error(
"'setSpecProperty' was used when there was no current spec"
);
}
currentRunnable().setSpecProperty(key, value);
};
this.setSuiteProperty = function(key, value) {
if (!currentSuite()) {
throw new Error(
"'setSuiteProperty' was used when there was no current suite"
);
}
currentSuite().setSuiteProperty(key, value);
};
this.expect = function(actual) {
if (!currentRunnable()) {
throw new Error(

View File

@@ -48,6 +48,7 @@ getJasmineRequireObj().Spec = function(j$) {
* @property {String} pendingReason - If the spec is {@link pending}, this will be the reason.
* @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec.
* @property {number} duration - The time in ms used by the spec execution, including any before/afterEach.
* @property {Object} properties - user-supplied key-value pairs.
*/
this.result = {
id: this.id,
@@ -57,7 +58,8 @@ getJasmineRequireObj().Spec = function(j$) {
passedExpectations: [],
deprecationWarnings: [],
pendingReason: '',
duration: null
duration: null,
properties: null
};
}
@@ -74,6 +76,11 @@ getJasmineRequireObj().Spec = function(j$) {
}
};
Spec.prototype.setSpecProperty = function(key, value) {
this.result.properties = this.result.properties || {};
this.result.properties[key] = value;
};
Spec.prototype.expect = function(actual) {
return this.expectationFactory(actual, this);
};

View File

@@ -27,6 +27,7 @@ getJasmineRequireObj().Suite = function(j$) {
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite.
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
* @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach.
* @property {Object} properties - user-supplied key-value pairs.
*/
this.result = {
id: this.id,
@@ -34,10 +35,16 @@ getJasmineRequireObj().Suite = function(j$) {
fullName: this.getFullName(),
failedExpectations: [],
deprecationWarnings: [],
duration: null
duration: null,
properties: null
};
}
Suite.prototype.setSuiteProperty = function(key, value) {
this.result.properties = this.result.properties || {};
this.result.properties[key] = value;
};
Suite.prototype.expect = function(actual) {
return this.expectationFactory(actual, this);
};