Merge branch 'main' into 5.99

This commit is contained in:
Steve Gravrock
2025-11-03 17:14:24 -08:00
24 changed files with 621 additions and 53 deletions

View File

@@ -1,5 +1,5 @@
# Run tests against supported Node versions, and (except for pull requests)
# against supported browsers.
# against supported browsers that are available on Saucelabs.
version: 2.1
@@ -93,7 +93,7 @@ jobs:
export SAUCE_TUNNEL_NAME=$CIRCLE_WORKFLOW_JOB_ID
scripts/start-sauce-connect
set +o errexit
scripts/run-all-browsers
scripts/run-sauce-browsers
exitcode=$?
set -o errexit
scripts/stop-sauce-connect

21
.github/workflows/safari.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Test in latest available Safari
on:
push:
pull_request:
jobs:
build:
runs-on: macos-latest
steps:
- name: Report Safari version
run: osascript -e 'get version of application "Safari"'
- uses: actions/checkout@v4
- name: Use Node.js 22.x
uses: actions/setup-node@v4
with:
node-version: 22.x
- run: npm install
- run: npm run build
- run: JASMINE_BROWSER=safari npm run ci

View File

@@ -30,7 +30,7 @@ Microsoft Edge) as well as Node.
| Environment | Supported versions |
|-------------------|----------------------------------|
| Node | 18.20.5+*, 20, 22, 24 |
| Safari | 16*, 17* |
| Safari | 16*, 17*, 26* |
| Chrome | Evergreen |
| Firefox | Evergreen, 102*, 115*, 128*, 140 |
| Edge | Evergreen |

View File

@@ -28,9 +28,18 @@ should also rev to that version.
When ready to release - specs are all green and the stories are done:
1. Update the release notes in `release_notes` - use the Anchorman gem to generate the markdown file and edit accordingly. Include a list of supported environments.
1. Update the version in `package.json`
1. Run `npm run build`.
1. Update the release notes in `release_notes` - use the Anchorman gem to
generate the Markdown file and edit accordingly. Include a list of supported
environments. Get that information from these places:
* For Node, see .circleci/config.yml or the README.
* For Firefox ESR and Safari <=17, see scripts/run-sauce-browsers or the README.
* For evergreen browsers, trigger a Circle CI run and check the
[Saucelabs dashboard](https://app.saucelabs.com/dashboard/tests?ownerId=90a771d55857492da3bd5251a2d92457&ownerType=user&ownerName=jasmine-js&start=last7days)
once it's finished.
* For Safari >17, trigger the [Safari action](https://github.com/jasmine/jasmine/actions/workflows/safari.yml)
and get the version from the output.
2. Update the version in `package.json`
3. Run `npm run build`.
### Commit and push core changes

View File

@@ -59,6 +59,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
j$.util = jRequire.util(j$);
j$.errors = jRequire.errors();
j$.formatErrorMsg = jRequire.formatErrorMsg();
j$.AllOf = jRequire.AllOf(j$);
j$.Any = jRequire.Any(j$);
j$.Anything = jRequire.Anything(j$);
j$.CallTracker = jRequire.CallTracker(j$);
@@ -417,6 +418,19 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
);
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value being compared matches every provided equality tester.
* @name asymmetricEqualityTesters.allOf
* @emittedName jasmine.allOf
* @since 5.13.0
* @function
* @param {...*} arguments - The asymmetric equality checkers to compare.
*/
j$.allOf = function() {
return new j$.AllOf(...arguments);
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value being compared is an instance of the specified class/constructor.
@@ -873,12 +887,11 @@ getJasmineRequireObj().Spec = function(j$) {
* @property {String} description - The description passed to the {@link it} that created this spec.
* @property {String} fullName - The full description including all ancestors of this spec.
* @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
* @property {String} filename - Deprecated. The name of the file the spec was defined in.
* @property {String} filename - The name of the file the spec was defined in.
* Note: The value may be incorrect if zone.js is installed or
* `it`/`fit`/`xit` have been replaced with versions that don't maintain the
* same call stack height as the originals. This property may be removed in
* a future version unless there is enough user interest in keeping it.
* See {@link https://github.com/jasmine/jasmine/issues/2065}.
* same call stack height as the originals. You can fix that by setting
* {@link Configuration#extraItStackFrames}.
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed during execution of this spec.
* @property {ExpectationResult[]} passedExpectations - The list of expectations that passed during execution of this spec.
* @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
@@ -1052,7 +1065,20 @@ getJasmineRequireObj().Spec = function(j$) {
* @returns {Array.<string>}
* @since 5.7.0
*/
getPath: this.getPath.bind(this)
getPath: this.getPath.bind(this),
/**
* The name of the file the spec was defined in.
* Note: The value may be incorrect if zone.js is installed or
* `it`/`fit`/`xit` have been replaced with versions that don't maintain the
* same call stack height as the originals. You can fix that by setting
* {@link Configuration#extraItStackFrames}.
* @name Spec#filename
* @readonly
* @type {string}
* @since 5.13.0
*/
filename: this.filename
};
}
@@ -1127,6 +1153,8 @@ getJasmineRequireObj().Order = function() {
};
getJasmineRequireObj().Env = function(j$) {
const DEFAULT_IT_DESCRIBE_STACK_DEPTH = 3;
/**
* @class Env
* @since 2.0.0
@@ -1722,14 +1750,14 @@ getJasmineRequireObj().Env = function(j$) {
this.describe = function(description, definitionFn) {
ensureIsNotNested('describe');
const filename = callerCallerFilename();
const filename = indirectCallerFilename(describeStackDepth());
return suiteBuilder.describe(description, definitionFn, filename)
.metadata;
};
this.xdescribe = function(description, definitionFn) {
ensureIsNotNested('xdescribe');
const filename = callerCallerFilename();
const filename = indirectCallerFilename(describeStackDepth());
return suiteBuilder.xdescribe(description, definitionFn, filename)
.metadata;
};
@@ -1737,30 +1765,38 @@ getJasmineRequireObj().Env = function(j$) {
this.fdescribe = function(description, definitionFn) {
ensureIsNotNested('fdescribe');
ensureNonParallel('fdescribe');
const filename = callerCallerFilename();
const filename = indirectCallerFilename(describeStackDepth());
return suiteBuilder.fdescribe(description, definitionFn, filename)
.metadata;
};
this.it = function(description, fn, timeout) {
ensureIsNotNested('it');
const filename = callerCallerFilename();
const filename = indirectCallerFilename(itStackDepth());
return suiteBuilder.it(description, fn, timeout, filename).metadata;
};
this.xit = function(description, fn, timeout) {
ensureIsNotNested('xit');
const filename = callerCallerFilename();
const filename = indirectCallerFilename(itStackDepth());
return suiteBuilder.xit(description, fn, timeout, filename).metadata;
};
this.fit = function(description, fn, timeout) {
ensureIsNotNested('fit');
ensureNonParallel('fit');
const filename = callerCallerFilename();
const filename = indirectCallerFilename(itStackDepth());
return suiteBuilder.fit(description, fn, timeout, filename).metadata;
};
function itStackDepth() {
return DEFAULT_IT_DESCRIBE_STACK_DEPTH + config.extraItStackFrames;
}
function describeStackDepth() {
return DEFAULT_IT_DESCRIBE_STACK_DEPTH + config.extraDescribeStackFrames;
}
/**
* Get a user-defined property as part of the properties field of {@link SpecResult}
* @name Env#getSpecProperty
@@ -1946,11 +1982,12 @@ getJasmineRequireObj().Env = function(j$) {
};
}
function callerCallerFilename() {
function indirectCallerFilename(depth) {
const frames = new j$.StackTrace(new Error()).frames;
// frames[3] should always exist except in Jasmine's own tests, which bypass
// the global it/describe layer, but don't crash if it doesn't.
return frames[3] && frames[3].file;
// The specified frame should always exist except in Jasmine's own tests,
// which bypass the global it/describe layer, but could be absent in case
// of misconfiguration. Don't crash if it's absent.
return frames[depth] && frames[depth].file;
}
return Env;
@@ -2086,6 +2123,34 @@ getJasmineRequireObj().JsApiReporter = function(j$) {
return JsApiReporter;
};
getJasmineRequireObj().AllOf = function(j$) {
function AllOf() {
const expectedValues = Array.from(arguments);
if (expectedValues.length === 0) {
throw new TypeError(
'jasmine.allOf() expects at least one argument to be passed.'
);
}
this.expectedValues = expectedValues;
}
AllOf.prototype.asymmetricMatch = function(other, matchersUtil) {
for (const expectedValue of this.expectedValues) {
if (!matchersUtil.equals(other, expectedValue)) {
return false;
}
}
return true;
};
AllOf.prototype.jasmineToString = function(pp) {
return '<jasmine.allOf(' + pp(this.expectedValues) + ')>';
};
return AllOf;
};
getJasmineRequireObj().Any = function(j$) {
function Any(expectedObject) {
if (typeof expectedObject === 'undefined') {
@@ -3391,7 +3456,30 @@ getJasmineRequireObj().Configuration = function(j$) {
* @type Boolean
* @default false
*/
detectLateRejectionHandling: false
detectLateRejectionHandling: false,
/**
* The number of extra stack frames inserted by a wrapper around {@link it}
* or by some other local modification. Jasmine uses this to determine the
* filename for {@link SpecStartedEvent} and {@link SpecDoneEvent}.
* @name Configuration#extraItStackFrames
* @since 5.13.0
* @type number
* @default 0
*/
extraItStackFrames: 0,
/**
* The number of extra stack frames inserted by a wrapper around
* {@link describe} or by some other local modification. Jasmine uses this
* to determine the filename for {@link SpecStartedEvent} and
* {@link SpecDoneEvent}.
* @name Configuration#extraDescribeStackFrames
* @since 5.13.0
* @type number
* @default 0
*/
extraDescribeStackFrames: 0
};
Object.freeze(defaultConfig);
@@ -3447,6 +3535,16 @@ getJasmineRequireObj().Configuration = function(j$) {
if (changes.hasOwnProperty('verboseDeprecations')) {
this.#values.verboseDeprecations = changes.verboseDeprecations;
}
// 0 is a valid value for both of these, so a truthiness check wouldn't work
if (typeof changes.extraItStackFrames !== 'undefined') {
this.#values.extraItStackFrames = changes.extraItStackFrames;
}
if (typeof changes.extraDescribeStackFrames !== 'undefined') {
this.#values.extraDescribeStackFrames =
changes.extraDescribeStackFrames;
}
}
}
@@ -10666,12 +10764,11 @@ getJasmineRequireObj().Suite = function(j$) {
* @property {String} description - The description text passed to the {@link describe} that made this suite.
* @property {String} fullName - The full description including all ancestors of this suite.
* @property {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe().
* @property {String} filename - Deprecated. The name of the file the suite was defined in.
* @property {String} filename - The name of the file the suite was defined in.
* Note: The value may be incorrect if zone.js is installed or
* `describe`/`fdescribe`/`xdescribe` have been replaced with versions that
* don't maintain the same call stack height as the originals. This property
* may be removed in a future version unless there is enough user interest
* in keeping it. See {@link https://github.com/jasmine/jasmine/issues/2065}.
* don't maintain the same call stack height as the originals. You can fix
* that by setting {@link Configuration#extraDescribeStackFrames}.
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
* @property {ExpectationResult[]} 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.
@@ -10878,6 +10975,19 @@ getJasmineRequireObj().Suite = function(j$) {
* @since 2.0.0
*/
this.description = suite.description;
/**
* The name of the file the suite was defined in.
* Note: The value may be incorrect if zone.js is installed or
* `describe`/`fdescribe`/`xdescribe` have been replaced with versions
* that don't maintain the same call stack height as the originals. You
* can fix that by setting {@link Configuration#extraItStackFrames}.
* @name Suite#filename
* @readonly
* @type {string}
* @since 5.13.0
*/
this.filename = suite.filename;
}
/**
@@ -11559,7 +11669,10 @@ getJasmineRequireObj().TreeRunner = function(j$) {
_executeSpec(spec, specOverallDone) {
const onStart = next => {
this.#currentRunableTracker.setCurrentSpec(spec);
this.#runableResources.initForRunable(spec.id, spec.parentSuiteId);
this.#runableResources.initForRunable(
spec.id,
spec.parentSuiteId || this.#executionTree.topSuite.id
);
this.#reportDispatcher.specStarted(spec.result).then(next);
};
const resultCallback = (result, next) => {
@@ -11833,5 +11946,5 @@ getJasmineRequireObj().UserContext = function(j$) {
};
getJasmineRequireObj().version = function() {
return '5.12.0';
return '5.12.1';
};

View File

@@ -1,7 +1,7 @@
{
"name": "jasmine-core",
"license": "MIT",
"version": "5.12.0",
"version": "5.12.1",
"repository": {
"type": "git",
"url": "https://github.com/jasmine/jasmine.git"

29
release_notes/5.12.1.md Normal file
View File

@@ -0,0 +1,29 @@
Jasmine Core 5.12.1 Release Notes
## Bug fixes
* Fix custom matchers in top-level specs
* Merges [#2088](https://github.com/jasmine/jasmine/pull/2088) from @bonkevin
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------|--------------------------------|
| Node | 18.20.5**, 20, 22, 24 |
| Safari | 16**, 17** |
| Chrome | 141* |
| Firefox | 102**, 115**, 128**, 140, 144* |
| Edge | 141* |
\* Evergreen browser. Each version of Jasmine is tested against the latest
version available at release time.<br>
\** Supported on a best-effort basis. Support for these versions may be dropped
if it becomes impractical, and bugs affecting only these versions may not be
treated as release blockers.
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -0,0 +1,116 @@
# Jasmine Core 6.0.0-alpha.1 Release Notes
This is a pre-release, intended to offer a preview of breaking changes and to
solicit feedback.
## A Note About Pre-Release Compatibility
There may be additional breaking changes in future 6.0 pre-releases or in the
final 6.0 release. That's allowed by the semver specification, but users are
sometimes unpleasantly surprised by it.
NPM's implementation of carat version ranges assumes that subsequent
pre-releases and final releases are fully compatible with earlier pre-releases.
If your package.json contains `"jasmine-core": "^6.0.0-alpha.1`,
NPM might install any later 6.x version even though there is no guarantee of
compatibility. If that isn't ok, you should specify an exact pre-release version:
`"jasmine-core": "6.0.0-alpha.1`.
## Breaking changes
### Changes that affect reporters
* Irrelevant properties such as `status` and `failedExpectations` are omitted
from [the event passed to suiteStarted](https://jasmine.github.io/api/6.0.0-alpha.1/global.html#SuiteStartedEvent).
This change should be compatible with most existing reporters but could break
reporters that manage their internal state in unusual ways. Please
[open an issue](https://github.com/jasmine/jasmine/issues/new?template=bug_report.yml)
if you find a published reporter package that works with jasmine-core 5.x but
not with this release.
### Changes that affect browser boot files
* The `createElement` and `createTextNode` options of `HtmlReporter` are ignored.
`HtmlReporter` now unconditionally uses `document.createElement` and
`document.createTextNode`.
### Changes that affect spec writing
* HTML reporters cache configuration throughout each run. Configuration changes
made while specs are running will not affect reporter behavior.
* Global error spies always receive a single argument. Previously, the browser
error event was passed as the second argument.
## New features
* A new `HtmlReporterV2` with several improvements over the old `HtmlReporter`:
* Clicking a spec/suite link does exact filtering rather than a substring
match.
* The old dots are replaced with a progress bar. This improves usability with
large suites and fixes an accessibility problem.
* Details of failed specs are displayed as soon as each spec finishes.
* Initialization and wire-up in boot files are much simpler.
If you're using jasmine-browser-runner or copying boot1.js from the standalone
distribution, you'll automatically get the new reporter. If you maintain your
own boot files, you'll get the old reporter unless you update your boot1.js
to match the one that's in this package.
The new reporter produces `spec` query string parameters that are different
from those created by the old reporter. If you use non-Jasmine software that
interprets the `spec` parameter, such as karma-jasmine, you may not be able to
adopt `HtmlReporterV2` unlesss it's updated.
* Use `globalThis` to determine the global object during initialization
This makes jasmine-core more tolerant of buggy bundlers or loaders that
cause `this` to be undefined in the global context.
## Deprecations
* Warn if jasmine-core is loaded as an ES module in a browser.
This is an untested and unsupported configuration that has been known to cause
problems in the past.
* Deprecated `HtmlReporter` and `HtmlSpecFilter` in favor of `HtmlReporterV2`.
## Documentation improvements
* Improved API reference documentation for APIs that are used from browser boot
files.
## Internal improvements
* Removed remaining code that supported suite re-entry.
* Encapsulated suite and spec result and status management.
* Adopted strict mode throughout the codebase.
* Decomposed `HtmlReporter` into components and converted to ES6 classes.
* Made global error handling more uniform between browsers and Node.
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------------------|
| Node | 20, 22, 24 |
| Safari | 16**, 17** |
| Chrome | 141* |
| Firefox | 102**, 115**, 128**, 140, 143* |
| Edge | 141* |
\* Evergreen browser. Each version of Jasmine is tested against the latest
version available at release time.<br>
\** Supported on a best-effort basis. Support for these versions may be dropped
if it becomes impractical, and bugs affecting only these versions may not be
treated as release blockers.
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,5 +1,10 @@
#!/bin/sh
# Run tests in supported browsers that are available on Saucelabs.
# Note: The latest Safari version is tested via GitHub Actions because Saucelabs
# only makes it available to paid enterprise accounts. See
# .github/workflows/safari.yml.
run_browser() {
browser=$1
version=$2

View File

@@ -13,7 +13,9 @@ describe('Configuration', function() {
...standardBooleanKeys,
'seed',
'specFilter',
'verboseDeprecations'
'verboseDeprecations',
'extraItStackFrames',
'extraDescribeStackFrames'
];
Object.freeze(standardBooleanKeys);
Object.freeze(allKeys);
@@ -32,6 +34,8 @@ describe('Configuration', function() {
expect(subject.forbidDuplicateNames).toEqual(false);
expect(subject.verboseDeprecations).toEqual(false);
expect(subject.detectLateRejectionHandling).toEqual(false);
expect(subject.extraItStackFrames).toEqual(0);
expect(subject.extraDescribeStackFrames).toEqual(0);
});
describe('copy()', function() {
@@ -130,5 +134,25 @@ describe('Configuration', function() {
subject.update({ seed: null });
expect(subject.seed).toBeNull();
});
it('sets extraItStackFrames when not undefined', function() {
const subject = new jasmineUnderTest.Configuration();
subject.update({ extraItStackFrames: undefined });
expect(subject.extraItStackFrames).toEqual(0);
subject.update({ extraItStackFrames: 100000 });
expect(subject.extraItStackFrames).toEqual(100000);
});
it('sets extraDescribeStackFrames when not undefined', function() {
const subject = new jasmineUnderTest.Configuration();
subject.update({ extraDescribeStackFrames: undefined });
expect(subject.extraDescribeStackFrames).toEqual(0);
subject.update({ extraDescribeStackFrames: 100000 });
expect(subject.extraDescribeStackFrames).toEqual(100000);
});
});
});

View File

@@ -1,4 +1,3 @@
// TODO: Fix these unit tests!
describe('Env', function() {
let env;
beforeEach(function() {
@@ -95,7 +94,7 @@ describe('Env', function() {
});
});
it('accepts its own current configureation', function() {
it('accepts its own current configuration', function() {
env.configure(env.configuration());
});
@@ -198,6 +197,29 @@ describe('Env', function() {
expect(innerSuite.parentSuite).toBe(suite);
expect(spec.getFullName()).toEqual('outer suite inner suite a spec');
});
it('sets the caller filename correctly when extraDescribeStackFrames is not set', function() {
// IIFE is used to match the stack depth when global describe() is called
const suite = (function() {
return env[methodName]('a suite', function() {
env.it('a spec');
});
})();
expect(suite.filename).toMatch(/EnvSpec\.js$/);
});
it('sets the caller filename correctly when extraDescribeStackFrames is set', function() {
env.configure({ extraDescribeStackFrames: 2 });
// IIFE is used to match the stack depth when global describe() is called
const suite = (function() {
return specHelpers.callerFilenameShim(function() {
return env[methodName]('a suite', function() {
env.it('a spec');
});
});
})();
expect(suite.filename).toMatch(/EnvSpec\.js$/);
});
}
describe('#describe', function() {
@@ -300,6 +322,25 @@ describe('Env', function() {
.not.toEqual('');
expect(spec.pend).toBeFalsy();
});
it('sets the caller filename correctly when extraItStackFrames is not set', function() {
// IIFE is used to match the stack depth when global it() is called
const spec = (function() {
return env[methodName]('a spec', function() {});
})();
expect(spec.filename).toMatch(/EnvSpec\.js$/);
});
it('sets the caller filename correctly when extraItStackFrames is set', function() {
env.configure({ extraItStackFrames: 2 });
// IIFE is used to match the stack depth when global it() is called
const spec = (function() {
return specHelpers.callerFilenameShim(function() {
return env[methodName]('a spec', function() {});
});
})();
expect(spec.filename).toMatch(/EnvSpec\.js$/);
});
}
describe('#it', function() {

View File

@@ -0,0 +1,63 @@
describe('AllOf', function() {
it('matches a single value', function() {
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const allOf = new jasmineUnderTest.AllOf('foo');
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeTrue();
});
it('matches a single matcher', function() {
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const allOf = new jasmineUnderTest.AllOf(
new jasmineUnderTest.StringContaining('oo')
);
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeTrue();
});
it('matches multiple matchers', function() {
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const allOf = new jasmineUnderTest.AllOf(
new jasmineUnderTest.StringContaining('o'),
new jasmineUnderTest.StringContaining('f')
);
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeTrue();
});
it('does not match when value does not match', function() {
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const allOf = new jasmineUnderTest.AllOf('bar');
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeFalse();
});
it('does not match when any matchers fail', function() {
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const allOf = new jasmineUnderTest.AllOf(
new jasmineUnderTest.StringContaining('o'),
new jasmineUnderTest.StringContaining('x')
);
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeFalse();
});
it('jasmineToStrings itself', function() {
const matcher = new jasmineUnderTest.AllOf('o');
const pp = jasmine.createSpy('pp').and.returnValue('sample');
expect(matcher.jasmineToString(pp)).toEqual('<jasmine.allOf(sample)>');
expect(pp).toHaveBeenCalledWith(['o']);
});
describe('when called without an argument', function() {
it('tells the user to pass a constructor argument', function() {
expect(function() {
new jasmineUnderTest.AllOf();
}).toThrowError(
TypeError,
'jasmine.allOf() expects at least one argument to be passed.'
);
});
});
});

View File

@@ -2309,6 +2309,34 @@ describe('Env integration', function() {
await env.execute();
});
it('Custom matchers set in top-level beforeAll should be available to all specs and suites', async function() {
const matchers = {
toFoo: function() {}
};
env.beforeAll(function() {
env.addMatchers(matchers);
});
env.describe('suite - top-level', function() {
env.it('has access to the custom matcher', function() {
expect(env.expect().toFoo).toBeDefined();
});
env.describe('suite - nested', function() {
env.it('has access to the custom matcher', function() {
expect(env.expect().toFoo).toBeDefined();
});
});
});
env.it('spec - top-level - has access to the custom matcher', function() {
expect(env.expect().toFoo).toBeDefined();
});
await env.execute();
});
it('throws an exception if you try to create a spy outside of a runnable', async function() {
const obj = { fn: function() {} };
let exception;

View File

@@ -0,0 +1,5 @@
(function() {
specHelpers.callerFilenameShim = function(fn) {
return fn();
};
})();

View File

@@ -23,6 +23,7 @@ module.exports = {
'helpers/BrowserFlags.js',
'helpers/domHelpers.js',
'helpers/integrationMatchers.js',
'helpers/callerFilenameShim.js',
'helpers/defineJasmineUnderTest.js',
'helpers/resetEnv.js'
],

View File

@@ -8,6 +8,7 @@
"helpers/init.js",
"helpers/domHelpers.js",
"helpers/integrationMatchers.js",
"helpers/callerFilenameShim.js",
"helpers/overrideConsoleLogForCircleCi.js",
"helpers/nodeDefineJasmineUnderTest.js",
"helpers/resetEnv.js"

View File

@@ -123,7 +123,30 @@ getJasmineRequireObj().Configuration = function(j$) {
* @type Boolean
* @default false
*/
detectLateRejectionHandling: false
detectLateRejectionHandling: false,
/**
* The number of extra stack frames inserted by a wrapper around {@link it}
* or by some other local modification. Jasmine uses this to determine the
* filename for {@link SpecStartedEvent} and {@link SpecDoneEvent}.
* @name Configuration#extraItStackFrames
* @since 5.13.0
* @type number
* @default 0
*/
extraItStackFrames: 0,
/**
* The number of extra stack frames inserted by a wrapper around
* {@link describe} or by some other local modification. Jasmine uses this
* to determine the filename for {@link SpecStartedEvent} and
* {@link SpecDoneEvent}.
* @name Configuration#extraDescribeStackFrames
* @since 5.13.0
* @type number
* @default 0
*/
extraDescribeStackFrames: 0
};
Object.freeze(defaultConfig);
@@ -179,6 +202,16 @@ getJasmineRequireObj().Configuration = function(j$) {
if (changes.hasOwnProperty('verboseDeprecations')) {
this.#values.verboseDeprecations = changes.verboseDeprecations;
}
// 0 is a valid value for both of these, so a truthiness check wouldn't work
if (typeof changes.extraItStackFrames !== 'undefined') {
this.#values.extraItStackFrames = changes.extraItStackFrames;
}
if (typeof changes.extraDescribeStackFrames !== 'undefined') {
this.#values.extraDescribeStackFrames =
changes.extraDescribeStackFrames;
}
}
}

View File

@@ -1,4 +1,6 @@
getJasmineRequireObj().Env = function(j$) {
const DEFAULT_IT_DESCRIBE_STACK_DEPTH = 3;
/**
* @class Env
* @since 2.0.0
@@ -594,14 +596,14 @@ getJasmineRequireObj().Env = function(j$) {
this.describe = function(description, definitionFn) {
ensureIsNotNested('describe');
const filename = callerCallerFilename();
const filename = indirectCallerFilename(describeStackDepth());
return suiteBuilder.describe(description, definitionFn, filename)
.metadata;
};
this.xdescribe = function(description, definitionFn) {
ensureIsNotNested('xdescribe');
const filename = callerCallerFilename();
const filename = indirectCallerFilename(describeStackDepth());
return suiteBuilder.xdescribe(description, definitionFn, filename)
.metadata;
};
@@ -609,30 +611,38 @@ getJasmineRequireObj().Env = function(j$) {
this.fdescribe = function(description, definitionFn) {
ensureIsNotNested('fdescribe');
ensureNonParallel('fdescribe');
const filename = callerCallerFilename();
const filename = indirectCallerFilename(describeStackDepth());
return suiteBuilder.fdescribe(description, definitionFn, filename)
.metadata;
};
this.it = function(description, fn, timeout) {
ensureIsNotNested('it');
const filename = callerCallerFilename();
const filename = indirectCallerFilename(itStackDepth());
return suiteBuilder.it(description, fn, timeout, filename).metadata;
};
this.xit = function(description, fn, timeout) {
ensureIsNotNested('xit');
const filename = callerCallerFilename();
const filename = indirectCallerFilename(itStackDepth());
return suiteBuilder.xit(description, fn, timeout, filename).metadata;
};
this.fit = function(description, fn, timeout) {
ensureIsNotNested('fit');
ensureNonParallel('fit');
const filename = callerCallerFilename();
const filename = indirectCallerFilename(itStackDepth());
return suiteBuilder.fit(description, fn, timeout, filename).metadata;
};
function itStackDepth() {
return DEFAULT_IT_DESCRIBE_STACK_DEPTH + config.extraItStackFrames;
}
function describeStackDepth() {
return DEFAULT_IT_DESCRIBE_STACK_DEPTH + config.extraDescribeStackFrames;
}
/**
* Get a user-defined property as part of the properties field of {@link SpecResult}
* @name Env#getSpecProperty
@@ -818,11 +828,12 @@ getJasmineRequireObj().Env = function(j$) {
};
}
function callerCallerFilename() {
function indirectCallerFilename(depth) {
const frames = new j$.StackTrace(new Error()).frames;
// frames[3] should always exist except in Jasmine's own tests, which bypass
// the global it/describe layer, but don't crash if it doesn't.
return frames[3] && frames[3].file;
// The specified frame should always exist except in Jasmine's own tests,
// which bypass the global it/describe layer, but could be absent in case
// of misconfiguration. Don't crash if it's absent.
return frames[depth] && frames[depth].file;
}
return Env;

View File

@@ -99,12 +99,11 @@ getJasmineRequireObj().Spec = function(j$) {
* @property {String} description - The description passed to the {@link it} that created this spec.
* @property {String} fullName - The full description including all ancestors of this spec.
* @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
* @property {String} filename - Deprecated. The name of the file the spec was defined in.
* @property {String} filename - The name of the file the spec was defined in.
* Note: The value may be incorrect if zone.js is installed or
* `it`/`fit`/`xit` have been replaced with versions that don't maintain the
* same call stack height as the originals. This property may be removed in
* a future version unless there is enough user interest in keeping it.
* See {@link https://github.com/jasmine/jasmine/issues/2065}.
* same call stack height as the originals. You can fix that by setting
* {@link Configuration#extraItStackFrames}.
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed during execution of this spec.
* @property {ExpectationResult[]} passedExpectations - The list of expectations that passed during execution of this spec.
* @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
@@ -278,7 +277,20 @@ getJasmineRequireObj().Spec = function(j$) {
* @returns {Array.<string>}
* @since 5.7.0
*/
getPath: this.getPath.bind(this)
getPath: this.getPath.bind(this),
/**
* The name of the file the spec was defined in.
* Note: The value may be incorrect if zone.js is installed or
* `it`/`fit`/`xit` have been replaced with versions that don't maintain the
* same call stack height as the originals. You can fix that by setting
* {@link Configuration#extraItStackFrames}.
* @name Spec#filename
* @readonly
* @type {string}
* @since 5.13.0
*/
filename: this.filename
};
}

View File

@@ -101,12 +101,11 @@ getJasmineRequireObj().Suite = function(j$) {
* @property {String} description - The description text passed to the {@link describe} that made this suite.
* @property {String} fullName - The full description including all ancestors of this suite.
* @property {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe().
* @property {String} filename - Deprecated. The name of the file the suite was defined in.
* @property {String} filename - The name of the file the suite was defined in.
* Note: The value may be incorrect if zone.js is installed or
* `describe`/`fdescribe`/`xdescribe` have been replaced with versions that
* don't maintain the same call stack height as the originals. This property
* may be removed in a future version unless there is enough user interest
* in keeping it. See {@link https://github.com/jasmine/jasmine/issues/2065}.
* don't maintain the same call stack height as the originals. You can fix
* that by setting {@link Configuration#extraDescribeStackFrames}.
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
* @property {ExpectationResult[]} 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.
@@ -313,6 +312,19 @@ getJasmineRequireObj().Suite = function(j$) {
* @since 2.0.0
*/
this.description = suite.description;
/**
* The name of the file the suite was defined in.
* Note: The value may be incorrect if zone.js is installed or
* `describe`/`fdescribe`/`xdescribe` have been replaced with versions
* that don't maintain the same call stack height as the originals. You
* can fix that by setting {@link Configuration#extraItStackFrames}.
* @name Suite#filename
* @readonly
* @type {string}
* @since 5.13.0
*/
this.filename = suite.filename;
}
/**

View File

@@ -47,7 +47,10 @@ getJasmineRequireObj().TreeRunner = function(j$) {
_executeSpec(spec, specOverallDone) {
const onStart = next => {
this.#currentRunableTracker.setCurrentSpec(spec);
this.#runableResources.initForRunable(spec.id, spec.parentSuiteId);
this.#runableResources.initForRunable(
spec.id,
spec.parentSuiteId || this.#executionTree.topSuite.id
);
this.#reportDispatcher.specStarted(spec.result).then(next);
};
const resultCallback = (result, next) => {

View File

@@ -0,0 +1,27 @@
getJasmineRequireObj().AllOf = function(j$) {
function AllOf() {
const expectedValues = Array.from(arguments);
if (expectedValues.length === 0) {
throw new TypeError(
'jasmine.allOf() expects at least one argument to be passed.'
);
}
this.expectedValues = expectedValues;
}
AllOf.prototype.asymmetricMatch = function(other, matchersUtil) {
for (const expectedValue of this.expectedValues) {
if (!matchersUtil.equals(other, expectedValue)) {
return false;
}
}
return true;
};
AllOf.prototype.jasmineToString = function(pp) {
return '<jasmine.allOf(' + pp(this.expectedValues) + ')>';
};
return AllOf;
};

View File

@@ -221,6 +221,19 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
);
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value being compared matches every provided equality tester.
* @name asymmetricEqualityTesters.allOf
* @emittedName jasmine.allOf
* @since 5.13.0
* @function
* @param {...*} arguments - The asymmetric equality checkers to compare.
*/
j$.allOf = function() {
return new j$.AllOf(...arguments);
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value being compared is an instance of the specified class/constructor.

View File

@@ -35,6 +35,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
j$.util = jRequire.util(j$);
j$.errors = jRequire.errors();
j$.formatErrorMsg = jRequire.formatErrorMsg();
j$.AllOf = jRequire.AllOf(j$);
j$.Any = jRequire.Any(j$);
j$.Anything = jRequire.Anything(j$);
j$.CallTracker = jRequire.CallTracker(j$);