Compare commits

...

7 Commits

Author SHA1 Message Date
Steve Gravrock
5cc739d879 Bump version to 5.0.0-alpha.1 2023-04-08 13:02:21 -07:00
Steve Gravrock
1e7f07259e API docs for parallel support things that jasmine-npm uses 2023-04-08 12:37:40 -07:00
Steve Gravrock
c36a5cfd96 Parallel: Cleaner interface for reporter dispatching
This gets jasmine-npm out of having to deal with QueueRunner, GlobalErrors,
and ReportDispatcher directly.
2023-04-08 11:41:15 -07:00
Steve Gravrock
299fd1f770 Removed unnecessary TODO 2023-03-25 11:21:04 -07:00
Steve Gravrock
656427d328 Parallel: Disallow calls to Env#config from spec and helper files
Such configuration changes only affect one worker, which is almost certainly
not the intent.
2023-03-25 11:16:54 -07:00
Steve Gravrock
621522fdd4 Updated Glob 2023-03-21 22:35:15 -07:00
Steve Gravrock
6e3589bf52 Updated most dev dependencies 2023-03-21 22:33:24 -07:00
13 changed files with 660 additions and 223 deletions

View File

@@ -89,7 +89,9 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
j$.CompleteOnFirstErrorSkipPolicy = jRequire.CompleteOnFirstErrorSkipPolicy(
j$
);
j$.reporterEvents = jRequire.reporterEvents(j$);
j$.ReportDispatcher = jRequire.ReportDispatcher(j$);
j$.ParallelReportDispatcher = jRequire.ParallelReportDispatcher(j$);
j$.RunableResources = jRequire.RunableResources(j$);
j$.Runner = jRequire.Runner(j$);
j$.Spec = jRequire.Spec(j$);
@@ -1309,6 +1311,12 @@ getJasmineRequireObj().Env = function(j$) {
* @function
*/
this.configure = function(configuration) {
if (parallelLoadingState) {
throw new Error(
'Jasmine cannot be configured via Env in parallel mode'
);
}
const booleanProps = [
'random',
'failSpecWithNoExpectations',
@@ -1549,72 +1557,7 @@ getJasmineRequireObj().Env = function(j$) {
* @see custom_reporter
*/
reporter = new j$.ReportDispatcher(
[
/**
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
* @function
* @name Reporter#jasmineStarted
* @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'jasmineStarted',
/**
* When the entire suite has finished execution `jasmineDone` is called
* @function
* @name Reporter#jasmineDone
* @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running.
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'jasmineDone',
/**
* `suiteStarted` is invoked when a `describe` starts to run
* @function
* @name Reporter#suiteStarted
* @param {SuiteResult} result Information about the individual {@link describe} being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'suiteStarted',
/**
* `suiteDone` is invoked when all of the child specs and suites for a given suite have been run
*
* While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`.
* @function
* @name Reporter#suiteDone
* @param {SuiteResult} result
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'suiteDone',
/**
* `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions)
* @function
* @name Reporter#specStarted
* @param {SpecResult} result Information about the individual {@link it} being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'specStarted',
/**
* `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run.
*
* While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed.
* @function
* @name Reporter#specDone
* @param {SpecResult} result
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'specDone'
],
j$.reporterEvents,
function(options) {
options.SkipPolicy = j$.NeverSkipPolicy;
return queueRunnerFactory(options);
@@ -1638,7 +1581,6 @@ getJasmineRequireObj().Env = function(j$) {
};
this.parallelReset = function() {
// TODO: ensure that autoCleanClosures was false
suiteBuilder.parallelReset();
runner.parallelReset();
};
@@ -7167,6 +7109,100 @@ getJasmineRequireObj().NeverSkipPolicy = function(j$) {
return NeverSkipPolicy;
};
getJasmineRequireObj().ParallelReportDispatcher = function(j$) {
/**
* @class ParallelReportDispatcher
* @implements Reporter
* @classdesc A report dispatcher packaged for convenient use from outside jasmine-core.
*
* This is intended to help packages like `jasmine` (the Jasmine runner for
* Node.js) do their own report dispatching in order to support parallel
* execution. If you aren't implementing a runner package that supports
* parallel execution, this class probably isn't what you're looking for.
*
* Warning: Do not use ParallelReportDispatcher in the same process that
* Jasmine specs run in. Doing so will break Jasmine's error handling.
* @param onError {function} Function called when an unhandled exception, unhandled promise rejection, or explicit reporter failure occurs
*/
function ParallelReportDispatcher(onError, deps = {}) {
const ReportDispatcher = deps.ReportDispatcher || j$.ReportDispatcher;
const QueueRunner = deps.QueueRunner || j$.QueueRunner;
const globalErrors = deps.globalErrors || new j$.GlobalErrors();
const dispatcher = ReportDispatcher(
j$.reporterEvents,
function(queueRunnerOptions) {
queueRunnerOptions = {
...queueRunnerOptions,
globalErrors,
timeout: { setTimeout, clearTimeout },
fail: function(error) {
// A callback-style async reporter called either done.fail()
// or done(anError).
if (!error) {
error = new Error('A reporter called done.fail()');
}
onError(error);
},
onException: function(error) {
// A reporter method threw an exception or returned a rejected
// promise, or there was an unhandled exception or unhandled promise
// rejection while an asynchronous reporter method was running.
onError(error);
}
};
new QueueRunner(queueRunnerOptions).execute();
},
function(error) {
// A reporter called done() more than once.
onError(error);
}
);
const self = {
/**
* Adds a reporter to the list of reporters that events will be dispatched to.
* @function
* @name ParallelReportDispatcher#addReporter
* @param {Reporter} reporterToAdd The reporter to be added.
* @see custom_reporter
*/
addReporter: dispatcher.addReporter.bind(dispatcher),
/**
* Clears all registered reporters.
* @function
* @name ParallelReportDispatcher#clearReporters
*/
clearReporters: dispatcher.clearReporters.bind(dispatcher),
/**
* Installs a global error handler. After this method is called, any
* unhandled exceptions or unhandled promise rejections will be passed to
* the onError callback that was passed to the constructor.
* @function
* @name ParallelReportDispatcher#installGlobalErrors
*/
installGlobalErrors: globalErrors.install.bind(globalErrors),
/**
* Uninstalls the global error handler.
* @function
* @name ParallelReportDispatcher#uninstallGlobalErrors
*/
uninstallGlobalErrors: function() {
// late-bind uninstall because it doesn't exist until install is called
globalErrors.uninstall(globalErrors);
}
};
for (const eventName of j$.reporterEvents) {
self[eventName] = dispatcher[eventName].bind(dispatcher);
}
return self;
}
return ParallelReportDispatcher;
};
getJasmineRequireObj().makePrettyPrinter = function(j$) {
class SinglePrettyPrintRun {
constructor(customObjectFormatters, pp) {
@@ -7528,15 +7564,6 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
};
getJasmineRequireObj().QueueRunner = function(j$) {
/*
QueueRunner isn't part of the public interface, but it is used by
jasmine-npm. -core and -npm version in lockstep at the major and minor
levels but version independently at the patch level. Any changes that
would break -npm should be done in a major or minor release, never a
patch release, and accompanied by a change to -npm that's released in
the same version.
*/
let nextid = 1;
function StopExecutionError() {}
@@ -7793,17 +7820,21 @@ getJasmineRequireObj().QueueRunner = function(j$) {
// on the stack at this point.
if (j$.isAsyncFunction_(fn)) {
this.onException(
'An asynchronous before/it/after ' +
'function was defined with the async keyword but also took a ' +
'done callback. Either remove the done callback (recommended) or ' +
'remove the async keyword.'
new Error(
'An asynchronous before/it/after ' +
'function was defined with the async keyword but also took a ' +
'done callback. Either remove the done callback (recommended) or ' +
'remove the async keyword.'
)
);
} else {
this.onException(
'An asynchronous before/it/after ' +
'function took a done callback but also returned a promise. ' +
'Either remove the done callback (recommended) or change the ' +
'function to not return a promise.'
new Error(
'An asynchronous before/it/after ' +
'function took a done callback but also returned a promise. ' +
'Either remove the done callback (recommended) or change the ' +
'function to not return a promise.'
)
);
}
}
@@ -7823,15 +7854,6 @@ getJasmineRequireObj().QueueRunner = function(j$) {
};
getJasmineRequireObj().ReportDispatcher = function(j$) {
/*
ReportDispatcher isn't part of the public interface, but it is used by
jasmine-npm. -core and -npm version in lockstep at the major and minor
levels but version independently at the patch level. Any changes that
would break -npm should be done in a major or minor release, never a
patch release, and accompanied by a change to -npm that's released in
the same version.
*/
function ReportDispatcher(methods, queueRunnerFactory, onLateError) {
const dispatchedMethods = methods || [];
@@ -7912,6 +7934,77 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
return ReportDispatcher;
};
getJasmineRequireObj().reporterEvents = function() {
const events = [
/**
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
* @function
* @name Reporter#jasmineStarted
* @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'jasmineStarted',
/**
* When the entire suite has finished execution `jasmineDone` is called
* @function
* @name Reporter#jasmineDone
* @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running.
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'jasmineDone',
/**
* `suiteStarted` is invoked when a `describe` starts to run
* @function
* @name Reporter#suiteStarted
* @param {SuiteResult} result Information about the individual {@link describe} being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'suiteStarted',
/**
* `suiteDone` is invoked when all of the child specs and suites for a given suite have been run
*
* While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`.
* @function
* @name Reporter#suiteDone
* @param {SuiteResult} result
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'suiteDone',
/**
* `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions)
* @function
* @name Reporter#specStarted
* @param {SpecResult} result Information about the individual {@link it} being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'specStarted',
/**
* `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run.
*
* While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed.
* @function
* @name Reporter#specDone
* @param {SpecResult} result
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'specDone'
];
Object.freeze(events);
return events;
};
getJasmineRequireObj().interface = function(jasmine, env) {
const jasmineInterface = {
/**
@@ -10293,31 +10386,41 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
};
getJasmineRequireObj().Timer = function() {
/*
Timer isn't part of the public interface, but it is used by
jasmine-npm. -core and -npm version in lockstep at the major and minor
levels but version independently at the patch level. Any changes that
would break -npm should be done in a major or minor release, never a
patch release, and accompanied by a change to -npm that's released in
the same version.
*/
const defaultNow = (function(Date) {
return function() {
return new Date().getTime();
};
})(Date);
/**
* @class Timer
* @classdesc Tracks elapsed time
* @example
* const timer = new jasmine.Timer();
* timer.start();
* const elapsed = timer.elapsed()
*/
function Timer(options) {
options = options || {};
const now = options.now || defaultNow;
let startTime;
/**
* Starts the timer.
* @function
* @name Timer#start
*/
this.start = function() {
startTime = now();
};
/**
* Determines the time since the timer was started.
* @function
* @name Timer#elapsed
* @returns {number} Elapsed time in milliseconds, or NaN if the timer has not been started
*/
this.elapsed = function() {
return now() - startTime;
};
@@ -10610,5 +10713,5 @@ getJasmineRequireObj().UserContext = function(j$) {
};
getJasmineRequireObj().version = function() {
return '5.0.0-alpha.0';
return '5.0.0-alpha.1';
};

View File

@@ -1,7 +1,7 @@
{
"name": "jasmine-core",
"license": "MIT",
"version": "5.0.0-alpha.0",
"version": "5.0.0-alpha.1",
"repository": {
"type": "git",
"url": "https://github.com/jasmine/jasmine.git"
@@ -34,10 +34,10 @@
"package.json"
],
"devDependencies": {
"eslint": "^7.32.0",
"eslint-plugin-compat": ">=4.0.0 <4.1.0",
"glob": "^7.2.0",
"grunt": ">=1.0.4 <1.6.0",
"eslint": "^8.36.0",
"eslint-plugin-compat": "^4.0.0",
"glob": "^9.3.1",
"grunt": "^1.0.4",
"grunt-cli": "^1.3.2",
"grunt-contrib-compress": "^2.0.0",
"grunt-contrib-concat": "^2.0.0",
@@ -45,10 +45,10 @@
"grunt-sass": "^3.0.2",
"jasmine": "github:jasmine/jasmine-npm#5.0",
"jasmine-browser-runner": "^1.0.0",
"jsdom": "^19.0.0",
"jsdom": "^21.1.1",
"load-grunt-tasks": "^5.1.0",
"prettier": "1.17.1",
"sass": "1.58.3",
"sass": "^1.58.3",
"shelljs": "^0.8.3",
"temp": "^0.9.0"
},

View File

@@ -0,0 +1,39 @@
# Jasmine Core 5.0.0-alpha.1 Release Notes
## Summary
This release provides improved support for parallel execution via the `jasmine`
package. Please see its release notes and the
[parallel documentation](https://jasmine.github.io/tutorials/running_specs_in_parallel)
for more information.
## New features and bug fixes
* Parallel: Cleaner interface for reporter dispatching
* When Env#config is called from spec and helper files in parallel mode, throw
an error rather than behaving unpredictably.
## Documentation improvements
* API reference docs for parallel support APIs that jasmine-npm uses
## Internal improvements
* Updated dev dependencies
## Supported environments
jasmine-core 5.0.0-alpha.1 has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 16.14+, 18 |
| Safari | 15-16 |
| Chrome | 112 |
| Firefox | 102, 111 |
| Edge | 111 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -758,16 +758,16 @@ describe('Env', function() {
describe('In parallel mode', function() {
it('rejects if random is set to false', async function() {
env.setParallelLoadingState('specs');
env.configure({ random: false });
env.setParallelLoadingState('specs');
await expectAsync(env.execute()).toBeRejectedWithError(
'Randomization cannot be disabled in parallel mode'
);
});
it('rejects if seed is set', async function() {
env.setParallelLoadingState('specs');
env.configure({ seed: 1234 });
env.setParallelLoadingState('specs');
await expectAsync(env.execute()).toBeRejectedWithError(
'Random seed cannot be set in parallel mode'
);
@@ -817,4 +817,18 @@ describe('Env', function() {
}).toThrowError('Reporters cannot be removed via Env in parallel mode');
});
});
describe('#configure', function() {
it('throws when called in parallel mode', function() {
env.setParallelLoadingState('helpers');
expect(function() {
env.configure({});
}).toThrowError('Jasmine cannot be configured via Env in parallel mode');
env.setParallelLoadingState('specs');
expect(function() {
env.configure({});
}).toThrowError('Jasmine cannot be configured via Env in parallel mode');
});
});
});

View File

@@ -0,0 +1,176 @@
describe('ParallelReportDispatcher', function() {
it('dispatches the standard reporter events', async function() {
const subject = new jasmineUnderTest.ParallelReportDispatcher(() => {}, {
globalErrors: mockGlobalErrors()
});
const events = [
'jasmineStarted',
'jasmineDone',
'suiteStarted',
'suiteDone',
'specStarted',
'specDone'
];
const reporter = jasmine.createSpyObj('reporter', events);
subject.addReporter(reporter);
for (const eventName of events) {
const payload = { payloadFor: eventName };
await subject[eventName](payload);
expect(reporter[eventName]).toHaveBeenCalledWith(payload);
}
});
it('installs and uninstalls the global error handler', function() {
const globalErrors = mockGlobalErrors();
const subject = new jasmineUnderTest.ParallelReportDispatcher(() => {}, {
globalErrors
});
subject.installGlobalErrors();
expect(globalErrors.install).toHaveBeenCalled();
subject.uninstallGlobalErrors();
expect(globalErrors.uninstall).toHaveBeenCalled();
});
it('handles global errors from async reporters', async function() {
const globalErrors = mockGlobalErrors();
const onError = jasmine.createSpy('onError');
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
globalErrors
});
const reporter = jasmine.createSpyObj('reporter', [
'jasmineStarted',
'jasmineDone'
]);
let resolveStarted;
reporter.jasmineStarted.and.callFake(function() {
return new Promise(function(res) {
resolveStarted = res;
});
});
subject.addReporter(reporter);
const promise = subject.jasmineStarted({});
expect(globalErrors.pushListener).toHaveBeenCalled();
expect(globalErrors.popListener).not.toHaveBeenCalled();
const error = new Error('nope');
globalErrors.pushListener.calls.argsFor(0)[0](error);
expect(onError).toHaveBeenCalledWith(error);
resolveStarted();
await promise;
expect(globalErrors.popListener).toHaveBeenCalled();
});
it('handles done(error) from callback-style async reporters', function() {
const globalErrors = mockGlobalErrors();
const onError = jasmine.createSpy('onError');
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
globalErrors
});
const reporter = jasmine.createSpyObj('reporter', [
'jasmineStarted',
'jasmineDone'
]);
let callback;
reporter.jasmineStarted = function(event, cb) {
callback = cb;
};
subject.addReporter(reporter);
subject.jasmineStarted({});
expect(callback).toBeInstanceOf(Function);
const error = new Error('nope');
callback(error);
expect(onError).toHaveBeenCalledWith(error);
});
it('handles done.fail() from callback-style async reporters', function() {
const globalErrors = mockGlobalErrors();
const onError = jasmine.createSpy('onError');
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
globalErrors
});
const reporter = jasmine.createSpyObj('reporter', [
'jasmineStarted',
'jasmineDone'
]);
let callback;
reporter.jasmineStarted = function(event, cb) {
callback = cb;
};
subject.addReporter(reporter);
subject.jasmineStarted({});
expect(callback).toBeInstanceOf(Function);
const error = new Error('nope');
callback.fail(error);
expect(onError).toHaveBeenCalledWith(error);
onError.calls.reset();
callback.fail();
expect(onError).toHaveBeenCalledWith(
new Error('A reporter called done.fail()')
);
});
it('handles errors due to mixed async style in reporters', async function() {
const globalErrors = mockGlobalErrors();
const onError = jasmine.createSpy('onError');
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
globalErrors
});
subject.addReporter({
async jasmineStarted(event, done) {
done();
}
});
await subject.jasmineStarted({});
expect(onError).toHaveBeenCalledWith(
new Error(
'An asynchronous before/it/after function took a done callback but also returned a promise. Either remove the done callback (recommended) or change the function to not return a promise.'
)
);
});
it('handles errors due to multiple done calls in reporters', async function() {
const globalErrors = mockGlobalErrors();
const onError = jasmine.createSpy('onError');
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
globalErrors
});
subject.addReporter({
jasmineStarted(event, done) {
done();
done();
}
});
await subject.jasmineStarted({});
expect(onError).toHaveBeenCalledWith(
new Error(
"An asynchronous reporter callback called its 'done' callback more than once."
)
);
});
function mockGlobalErrors() {
const globalErrors = jasmine.createSpyObj('globalErrors', [
'install',
'pushListener',
'popListener'
]);
globalErrors.install.and.callFake(function() {
globalErrors.uninstall = jasmine.createSpy('globalErrors.uninstall');
});
return globalErrors;
}
});

View File

@@ -595,11 +595,13 @@ describe('QueueRunner', function() {
queueRunner.execute();
expect(onException).toHaveBeenCalledWith(
'An asynchronous ' +
'before/it/after function took a done callback but also returned a ' +
'promise. ' +
'Either remove the done callback (recommended) or change the function ' +
'to not return a promise.'
new Error(
'An asynchronous ' +
'before/it/after function took a done callback but also returned a ' +
'promise. ' +
'Either remove the done callback (recommended) or change the function ' +
'to not return a promise.'
)
);
});
@@ -615,10 +617,12 @@ describe('QueueRunner', function() {
queueRunner.execute();
expect(onException).toHaveBeenCalledWith(
'An asynchronous ' +
'before/it/after function was defined with the async keyword but ' +
'also took a done callback. Either remove the done callback ' +
'(recommended) or remove the async keyword.'
new Error(
'An asynchronous ' +
'before/it/after function was defined with the async keyword but ' +
'also took a done callback. Either remove the done callback ' +
'(recommended) or remove the async keyword.'
)
);
});
});

View File

@@ -174,6 +174,12 @@ getJasmineRequireObj().Env = function(j$) {
* @function
*/
this.configure = function(configuration) {
if (parallelLoadingState) {
throw new Error(
'Jasmine cannot be configured via Env in parallel mode'
);
}
const booleanProps = [
'random',
'failSpecWithNoExpectations',
@@ -414,72 +420,7 @@ getJasmineRequireObj().Env = function(j$) {
* @see custom_reporter
*/
reporter = new j$.ReportDispatcher(
[
/**
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
* @function
* @name Reporter#jasmineStarted
* @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'jasmineStarted',
/**
* When the entire suite has finished execution `jasmineDone` is called
* @function
* @name Reporter#jasmineDone
* @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running.
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'jasmineDone',
/**
* `suiteStarted` is invoked when a `describe` starts to run
* @function
* @name Reporter#suiteStarted
* @param {SuiteResult} result Information about the individual {@link describe} being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'suiteStarted',
/**
* `suiteDone` is invoked when all of the child specs and suites for a given suite have been run
*
* While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`.
* @function
* @name Reporter#suiteDone
* @param {SuiteResult} result
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'suiteDone',
/**
* `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions)
* @function
* @name Reporter#specStarted
* @param {SpecResult} result Information about the individual {@link it} being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'specStarted',
/**
* `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run.
*
* While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed.
* @function
* @name Reporter#specDone
* @param {SpecResult} result
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'specDone'
],
j$.reporterEvents,
function(options) {
options.SkipPolicy = j$.NeverSkipPolicy;
return queueRunnerFactory(options);
@@ -503,7 +444,6 @@ getJasmineRequireObj().Env = function(j$) {
};
this.parallelReset = function() {
// TODO: ensure that autoCleanClosures was false
suiteBuilder.parallelReset();
runner.parallelReset();
};

View File

@@ -0,0 +1,93 @@
getJasmineRequireObj().ParallelReportDispatcher = function(j$) {
/**
* @class ParallelReportDispatcher
* @implements Reporter
* @classdesc A report dispatcher packaged for convenient use from outside jasmine-core.
*
* This is intended to help packages like `jasmine` (the Jasmine runner for
* Node.js) do their own report dispatching in order to support parallel
* execution. If you aren't implementing a runner package that supports
* parallel execution, this class probably isn't what you're looking for.
*
* Warning: Do not use ParallelReportDispatcher in the same process that
* Jasmine specs run in. Doing so will break Jasmine's error handling.
* @param onError {function} Function called when an unhandled exception, unhandled promise rejection, or explicit reporter failure occurs
*/
function ParallelReportDispatcher(onError, deps = {}) {
const ReportDispatcher = deps.ReportDispatcher || j$.ReportDispatcher;
const QueueRunner = deps.QueueRunner || j$.QueueRunner;
const globalErrors = deps.globalErrors || new j$.GlobalErrors();
const dispatcher = ReportDispatcher(
j$.reporterEvents,
function(queueRunnerOptions) {
queueRunnerOptions = {
...queueRunnerOptions,
globalErrors,
timeout: { setTimeout, clearTimeout },
fail: function(error) {
// A callback-style async reporter called either done.fail()
// or done(anError).
if (!error) {
error = new Error('A reporter called done.fail()');
}
onError(error);
},
onException: function(error) {
// A reporter method threw an exception or returned a rejected
// promise, or there was an unhandled exception or unhandled promise
// rejection while an asynchronous reporter method was running.
onError(error);
}
};
new QueueRunner(queueRunnerOptions).execute();
},
function(error) {
// A reporter called done() more than once.
onError(error);
}
);
const self = {
/**
* Adds a reporter to the list of reporters that events will be dispatched to.
* @function
* @name ParallelReportDispatcher#addReporter
* @param {Reporter} reporterToAdd The reporter to be added.
* @see custom_reporter
*/
addReporter: dispatcher.addReporter.bind(dispatcher),
/**
* Clears all registered reporters.
* @function
* @name ParallelReportDispatcher#clearReporters
*/
clearReporters: dispatcher.clearReporters.bind(dispatcher),
/**
* Installs a global error handler. After this method is called, any
* unhandled exceptions or unhandled promise rejections will be passed to
* the onError callback that was passed to the constructor.
* @function
* @name ParallelReportDispatcher#installGlobalErrors
*/
installGlobalErrors: globalErrors.install.bind(globalErrors),
/**
* Uninstalls the global error handler.
* @function
* @name ParallelReportDispatcher#uninstallGlobalErrors
*/
uninstallGlobalErrors: function() {
// late-bind uninstall because it doesn't exist until install is called
globalErrors.uninstall(globalErrors);
}
};
for (const eventName of j$.reporterEvents) {
self[eventName] = dispatcher[eventName].bind(dispatcher);
}
return self;
}
return ParallelReportDispatcher;
};

View File

@@ -1,13 +1,4 @@
getJasmineRequireObj().QueueRunner = function(j$) {
/*
QueueRunner isn't part of the public interface, but it is used by
jasmine-npm. -core and -npm version in lockstep at the major and minor
levels but version independently at the patch level. Any changes that
would break -npm should be done in a major or minor release, never a
patch release, and accompanied by a change to -npm that's released in
the same version.
*/
let nextid = 1;
function StopExecutionError() {}
@@ -264,17 +255,21 @@ getJasmineRequireObj().QueueRunner = function(j$) {
// on the stack at this point.
if (j$.isAsyncFunction_(fn)) {
this.onException(
'An asynchronous before/it/after ' +
'function was defined with the async keyword but also took a ' +
'done callback. Either remove the done callback (recommended) or ' +
'remove the async keyword.'
new Error(
'An asynchronous before/it/after ' +
'function was defined with the async keyword but also took a ' +
'done callback. Either remove the done callback (recommended) or ' +
'remove the async keyword.'
)
);
} else {
this.onException(
'An asynchronous before/it/after ' +
'function took a done callback but also returned a promise. ' +
'Either remove the done callback (recommended) or change the ' +
'function to not return a promise.'
new Error(
'An asynchronous before/it/after ' +
'function took a done callback but also returned a promise. ' +
'Either remove the done callback (recommended) or change the ' +
'function to not return a promise.'
)
);
}
}

View File

@@ -1,13 +1,4 @@
getJasmineRequireObj().ReportDispatcher = function(j$) {
/*
ReportDispatcher isn't part of the public interface, but it is used by
jasmine-npm. -core and -npm version in lockstep at the major and minor
levels but version independently at the patch level. Any changes that
would break -npm should be done in a major or minor release, never a
patch release, and accompanied by a change to -npm that's released in
the same version.
*/
function ReportDispatcher(methods, queueRunnerFactory, onLateError) {
const dispatchedMethods = methods || [];

View File

@@ -1,29 +1,39 @@
getJasmineRequireObj().Timer = function() {
/*
Timer isn't part of the public interface, but it is used by
jasmine-npm. -core and -npm version in lockstep at the major and minor
levels but version independently at the patch level. Any changes that
would break -npm should be done in a major or minor release, never a
patch release, and accompanied by a change to -npm that's released in
the same version.
*/
const defaultNow = (function(Date) {
return function() {
return new Date().getTime();
};
})(Date);
/**
* @class Timer
* @classdesc Tracks elapsed time
* @example
* const timer = new jasmine.Timer();
* timer.start();
* const elapsed = timer.elapsed()
*/
function Timer(options) {
options = options || {};
const now = options.now || defaultNow;
let startTime;
/**
* Starts the timer.
* @function
* @name Timer#start
*/
this.start = function() {
startTime = now();
};
/**
* Determines the time since the timer was started.
* @function
* @name Timer#elapsed
* @returns {number} Elapsed time in milliseconds, or NaN if the timer has not been started
*/
this.elapsed = function() {
return now() - startTime;
};

View File

@@ -0,0 +1,70 @@
getJasmineRequireObj().reporterEvents = function() {
const events = [
/**
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
* @function
* @name Reporter#jasmineStarted
* @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'jasmineStarted',
/**
* When the entire suite has finished execution `jasmineDone` is called
* @function
* @name Reporter#jasmineDone
* @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running.
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'jasmineDone',
/**
* `suiteStarted` is invoked when a `describe` starts to run
* @function
* @name Reporter#suiteStarted
* @param {SuiteResult} result Information about the individual {@link describe} being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'suiteStarted',
/**
* `suiteDone` is invoked when all of the child specs and suites for a given suite have been run
*
* While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`.
* @function
* @name Reporter#suiteDone
* @param {SuiteResult} result
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'suiteDone',
/**
* `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions)
* @function
* @name Reporter#specStarted
* @param {SpecResult} result Information about the individual {@link it} being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'specStarted',
/**
* `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run.
*
* While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed.
* @function
* @name Reporter#specDone
* @param {SpecResult} result
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'specDone'
];
Object.freeze(events);
return events;
};

View File

@@ -67,7 +67,9 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
j$.CompleteOnFirstErrorSkipPolicy = jRequire.CompleteOnFirstErrorSkipPolicy(
j$
);
j$.reporterEvents = jRequire.reporterEvents(j$);
j$.ReportDispatcher = jRequire.ReportDispatcher(j$);
j$.ParallelReportDispatcher = jRequire.ParallelReportDispatcher(j$);
j$.RunableResources = jRequire.RunableResources(j$);
j$.Runner = jRequire.Runner(j$);
j$.Spec = jRequire.Spec(j$);