Compare commits
52 Commits
v6.0.0-alp
...
v6.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a6f6d2b0e | ||
|
|
6755b03f12 | ||
|
|
e759ddced2 | ||
|
|
1ad28d8515 | ||
|
|
3d36b11c8f | ||
|
|
a15df6d455 | ||
|
|
9f0488dc32 | ||
|
|
f3dba82b04 | ||
|
|
c999ce0787 | ||
|
|
5b76bf9552 | ||
|
|
9cf9b856b0 | ||
|
|
db6c142afd | ||
|
|
79405426fa | ||
|
|
00b09a9a04 | ||
|
|
f5e9b61f73 | ||
|
|
4371081763 | ||
|
|
9530ff68ab | ||
|
|
51dc79dc22 | ||
|
|
b559faec2a | ||
|
|
d7b1456584 | ||
|
|
23894c1a0a | ||
|
|
1e691b2470 | ||
|
|
c5555dd8cc | ||
|
|
32168be6c7 | ||
|
|
78c14f81a8 | ||
|
|
788eba34b6 | ||
|
|
1f31b4b0f6 | ||
|
|
00a8a11904 | ||
|
|
3899d83fb6 | ||
|
|
8f13684a01 | ||
|
|
bdf63f2402 | ||
|
|
9c2ffae2f9 | ||
|
|
7b2807b321 | ||
|
|
e930622548 | ||
|
|
56e2832ebe | ||
|
|
d31d33aeb3 | ||
|
|
e4c69e960e | ||
|
|
a8431f33bd | ||
|
|
4995c967ac | ||
|
|
9a9d3994da | ||
|
|
ff9feb29d3 | ||
|
|
fee7e6e64e | ||
|
|
18d4d38655 | ||
|
|
53e9bc68d2 | ||
|
|
2be50e1b87 | ||
|
|
27a1257b6d | ||
|
|
75658e0566 | ||
|
|
85322d1877 | ||
|
|
6667a42301 | ||
|
|
020dffd504 | ||
|
|
4201fd848f | ||
|
|
9a67c4e24d |
@@ -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
|
||||
|
||||
@@ -89,7 +89,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
|
||||
|
||||
23
.github/workflows/safari.yml
vendored
Normal file
23
.github/workflows/safari.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Test in latest available Safari
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
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
|
||||
2
LICENSE
2
LICENSE
@@ -1,5 +1,5 @@
|
||||
Copyright (c) 2008-2019 Pivotal Labs
|
||||
Copyright (c) 2008-2025 The Jasmine developers
|
||||
Copyright (c) 2008-2026 The Jasmine developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -30,7 +30,7 @@ Microsoft Edge) as well as Node.
|
||||
| Environment | Supported versions |
|
||||
|-------------------|----------------------------------|
|
||||
| Node | 20, 22, 24 |
|
||||
| Safari | 16*, 17* |
|
||||
| Safari | 16*, 17*, 26* |
|
||||
| Chrome | Evergreen |
|
||||
| Firefox | Evergreen, 102*, 115*, 128*, 140 |
|
||||
| Edge | Evergreen |
|
||||
@@ -60,5 +60,5 @@ To find out what environments work with a particular Jasmine release, see the [r
|
||||
* Sheel Choksi
|
||||
|
||||
Copyright (c) 2008-2019 Pivotal Labs<br>
|
||||
Copyright (c) 2008-2025 The Jasmine developers<br>
|
||||
Copyright (c) 2008-2026 The Jasmine developers<br>
|
||||
This software is licensed under the [MIT License](https://github.com/jasmine/jasmine/blob/main/LICENSE).
|
||||
|
||||
15
RELEASE.md
15
RELEASE.md
@@ -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
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Copyright (c) 2008-2019 Pivotal Labs
|
||||
Copyright (c) 2008-2025 The Jasmine developers
|
||||
Copyright (c) 2008-2026 The Jasmine developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Copyright (c) 2008-2019 Pivotal Labs
|
||||
Copyright (c) 2008-2025 The Jasmine developers
|
||||
Copyright (c) 2008-2026 The Jasmine developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Copyright (c) 2008-2019 Pivotal Labs
|
||||
Copyright (c) 2008-2025 The Jasmine developers
|
||||
Copyright (c) 2008-2026 The Jasmine developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@@ -42,36 +42,23 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
const urls = new jasmine.HtmlReporterV2Urls();
|
||||
|
||||
/**
|
||||
* ## Reporters
|
||||
* The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
|
||||
* Configures Jasmine based on the current set of query parameters. This
|
||||
* supports all parameters set by the HTML reporter as well as
|
||||
* spec=partialPath, which filters out specs whose paths don't contain the
|
||||
* parameter.
|
||||
*/
|
||||
const htmlReporter = new jasmine.HtmlReporterV2({
|
||||
env,
|
||||
urls,
|
||||
getContainer() {
|
||||
return document.body;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
|
||||
*/
|
||||
env.addReporter(jsApiReporter);
|
||||
env.addReporter(htmlReporter);
|
||||
env.configure(urls.configFromCurrentUrl());
|
||||
|
||||
/**
|
||||
* ## Execution
|
||||
*
|
||||
* Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
|
||||
*/
|
||||
const currentWindowOnload = window.onload;
|
||||
|
||||
window.onload = function() {
|
||||
if (currentWindowOnload) {
|
||||
currentWindowOnload();
|
||||
}
|
||||
htmlReporter.initialize();
|
||||
|
||||
// The HTML reporter needs to be set up here so it can access the DOM. Other
|
||||
// reporters can be added at any time before env.execute() is called.
|
||||
const htmlReporter = new jasmine.HtmlReporterV2({ env, urls });
|
||||
env.addReporter(htmlReporter);
|
||||
env.execute();
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Copyright (c) 2008-2019 Pivotal Labs
|
||||
Copyright (c) 2008-2025 The Jasmine developers
|
||||
Copyright (c) 2008-2026 The Jasmine developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@@ -35,6 +35,8 @@ jasmineRequire.html = function(j$) {
|
||||
j$.private.SymbolsView = jasmineRequire.SymbolsView(j$);
|
||||
j$.private.SummaryTreeView = jasmineRequire.SummaryTreeView(j$);
|
||||
j$.private.FailuresView = jasmineRequire.FailuresView(j$);
|
||||
j$.private.PerformanceView = jasmineRequire.PerformanceView(j$);
|
||||
j$.private.TabBar = jasmineRequire.TabBar(j$);
|
||||
j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
|
||||
j$.HtmlReporterV2Urls = jasmineRequire.HtmlReporterV2Urls(j$);
|
||||
j$.HtmlReporterV2 = jasmineRequire.HtmlReporterV2(j$);
|
||||
@@ -187,16 +189,52 @@ jasmineRequire.HtmlReporter = function(j$) {
|
||||
results.appendChild(summary.rootEl);
|
||||
|
||||
if (this.#stateBuilder.anyNonTopSuiteFailures) {
|
||||
this.#alerts.addFailureToggle(
|
||||
() => this.#setMenuModeTo('jasmine-failure-list'),
|
||||
() => this.#setMenuModeTo('jasmine-spec-list')
|
||||
);
|
||||
|
||||
this.#addFailureToggle();
|
||||
this.#setMenuModeTo('jasmine-failure-list');
|
||||
this.#failures.show();
|
||||
}
|
||||
}
|
||||
|
||||
#addFailureToggle() {
|
||||
const onClickFailures = () => this.#setMenuModeTo('jasmine-failure-list');
|
||||
const onClickSpecList = () => this.#setMenuModeTo('jasmine-spec-list');
|
||||
const failuresLink = createDom(
|
||||
'a',
|
||||
{ className: 'jasmine-failures-menu', href: '#' },
|
||||
'Failures'
|
||||
);
|
||||
let specListLink = createDom(
|
||||
'a',
|
||||
{ className: 'jasmine-spec-list-menu', href: '#' },
|
||||
'Spec List'
|
||||
);
|
||||
|
||||
failuresLink.onclick = function() {
|
||||
onClickFailures();
|
||||
return false;
|
||||
};
|
||||
|
||||
specListLink.onclick = function() {
|
||||
onClickSpecList();
|
||||
return false;
|
||||
};
|
||||
|
||||
this.#alerts.addBar(
|
||||
createDom(
|
||||
'span',
|
||||
{ className: 'jasmine-menu jasmine-bar jasmine-spec-list' },
|
||||
[createDom('span', {}, 'Spec List | '), failuresLink]
|
||||
)
|
||||
);
|
||||
this.#alerts.addBar(
|
||||
createDom(
|
||||
'span',
|
||||
{ className: 'jasmine-menu jasmine-bar jasmine-failure-list' },
|
||||
[specListLink, createDom('span', {}, ' | Failures ')]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#find(selector) {
|
||||
return this.#getContainer().querySelector(
|
||||
'.jasmine_html-reporter ' + selector
|
||||
@@ -271,12 +309,14 @@ jasmineRequire.HtmlSpecFilter = function(j$) {
|
||||
* @deprecated Use {@link HtmlReporterV2Urls} instead.
|
||||
*/
|
||||
function HtmlSpecFilter(options) {
|
||||
j$.getEnv().deprecated(
|
||||
const env = options?.env ?? j$.getEnv();
|
||||
env.deprecated(
|
||||
'HtmlReporter and HtmlSpecFilter are deprecated. Use HtmlReporterV2 instead.'
|
||||
);
|
||||
|
||||
const filterString =
|
||||
options &&
|
||||
options.filterString &&
|
||||
options.filterString() &&
|
||||
options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
||||
const filterPattern = new RegExp(filterString);
|
||||
@@ -434,38 +474,6 @@ jasmineRequire.AlertsView = function(j$) {
|
||||
);
|
||||
}
|
||||
|
||||
addFailureToggle(onClickFailures, onClickSpecList) {
|
||||
const failuresLink = createDom(
|
||||
'a',
|
||||
{ className: 'jasmine-failures-menu', href: '#' },
|
||||
'Failures'
|
||||
);
|
||||
let specListLink = createDom(
|
||||
'a',
|
||||
{ className: 'jasmine-spec-list-menu', href: '#' },
|
||||
'Spec List'
|
||||
);
|
||||
|
||||
failuresLink.onclick = function() {
|
||||
onClickFailures();
|
||||
return false;
|
||||
};
|
||||
|
||||
specListLink.onclick = function() {
|
||||
onClickSpecList();
|
||||
return false;
|
||||
};
|
||||
|
||||
this.#createAndAdd('jasmine-menu jasmine-bar jasmine-spec-list', [
|
||||
createDom('span', {}, 'Spec List | '),
|
||||
failuresLink
|
||||
]);
|
||||
this.#createAndAdd('jasmine-menu jasmine-bar jasmine-failure-list', [
|
||||
specListLink,
|
||||
createDom('span', {}, ' | Failures ')
|
||||
]);
|
||||
}
|
||||
|
||||
addGlobalFailure(failure) {
|
||||
this.#createAndAdd(
|
||||
errorBarClassName,
|
||||
@@ -895,7 +903,7 @@ jasmineRequire.htmlReporterUtils = function(j$) {
|
||||
const el = document.createElement(type);
|
||||
let children;
|
||||
|
||||
if (j$.private.isArray(childrenArrayOrVarArgs)) {
|
||||
if (Array.isArray(childrenArrayOrVarArgs)) {
|
||||
children = childrenArrayOrVarArgs;
|
||||
} else {
|
||||
children = [];
|
||||
@@ -946,6 +954,10 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
|
||||
const { createDom, noExpectations } = j$.private.htmlReporterUtils;
|
||||
|
||||
const specListTabId = 'jasmine-specListTab';
|
||||
const failuresTabId = 'jasmine-failuresTab';
|
||||
const perfTabId = 'jasmine-perfTab';
|
||||
|
||||
/**
|
||||
* @class HtmlReporterV2
|
||||
* @classdesc Displays results and allows re-running individual specs and suites.
|
||||
@@ -958,12 +970,12 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
* const reporter = new jasmine.HtmlReporterV2({
|
||||
* env,
|
||||
* urls,
|
||||
* container: document.body
|
||||
* // container is optional and defaults to document.body.
|
||||
* container: someElement
|
||||
* });
|
||||
*/
|
||||
class HtmlReporterV2 {
|
||||
#env;
|
||||
#getContainer;
|
||||
#container;
|
||||
#queryString;
|
||||
#urlBuilder;
|
||||
#filterSpecs;
|
||||
@@ -974,14 +986,13 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
// Sub-views
|
||||
#alerts;
|
||||
#statusBar;
|
||||
#tabBar;
|
||||
#progress;
|
||||
#banner;
|
||||
#failures;
|
||||
|
||||
constructor(options) {
|
||||
this.#env = options.env;
|
||||
|
||||
this.#getContainer = options.getContainer;
|
||||
this.#container = options.container || document.body;
|
||||
this.#queryString =
|
||||
options.queryString ||
|
||||
new j$.QueryString({
|
||||
@@ -994,16 +1005,8 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
getSuiteById: id => this.#stateBuilder.suitesById[id]
|
||||
});
|
||||
this.#filterSpecs = options.urls.filteringSpecs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the reporter. Should be called before {@link Env#execute}.
|
||||
* @function
|
||||
* @name HtmlReporter#initialize
|
||||
*/
|
||||
initialize() {
|
||||
this.#clearPrior();
|
||||
this.#config = this.#env ? this.#env.configuration() : {};
|
||||
this.#config = options.env ? options.env.configuration() : {};
|
||||
|
||||
this.#stateBuilder = new j$.private.ResultsStateBuilder();
|
||||
|
||||
@@ -1011,6 +1014,25 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
this.#statusBar = new j$.private.OverallStatusBar(this.#urlBuilder);
|
||||
this.#statusBar.showRunning();
|
||||
this.#alerts.addBar(this.#statusBar.rootEl);
|
||||
|
||||
this.#tabBar = new j$.private.TabBar(
|
||||
[
|
||||
{ id: specListTabId, label: 'Spec List' },
|
||||
{ id: failuresTabId, label: 'Failures' },
|
||||
{ id: perfTabId, label: 'Performance' }
|
||||
],
|
||||
tabId => {
|
||||
if (tabId === specListTabId) {
|
||||
this.#setMenuModeTo('jasmine-spec-list');
|
||||
} else if (tabId === failuresTabId) {
|
||||
this.#setMenuModeTo('jasmine-failure-list');
|
||||
} else {
|
||||
this.#setMenuModeTo('jasmine-performance');
|
||||
}
|
||||
}
|
||||
);
|
||||
this.#alerts.addBar(this.#tabBar.rootEl);
|
||||
|
||||
this.#progress = new ProgressView();
|
||||
this.#banner = new j$.private.Banner(
|
||||
this.#queryString.navigateWithNewParam.bind(this.#queryString),
|
||||
@@ -1025,13 +1047,15 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
this.#alerts.rootEl,
|
||||
this.#failures.rootEl
|
||||
);
|
||||
this.#getContainer().appendChild(this.#htmlReporterMain);
|
||||
this.#container.appendChild(this.#htmlReporterMain);
|
||||
this.#failures.show();
|
||||
}
|
||||
|
||||
jasmineStarted(options) {
|
||||
this.#stateBuilder.jasmineStarted(options);
|
||||
this.#progress.start(options.totalSpecsDefined);
|
||||
this.#progress.start(
|
||||
options.totalSpecsDefined - options.numExcludedSpecs
|
||||
);
|
||||
}
|
||||
|
||||
suiteStarted(result) {
|
||||
@@ -1101,32 +1125,26 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
);
|
||||
summary.addResults(this.#stateBuilder.topResults);
|
||||
results.appendChild(summary.rootEl);
|
||||
const perf = new j$.private.PerformanceView();
|
||||
perf.addResults(this.#stateBuilder.topResults);
|
||||
results.appendChild(perf.rootEl);
|
||||
this.#tabBar.showTab(specListTabId);
|
||||
this.#tabBar.showTab(perfTabId);
|
||||
|
||||
if (this.#stateBuilder.anyNonTopSuiteFailures) {
|
||||
this.#alerts.addFailureToggle(
|
||||
() => this.#setMenuModeTo('jasmine-failure-list'),
|
||||
() => this.#setMenuModeTo('jasmine-spec-list')
|
||||
);
|
||||
|
||||
this.#setMenuModeTo('jasmine-failure-list');
|
||||
this.#failures.show();
|
||||
this.#tabBar.showTab(failuresTabId);
|
||||
this.#tabBar.selectTab(failuresTabId);
|
||||
} else {
|
||||
this.#tabBar.selectTab(specListTabId);
|
||||
}
|
||||
}
|
||||
|
||||
#find(selector) {
|
||||
return this.#getContainer().querySelector(
|
||||
return this.#container.querySelector(
|
||||
'.jasmine_html-reporter ' + selector
|
||||
);
|
||||
}
|
||||
|
||||
#clearPrior() {
|
||||
const oldReporter = this.#find('');
|
||||
|
||||
if (oldReporter) {
|
||||
this.#getContainer().removeChild(oldReporter);
|
||||
}
|
||||
}
|
||||
|
||||
#setMenuModeTo(mode) {
|
||||
this.#htmlReporterMain.setAttribute(
|
||||
'class',
|
||||
@@ -1145,10 +1163,11 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
}
|
||||
|
||||
specDone(result) {
|
||||
this.rootEl.value = this.rootEl.value + 1;
|
||||
if (result.status !== 'excluded') {
|
||||
this.rootEl.value = this.rootEl.value + 1;
|
||||
}
|
||||
|
||||
if (result.status === 'failed') {
|
||||
// TODO: also a non-color indicator
|
||||
this.rootEl.classList.add('failed');
|
||||
}
|
||||
}
|
||||
@@ -1233,7 +1252,10 @@ jasmineRequire.HtmlReporterV2Urls = function(j$) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Configuration} from the current page's URL.
|
||||
* Creates a {@link Configuration} from the current page's URL. Supported
|
||||
* query string parameters include all those set by {@link HtmlReporterV2}
|
||||
* as well as spec=partialPath, which filters out specs whose paths don't
|
||||
* contain partialPath.
|
||||
* @returns {Configuration}
|
||||
* @example
|
||||
* const urls = new jasmine.HtmlReporterV2Urls();
|
||||
@@ -1259,9 +1281,10 @@ jasmineRequire.HtmlReporterV2Urls = function(j$) {
|
||||
}
|
||||
|
||||
const specFilter = new j$.private.HtmlSpecFilterV2({
|
||||
filterString: () => {
|
||||
return this.queryString.getParam('path');
|
||||
}
|
||||
filterParams: () => ({
|
||||
path: this.queryString.getParam('path'),
|
||||
spec: this.queryString.getParam('spec')
|
||||
})
|
||||
});
|
||||
|
||||
config.specFilter = function(spec) {
|
||||
@@ -1281,35 +1304,35 @@ jasmineRequire.HtmlReporterV2Urls = function(j$) {
|
||||
|
||||
jasmineRequire.HtmlSpecFilterV2 = function() {
|
||||
class HtmlSpecFilterV2 {
|
||||
#getFilterString;
|
||||
#getFilterParams;
|
||||
|
||||
constructor(options) {
|
||||
this.#getFilterString = options.filterString;
|
||||
this.#getFilterParams = options.filterParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the spec with the specified name should be executed.
|
||||
* @name HtmlSpecFilterV2#matches
|
||||
* @function
|
||||
* @param {Spec} spec
|
||||
* @returns {boolean}
|
||||
*/
|
||||
matches(spec) {
|
||||
const filterString = this.#getFilterString();
|
||||
const params = this.#getFilterParams();
|
||||
|
||||
if (!filterString) {
|
||||
return true;
|
||||
if (params.path) {
|
||||
return this.#matchesPath(spec, JSON.parse(params.path));
|
||||
} else if (params.spec) {
|
||||
// Like legacy HtmlSpecFilter, retained because it's convenient for
|
||||
// hand-constructing filter URLs
|
||||
return spec.getFullName().includes(params.spec);
|
||||
}
|
||||
|
||||
const filterPath = JSON.parse(this.#getFilterString());
|
||||
return true;
|
||||
}
|
||||
|
||||
#matchesPath(spec, path) {
|
||||
const specPath = spec.getPath();
|
||||
|
||||
if (filterPath.length > specPath.length) {
|
||||
if (path.length > specPath.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < filterPath.length; i++) {
|
||||
if (specPath[i] !== filterPath[i]) {
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
if (specPath[i] !== path[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1430,6 +1453,105 @@ jasmineRequire.OverallStatusBar = function(j$) {
|
||||
return OverallStatusBar;
|
||||
};
|
||||
|
||||
jasmineRequire.PerformanceView = function(j$) {
|
||||
const createDom = j$.private.htmlReporterUtils.createDom;
|
||||
const MAX_SLOW_SPECS = 20;
|
||||
|
||||
class PerformanceView {
|
||||
#summary;
|
||||
#tbody;
|
||||
|
||||
constructor() {
|
||||
this.#tbody = document.createElement('tbody');
|
||||
this.#summary = document.createElement('div');
|
||||
this.rootEl = createDom(
|
||||
'div',
|
||||
{ className: 'jasmine-performance-view' },
|
||||
createDom('h2', {}, 'Performance'),
|
||||
this.#summary,
|
||||
createDom('h3', {}, 'Slowest Specs'),
|
||||
createDom(
|
||||
'table',
|
||||
{},
|
||||
createDom(
|
||||
'thead',
|
||||
{},
|
||||
createDom(
|
||||
'tr',
|
||||
{},
|
||||
createDom('th', {}, 'Duration'),
|
||||
createDom('th', {}, 'Spec Name')
|
||||
)
|
||||
),
|
||||
this.#tbody
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
addResults(resultsTree) {
|
||||
const specResults = [];
|
||||
getSpecResults(resultsTree, specResults);
|
||||
|
||||
if (specResults.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
specResults.sort(function(a, b) {
|
||||
if (a.duration < b.duration) {
|
||||
return 1;
|
||||
} else if (a.duration > b.duration) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
this.#populateSumary(specResults);
|
||||
this.#populateTable(specResults);
|
||||
}
|
||||
|
||||
#populateSumary(specResults) {
|
||||
const total = specResults.map(r => r.duration).reduce((a, b) => a + b, 0);
|
||||
const mean = total / specResults.length;
|
||||
const median = specResults[Math.floor(specResults.length / 2)].duration;
|
||||
this.#summary.appendChild(
|
||||
document.createTextNode(`Mean spec run time: ${mean.toFixed(0)}ms`)
|
||||
);
|
||||
this.#summary.appendChild(document.createElement('br'));
|
||||
this.#summary.appendChild(
|
||||
document.createTextNode(`Median spec run time: ${median}ms`)
|
||||
);
|
||||
}
|
||||
|
||||
#populateTable(specResults) {
|
||||
specResults = specResults.slice(0, MAX_SLOW_SPECS);
|
||||
|
||||
for (const r of specResults) {
|
||||
this.#tbody.appendChild(
|
||||
createDom(
|
||||
'tr',
|
||||
{},
|
||||
createDom('td', {}, `${r.duration}ms`),
|
||||
createDom('td', {}, r.fullName)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSpecResults(resultsTree, dest) {
|
||||
for (const node of resultsTree.children) {
|
||||
if (node.type === 'suite') {
|
||||
getSpecResults(node, dest);
|
||||
} else if (node.result.status !== 'excluded') {
|
||||
dest.push(node.result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PerformanceView;
|
||||
};
|
||||
|
||||
jasmineRequire.ResultsStateBuilder = function(j$) {
|
||||
'use strict';
|
||||
|
||||
@@ -1661,3 +1783,81 @@ jasmineRequire.SymbolsView = function(j$) {
|
||||
|
||||
return SymbolsView;
|
||||
};
|
||||
|
||||
jasmineRequire.TabBar = function(j$) {
|
||||
const createDom = j$.private.htmlReporterUtils.createDom;
|
||||
|
||||
class TabBar {
|
||||
#tabs;
|
||||
#onSelectTab;
|
||||
|
||||
// tabSpecs should be an array of {id, label}.
|
||||
// All tabs are initially not visible and not selected.
|
||||
constructor(tabSpecs, onSelectTab) {
|
||||
this.#onSelectTab = onSelectTab;
|
||||
this.#tabs = [];
|
||||
this.#tabs = tabSpecs.map(ts => new Tab(ts, () => this.selectTab(ts.id)));
|
||||
|
||||
this.rootEl = createDom(
|
||||
'span',
|
||||
{ className: 'jasmine-menu jasmine-bar' },
|
||||
this.#tabs.map(t => t.rootEl)
|
||||
);
|
||||
}
|
||||
|
||||
showTab(id) {
|
||||
for (const tab of this.#tabs) {
|
||||
if (tab.rootEl.id === id) {
|
||||
tab.setVisibility(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectTab(id) {
|
||||
for (const tab of this.#tabs) {
|
||||
tab.setSelected(tab.rootEl.id === id);
|
||||
}
|
||||
|
||||
this.#onSelectTab(id);
|
||||
}
|
||||
}
|
||||
|
||||
class Tab {
|
||||
#spec;
|
||||
#onClick;
|
||||
|
||||
constructor(spec, onClick) {
|
||||
this.#spec = spec;
|
||||
this.#onClick = onClick;
|
||||
this.rootEl = createDom(
|
||||
'span',
|
||||
{ id: spec.id, className: 'jasmine-tab jasmine-hidden' },
|
||||
this.#createLink()
|
||||
);
|
||||
}
|
||||
|
||||
setVisibility(visible) {
|
||||
this.rootEl.classList.toggle('jasmine-hidden', !visible);
|
||||
}
|
||||
|
||||
setSelected(selected) {
|
||||
if (selected) {
|
||||
this.rootEl.textContent = this.#spec.label;
|
||||
} else {
|
||||
this.rootEl.textContent = '';
|
||||
this.rootEl.appendChild(this.#createLink());
|
||||
}
|
||||
}
|
||||
|
||||
#createLink() {
|
||||
const link = createDom('a', { href: '#' }, this.#spec.label);
|
||||
link.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
this.#onClick();
|
||||
});
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
||||
return TabBar;
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ body {
|
||||
background-color: #eee;
|
||||
padding: 5px;
|
||||
margin: -8px;
|
||||
font-size: 11px;
|
||||
font-size: 12px;
|
||||
font-family: Monaco, "Lucida Console", monospace;
|
||||
line-height: 14px;
|
||||
color: #333;
|
||||
@@ -63,7 +63,7 @@ body {
|
||||
float: right;
|
||||
line-height: 28px;
|
||||
padding-right: 9px;
|
||||
font-size: 11px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.jasmine_html-reporter .jasmine-symbol-summary {
|
||||
overflow: hidden;
|
||||
@@ -198,11 +198,17 @@ body {
|
||||
color: white;
|
||||
}
|
||||
.jasmine_html-reporter.jasmine-spec-list .jasmine-bar.jasmine-menu.jasmine-failure-list,
|
||||
.jasmine_html-reporter.jasmine-spec-list .jasmine-results .jasmine-failures {
|
||||
.jasmine_html-reporter.jasmine-spec-list .jasmine-results .jasmine-failures,
|
||||
.jasmine_html-reporter.jasmine-spec-list .jasmine-performance-view {
|
||||
display: none;
|
||||
}
|
||||
.jasmine_html-reporter.jasmine-failure-list .jasmine-bar.jasmine-menu.jasmine-spec-list,
|
||||
.jasmine_html-reporter.jasmine-failure-list .jasmine-summary {
|
||||
.jasmine_html-reporter.jasmine-failure-list .jasmine-summary,
|
||||
.jasmine_html-reporter.jasmine-failure-list .jasmine-performance-view {
|
||||
display: none;
|
||||
}
|
||||
.jasmine_html-reporter.jasmine-performance .jasmine-results .jasmine-failures,
|
||||
.jasmine_html-reporter.jasmine-performance .jasmine-summary {
|
||||
display: none;
|
||||
}
|
||||
.jasmine_html-reporter .jasmine-results {
|
||||
@@ -323,4 +329,23 @@ body {
|
||||
}
|
||||
.jasmine_html-reporter .jasmine-debug-log .jasmine-debug-log-msg {
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.jasmine-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jasmine-tab + .jasmine-tab:before {
|
||||
content: " | ";
|
||||
}
|
||||
|
||||
.jasmine-performance-view h2, .jasmine-performance-view h3 {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.jasmine-performance-view table {
|
||||
border-spacing: 5px;
|
||||
}
|
||||
.jasmine-performance-view th, .jasmine-performance-view td {
|
||||
text-align: left;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jasmine-core",
|
||||
"license": "MIT",
|
||||
"version": "6.0.0-alpha.1",
|
||||
"version": "6.0.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jasmine/jasmine.git"
|
||||
@@ -43,10 +43,10 @@
|
||||
"ejs": "^3.1.10",
|
||||
"eslint": "^9.24.0",
|
||||
"eslint-plugin-compat": "^6.0.2",
|
||||
"glob": "^10.2.3",
|
||||
"glob": "^13.0.0",
|
||||
"globals": "^16.0.0",
|
||||
"jasmine": "github:jasmine/jasmine-npm#6.0",
|
||||
"jasmine-browser-runner": "github:jasmine/jasmine-browser-runner#4.0",
|
||||
"jasmine": "github:jasmine/jasmine-npm",
|
||||
"jasmine-browser-runner": "github:jasmine/jasmine-browser-runner",
|
||||
"jsdom": "^26.0.0",
|
||||
"prettier": "1.17.1",
|
||||
"sass": "^1.58.3"
|
||||
|
||||
29
release_notes/5.12.1.md
Normal file
29
release_notes/5.12.1.md
Normal 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)_
|
||||
44
release_notes/5.13.0.md
Normal file
44
release_notes/5.13.0.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Jasmine Core 5.13.0 Release Notes
|
||||
|
||||
## Changes to supported environments
|
||||
|
||||
Safari 26 is now supported on a best-effort basis.
|
||||
|
||||
Due to the limited availability of Safari 18 and later on free CI services,
|
||||
Safari support in future jasmine-core versions will be limited to:
|
||||
|
||||
* Best-effort support for the latest Safari version available on GitHub Actions,
|
||||
which may change at any time.
|
||||
* Best-effort support for Safari 16 and 17 for as long as it remains practical.
|
||||
|
||||
## New Features
|
||||
|
||||
* New `extraItStackFrames` and `extraDescribeStackFrames` config options to fix
|
||||
the filename properties of reporter events in configurations that wrap
|
||||
`it`/`describe`, such as zone.js. The `filename` properties of reporter events
|
||||
are no longer deprecated.
|
||||
* `jasmine.allOf` asymmetric equality tester
|
||||
* Merges [#2087](https://github.com/jasmine/jasmine/issues/2083) from @jonahd-g
|
||||
* Fixes [#2083](https://github.com/jasmine/jasmine/pull/2087)
|
||||
|
||||
## Supported environments
|
||||
|
||||
This version has been tested in the following environments.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------|--------------------------------|
|
||||
| Node | 18.20.5**, 20, 22, 24 |
|
||||
| Safari** | 16, 17, 26.1 |
|
||||
| Chrome | 142* |
|
||||
| Firefox | 102**, 115**, 128**, 140, 145* |
|
||||
| Edge | 142* |
|
||||
|
||||
\* 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)_
|
||||
90
release_notes/6.0.0-alpha.2.md
Normal file
90
release_notes/6.0.0-alpha.2.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Jasmine Core 6.0.0-alpha.2 Release Notes
|
||||
|
||||
This is a pre-release, intended to offer a preview of upcoming 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.2`,
|
||||
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.2`.
|
||||
|
||||
|
||||
## Changes to supported environments
|
||||
|
||||
Safari 26 is now supported on a best-effort basis.†
|
||||
|
||||
Due to the limited availability of Safari 18 and later on free CI services,
|
||||
Safari support in future jasmine-core versions will be limited to:
|
||||
|
||||
* Best-effort support for the latest Safari version available on GitHub Actions,
|
||||
which may change at any time.
|
||||
* Best-effort support for Safari 16 and 17 for as long as it remains practical.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fix custom matchers in top-level specs††
|
||||
* Merges [#2088](https://github.com/jasmine/jasmine/pull/2088) from @bonkevin
|
||||
|
||||
## New features
|
||||
|
||||
* Larger body font size in HTML reporters
|
||||
* New Performance tab in HtmlReporterV2 shows metrics and a list of the slowest
|
||||
specs.
|
||||
* Experimental `safariYieldStrategy: "time"` config option, which may make
|
||||
Jasmine run significantly faster in Safari and similar browsers. So far, this
|
||||
option has not been tested on a wide variety of workloads. Feedback is
|
||||
appreciated.
|
||||
* New `extraItStackFrames` and `extraDescribeStackFrames` config options to fix
|
||||
the filename properties of reporter events in configurations that wrap
|
||||
`it`/`describe`, such as zone.js.†
|
||||
* `jasmine.allOf asymmetric` equality tester†
|
||||
* Merges [#2087](https://github.com/jasmine/jasmine/pull/2087) from @jonahd-g
|
||||
* Fixes [#2083](https://github.com/jasmine/jasmine/issues/2083)
|
||||
* Re-add support for partial spec name filtering via `spec` query parameter
|
||||
* Fixes [#2085](https://github.com/jasmine/jasmine/issues/2085).
|
||||
* Require spec/suite property keys to be strings, not just anything that's
|
||||
cloneable and serializable. This matches the existing API reference
|
||||
documentation.
|
||||
|
||||
## Documentation improvements
|
||||
|
||||
* Fix HtmlReporterV2 ctor example
|
||||
|
||||
## Internal Improvements
|
||||
|
||||
* Remove code to support browsers that don't have MessageChannel. Jasmine hasn't
|
||||
run in any such browsers since 2.x.
|
||||
|
||||
† Also likely to be included in a future 5.x release.<br>
|
||||
†† Also released in 5.12.1.
|
||||
|
||||
## Supported environments
|
||||
|
||||
This version has been tested in the following environments.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|--------------------------------|
|
||||
| Node | 20, 22, 24 |
|
||||
| Safari | 16**, 17**, 26** |
|
||||
| Chrome | 142* |
|
||||
| Firefox | 102**, 115**, 128**, 140, 145* |
|
||||
| Edge | 142* |
|
||||
|
||||
\* 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)_
|
||||
74
release_notes/6.0.0-beta.0.md
Normal file
74
release_notes/6.0.0-beta.0.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Jasmine Core 6.0.0-beta.0 Release Notes
|
||||
|
||||
This is a pre-release, intended to offer a preview of upcoming 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-beta.0`,
|
||||
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-beta.0`.
|
||||
|
||||
|
||||
## Breaking changes
|
||||
|
||||
* boot1.js no longer adds jsApiReporter to the env.
|
||||
* HtmlReporterV2 initialization and boot1.js have been simplified. If you
|
||||
maintain your own boot file, update it to match the boot1.js included in this
|
||||
package.
|
||||
|
||||
|
||||
## New features
|
||||
|
||||
* Statically exposed pretty printer as jasmine.pp().
|
||||
|
||||
## Bug fixes
|
||||
|
||||
* Fixed HtmlReporterV2 progress bar when running a subset of specs.
|
||||
|
||||
|
||||
## Deprecations
|
||||
|
||||
* jsApiReporter is deprecated.
|
||||
* Detect monkey patching and emit a deprecation warning.
|
||||
|
||||
## Documentation improvements
|
||||
|
||||
* Documented the set of possible spec statuses.
|
||||
|
||||
|
||||
## Internal improvements
|
||||
|
||||
* Replaced isArray helper with native Array.isArray
|
||||
|
||||
|
||||
## Supported environments
|
||||
|
||||
This version has been tested in the following environments.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|--------------------------------|
|
||||
| Node | 20, 22, 24 |
|
||||
| Safari | 16**, 17**, 26.1** |
|
||||
| Chrome | 142* |
|
||||
| Firefox | 102**, 115**, 128**, 140, 145* |
|
||||
| Edge | 142* |
|
||||
|
||||
\* 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)_
|
||||
47
release_notes/6.0.0-beta.1.md
Normal file
47
release_notes/6.0.0-beta.1.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Jasmine Core 6.0.0-beta.1 Release Notes
|
||||
|
||||
This is a pre-release, intended to offer a preview of upcoming changes and to
|
||||
solicit feedback.
|
||||
|
||||
A corresponding release of the `jasmine` package is not planned because the
|
||||
change in this release only affects browser users.
|
||||
|
||||
## 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-beta.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-beta.1`.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Revert to using window.onload in boot1.js. This fixes top level await in
|
||||
jasmine-browser-runner.
|
||||
|
||||
## Supported environments
|
||||
|
||||
This version has been tested in the following environments.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|--------------------------------|
|
||||
| Node | 20, 22, 24 |
|
||||
| Safari** | 16, 17, 26.1 |
|
||||
| Chrome | 143* |
|
||||
| Firefox | 102**, 115**, 128**, 140, 145* |
|
||||
| Edge | 142* |
|
||||
|
||||
\* 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)_
|
||||
190
release_notes/6.0.0.md
Normal file
190
release_notes/6.0.0.md
Normal file
@@ -0,0 +1,190 @@
|
||||
# Jasmine Core 6.0.0 Release Notes
|
||||
|
||||
## Summary
|
||||
|
||||
This is a major release that includes breaking changes as well as significant
|
||||
new features. Many of the breaking changes and deprecations in this release are
|
||||
intended to improve the stability of the Jasmine ecosystem by making the
|
||||
distinction between public and private APIs more obvious, reducing exposure of
|
||||
jasmine-core's internal state, removing ambiguities from the reporter API, and
|
||||
warning about monkey patching.
|
||||
|
||||
6.x is intended to ba a relatively short-lived, transitional series. It is
|
||||
compatible with the current versions of karam-jasmine and other legacy Angular
|
||||
tools but emits deprecation warnings when used with them. 7.0 will drop
|
||||
compatibility with those tools. If you use Karma in a non-Angular context,
|
||||
consider migrating to a maintained alternative such as jasmine-browser-runner or
|
||||
web-test-runner. If you use Angular, please direct any questions about support
|
||||
for future versions of Jasmine to the Angular team.
|
||||
|
||||
Please see the [migration guide](https://jasmine.github.io/upgrade-guides/6.0)
|
||||
for more information. If you use Jasmine via the `jasmine` package, please see
|
||||
[its release notes](https://github.com/jasmine/jasmine-npm/blob/main/release_notes/6.0.0.md)
|
||||
as well.
|
||||
|
||||
|
||||
## Changes to supported environments
|
||||
|
||||
* Node 18 is no longer supported.
|
||||
|
||||
|
||||
## Breaking changes
|
||||
|
||||
### General
|
||||
|
||||
* Private APIs have been removed from the `jasmine` namespace.
|
||||
|
||||
The purpose of this change is to reduce the risk of users inadvertently
|
||||
depending on private APIs. Anything not covered by
|
||||
[the documentation](https://jasmine.github.io/pages/docs_home.html) remains
|
||||
private regardless of namespacing. Private APIs may be changed or removed in
|
||||
any release. This change is being made in a major release as a courtesy to
|
||||
users of libraries that depend on private APIs.
|
||||
|
||||
### Changes that affect spec writing
|
||||
|
||||
* Global error spies always receive a single argument. Previously, the browser
|
||||
error event was passed as the second argument.
|
||||
* If a spy is invoked via `.call(null, ...)` or `.apply(null, ...)`, the spy's
|
||||
`this` argument will be `null`. Previously it was `globalThis`.
|
||||
* Mock clock timing functions cannot be spied on. Previously this "worked" but
|
||||
prevented the mock clock from uninstalling itself.
|
||||
* The mock clock no longer supports the eval forms of `setTimeout` and
|
||||
`setInterval`.
|
||||
* Keys passed to `setSpecProperty`/`setSuiteProperty` must be strings. Values
|
||||
must be both structured-cloneable and JSON-serializable.
|
||||
|
||||
### Changes that affect configuration
|
||||
|
||||
* HTML reporters cache configuration throughout each run. Configuration changes
|
||||
made while specs are running will not affect reporter behavior.
|
||||
* If an execution order is passed to `Env#execute`, it must not enter any suite
|
||||
more than once.
|
||||
* The argument passed to spec filters is a
|
||||
[spec metadata](https://jasmine.github.io/api/6.0/Spec.html)
|
||||
instance, not the internal spec object.
|
||||
* The default value of the `forbidDuplicateNames` config option has been
|
||||
changed to true.
|
||||
|
||||
### Changes that affect reporters
|
||||
|
||||
This release includes changes that are intended to streamline and clarify the
|
||||
reporter interface, prevent sharing of mutable state, and prevent bugs involving
|
||||
non-serializable objects. These changes should be compatible with most existing
|
||||
reporters but could break reporters that manage their internal state in unusual
|
||||
ways.
|
||||
|
||||
* Meaningless properties such as `status` and `failedExpectations` are omitted
|
||||
from the events passed to [suiteStarted](https://jasmine.github.io/api/6.0/global.html#SuiteStartedEvent)
|
||||
and [specStarted](https://jasmine.github.io/api/6.0/global.html#SpecStartedEvent).
|
||||
* Reporter events are deep-cloned before being passed to each reporter. This
|
||||
protects reporters against later mutation by jasmine-core or other reporters.
|
||||
* The `expected` and `actual` properties of
|
||||
[passed and failed expectations](https://jasmine.github.io/api/6.0/global.html#ExpectationResult)
|
||||
have been removed.
|
||||
* The [order](https://jasmine.github.io/api/6.0/global.html#Order)
|
||||
property of the`jasmineStarted` and `jasmineDone` reporter events no longer
|
||||
includes undocumented properties.
|
||||
* boot1.js no longer adds `jsApiReporter` to the env.
|
||||
|
||||
### Changes that affect browser boot files
|
||||
|
||||
* The `createElement` and `createTextNode` options of the legacy `HtmlReporter`
|
||||
are ignored. `HtmlReporter` now unconditionally uses `document.createElement`
|
||||
and `document.createTextNode`.
|
||||
|
||||
### Changes to Node boot functions
|
||||
|
||||
* [boot](https://jasmine.github.io/api/6.0/module-jasmine-core.html#.boot)
|
||||
defaults to creating a new core instance each time it's called. This restores
|
||||
the pre-5.0 default behavior.
|
||||
* [noGlobals](https://jasmine.github.io/api/6.0/module-jasmine-core.html#.noGlobals)
|
||||
no longer takes a parameter. It always returns the same object when called
|
||||
repeatedly.
|
||||
|
||||
|
||||
## 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.
|
||||
* A Performance tab shows metrics and a list of the slowest specs.
|
||||
* 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 boot files
|
||||
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 other software that
|
||||
interprets the `spec` parameter, such as karma-jasmine, you won't be able to
|
||||
adopt `HtmlReporterV2` unlesss that other software is updated.
|
||||
|
||||
* Larger body font size in HTML reporters
|
||||
* `globalThis` is used 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.
|
||||
* Experimental [`safariYieldStrategy: "time"`](https://jasmine.github.io/api/6.0/Configuration.html#safariYieldStrategy)
|
||||
config option, which may make Jasmine run significantly faster in Safari and
|
||||
similar browsers. So far, this option has not been tested on a wide variety of
|
||||
workloads. Feedback is appreciated.
|
||||
* Statically exposed pretty printer as `jasmine.pp()`.
|
||||
|
||||
|
||||
## Deprecations
|
||||
|
||||
* Common monkey patching patterns are detected and result in a deprecation
|
||||
warning. Code that overwrites anything provided by jasmine-core (other than
|
||||
globals like `it`/`expect`/etc or configuration properties like
|
||||
`jasmine.DEFAULT_TIMEOUT_INTERVAL`) may be broken by any jasmine-core release.
|
||||
* 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.
|
||||
* `HtmlReporter` and `HtmlSpecFilter` are deprecated in favor of `HtmlReporterV2`.
|
||||
* `jsApiReporter` is deprecated.
|
||||
|
||||
|
||||
## Documentation improvements
|
||||
|
||||
* Improved API reference documentation for APIs that are used from browser boot
|
||||
files.
|
||||
* Documented the set of possible spec statuses.
|
||||
* Documented that globals are overwriteable.
|
||||
|
||||
|
||||
## Internal improvements
|
||||
|
||||
* 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.
|
||||
* Removed code to support browsers that don't have `MessageChannel`. Jasmine
|
||||
hasn't run in any such browsers since 2.x.
|
||||
* Replaced `isArray` helper with native `Array.isArray`.
|
||||
|
||||
## Supported environments
|
||||
|
||||
This version has been tested in the following environments.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|--------------------------------|
|
||||
| Node | 20, 22, 24 |
|
||||
| Safari** | 16, 17, 26.2 |
|
||||
| Chrome | 143* |
|
||||
| Firefox | 102**, 115**, 128**, 140, 147* |
|
||||
| Edge | 143* |
|
||||
|
||||
\* 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)_
|
||||
@@ -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
|
||||
@@ -39,8 +44,8 @@ else
|
||||
fi
|
||||
run_browser firefox 102
|
||||
|
||||
run_browser safari 17
|
||||
run_browser safari 16
|
||||
# run_browser safari 17
|
||||
# run_browser safari 16
|
||||
|
||||
run_browser MicrosoftEdge latest
|
||||
|
||||
@@ -1247,4 +1247,25 @@ describe('Clock (acceptance)', function() {
|
||||
|
||||
clock.tick(400);
|
||||
});
|
||||
|
||||
describe('Warning about monkey patching', function() {
|
||||
for (const name of ['tick', 'mockDate', 'install', 'uninstall']) {
|
||||
it(`warns if Clock#${name} is monkey patched`, function() {
|
||||
spyOn(console, 'error');
|
||||
const clock = new privateUnderTest.Clock({}, function() {}, {});
|
||||
const patch = {};
|
||||
clock[name] = patch;
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.error).toHaveBeenCalledOnceWith(
|
||||
jasmine.stringContaining('DEPRECATION: Monkey patching detected.')
|
||||
);
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.error).toHaveBeenCalledOnceWith(
|
||||
jasmine.stringContaining('ClockSpec.js')
|
||||
);
|
||||
expect(clock[name]).toBe(patch);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,7 +10,14 @@ describe('Configuration', function() {
|
||||
'detectLateRejectionHandling',
|
||||
'verboseDeprecations'
|
||||
];
|
||||
const allKeys = [...standardBooleanKeys, 'seed', 'specFilter'];
|
||||
const allKeys = [
|
||||
...standardBooleanKeys,
|
||||
'seed',
|
||||
'specFilter',
|
||||
'extraItStackFrames',
|
||||
'extraDescribeStackFrames',
|
||||
'safariYieldStrategy'
|
||||
];
|
||||
Object.freeze(standardBooleanKeys);
|
||||
Object.freeze(allKeys);
|
||||
|
||||
@@ -28,6 +35,9 @@ describe('Configuration', function() {
|
||||
expect(subject.forbidDuplicateNames).toEqual(true);
|
||||
expect(subject.verboseDeprecations).toEqual(false);
|
||||
expect(subject.detectLateRejectionHandling).toEqual(false);
|
||||
expect(subject.extraItStackFrames).toEqual(0);
|
||||
expect(subject.extraDescribeStackFrames).toEqual(0);
|
||||
expect(subject.safariYieldStrategy).toEqual('count');
|
||||
});
|
||||
|
||||
describe('copy()', function() {
|
||||
@@ -109,5 +119,48 @@ describe('Configuration', function() {
|
||||
subject.update({ seed: null });
|
||||
expect(subject.seed).toBeNull();
|
||||
});
|
||||
|
||||
it('sets extraItStackFrames when not undefined', function() {
|
||||
const subject = new privateUnderTest.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 privateUnderTest.Configuration();
|
||||
|
||||
subject.update({ extraDescribeStackFrames: undefined });
|
||||
expect(subject.extraDescribeStackFrames).toEqual(0);
|
||||
|
||||
subject.update({ extraDescribeStackFrames: 100000 });
|
||||
expect(subject.extraDescribeStackFrames).toEqual(100000);
|
||||
});
|
||||
|
||||
it('sets safariYieldStrategy when valid', function() {
|
||||
const subject = new privateUnderTest.Configuration();
|
||||
|
||||
subject.update({ safariYieldStrategy: undefined });
|
||||
expect(subject.safariYieldStrategy).toEqual('count');
|
||||
|
||||
subject.update({ safariYieldStrategy: 'time' });
|
||||
expect(subject.safariYieldStrategy).toEqual('time');
|
||||
|
||||
subject.update({ safariYieldStrategy: 'count' });
|
||||
expect(subject.safariYieldStrategy).toEqual('count');
|
||||
});
|
||||
|
||||
it('rejcts invalid safariYieldStrategy values', function() {
|
||||
const subject = new privateUnderTest.Configuration();
|
||||
|
||||
expect(function() {
|
||||
subject.update({ safariYieldStrategy: 'thyme' });
|
||||
}).toThrowError(
|
||||
"Invalid safariYieldStrategy value. Valid values are 'count' and 'time'."
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// TODO: Fix these unit tests!
|
||||
describe('Env', function() {
|
||||
let env;
|
||||
beforeEach(function() {
|
||||
@@ -27,8 +26,6 @@ describe('Env', function() {
|
||||
|
||||
describe('#topSuite', function() {
|
||||
it('returns an object that describes the tree of suites and specs', function() {
|
||||
spyOn(env, 'deprecated');
|
||||
|
||||
env.it('a top level spec');
|
||||
env.describe('a suite', function() {
|
||||
env.it('a spec');
|
||||
@@ -95,7 +92,7 @@ describe('Env', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts its own current configureation', function() {
|
||||
it('accepts its own current configuration', function() {
|
||||
env.configure(env.configuration());
|
||||
});
|
||||
|
||||
@@ -124,7 +121,6 @@ describe('Env', function() {
|
||||
});
|
||||
|
||||
it('ignores configuration properties that are present but undefined', function() {
|
||||
spyOn(env, 'deprecated');
|
||||
const initialConfig = {
|
||||
random: true,
|
||||
seed: '123',
|
||||
@@ -198,6 +194,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 +319,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() {
|
||||
@@ -722,7 +760,6 @@ describe('Env', function() {
|
||||
|
||||
it("does not expose the suite as 'this'", function() {
|
||||
let suiteThis;
|
||||
spyOn(env, 'deprecated');
|
||||
|
||||
env.describe('a suite', function() {
|
||||
suiteThis = this;
|
||||
@@ -837,4 +874,44 @@ describe('Env', function() {
|
||||
}).toThrowError('Jasmine cannot be configured via Env in parallel mode');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Warning about monkey patching', function() {
|
||||
afterEach(function() {
|
||||
// deprecateMonkeyPatching() uses jasmine.getEnv(), not the env from
|
||||
// this suite. Clean it up so we don't leak event listeners.
|
||||
jasmineUnderTest.getEnv().cleanup_();
|
||||
privateUnderTest.currentEnv_ = null;
|
||||
});
|
||||
|
||||
const names = [
|
||||
'describe',
|
||||
'xdescribe',
|
||||
'fdescribe',
|
||||
'it',
|
||||
'xit',
|
||||
'fit',
|
||||
'beforeEach',
|
||||
'afterEach',
|
||||
'beforeAll',
|
||||
'afterAll'
|
||||
];
|
||||
|
||||
for (const name of names) {
|
||||
it(`warns if Env#${name} is monkey patched`, function() {
|
||||
spyOn(console, 'error');
|
||||
const patch = {};
|
||||
env[name] = patch;
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.error).toHaveBeenCalledOnceWith(
|
||||
jasmine.stringContaining('DEPRECATION: Monkey patching detected.')
|
||||
);
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.error).toHaveBeenCalledOnceWith(
|
||||
jasmine.stringContaining('EnvSpec.js')
|
||||
);
|
||||
expect(env[name]).toBe(patch);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -486,7 +486,7 @@ describe('QueueRunner', function() {
|
||||
errorListeners.pop();
|
||||
}
|
||||
},
|
||||
clearStack = jasmine.createSpy('clearStack'),
|
||||
clearStack = jasmine.createSpyObj('clearStack', ['clearStack']),
|
||||
onException = jasmine.createSpy('onException'),
|
||||
queueRunner = new privateUnderTest.QueueRunner({
|
||||
queueableFns: [queueableFn],
|
||||
@@ -498,10 +498,10 @@ describe('QueueRunner', function() {
|
||||
|
||||
queueRunner.execute();
|
||||
jasmine.clock().tick();
|
||||
expect(clearStack).toHaveBeenCalled();
|
||||
expect(clearStack.clearStack).toHaveBeenCalled();
|
||||
expect(errorListeners.length).toEqual(1);
|
||||
errorListeners[0](error);
|
||||
clearStack.calls.argsFor(0)[0]();
|
||||
clearStack.clearStack.calls.argsFor(0)[0]();
|
||||
expect(onException).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
@@ -908,22 +908,22 @@ describe('QueueRunner', function() {
|
||||
},
|
||||
afterFn = { fn: jasmine.createSpy('afterFn') },
|
||||
completeCallback = jasmine.createSpy('completeCallback'),
|
||||
clearStack = jasmine.createSpy('clearStack'),
|
||||
clearStack = jasmine.createSpyObj('clearStack', ['clearStack']),
|
||||
queueRunner = new privateUnderTest.QueueRunner({
|
||||
queueableFns: [asyncFn, afterFn],
|
||||
clearStack: clearStack,
|
||||
onComplete: completeCallback
|
||||
});
|
||||
|
||||
clearStack.and.callFake(function(fn) {
|
||||
clearStack.clearStack.and.callFake(function(fn) {
|
||||
fn();
|
||||
});
|
||||
|
||||
queueRunner.execute();
|
||||
jasmine.clock().tick();
|
||||
expect(afterFn.fn).toHaveBeenCalled();
|
||||
expect(clearStack).toHaveBeenCalled();
|
||||
clearStack.calls.argsFor(0)[0]();
|
||||
expect(clearStack.clearStack).toHaveBeenCalled();
|
||||
clearStack.clearStack.calls.argsFor(0)[0]();
|
||||
expect(completeCallback).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -87,7 +87,7 @@ describe('Runner', function() {
|
||||
function arrayNotContaining(item) {
|
||||
return {
|
||||
asymmetricMatch(other, matchersUtil) {
|
||||
if (!jasmine.private.isArray(other)) {
|
||||
if (!Array.isArray(other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,26 +103,14 @@ describe('Spec', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('throws if the key is not structured-cloneable', function() {
|
||||
it('throws if the key is not a string', function() {
|
||||
const spec = new privateUnderTest.Spec({
|
||||
queueableFn: { fn: () => {} }
|
||||
});
|
||||
|
||||
expect(function() {
|
||||
spec.setSpecProperty(new Promise(() => {}), '');
|
||||
}).toThrowError("Key can't be cloned");
|
||||
});
|
||||
|
||||
it('throws if the key is not JSON-serializable', function() {
|
||||
const spec = new privateUnderTest.Spec({
|
||||
queueableFn: { fn: () => {} }
|
||||
});
|
||||
|
||||
expect(function() {
|
||||
const k = {};
|
||||
k.self = k;
|
||||
spec.setSpecProperty(k, '');
|
||||
}).toThrowError("Key can't be cloned");
|
||||
spec.setSpecProperty({}, '');
|
||||
}).toThrowError('Key must be a string');
|
||||
});
|
||||
|
||||
it('throws if the value is not structured-cloneable', function() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
describe('ClearStack', function() {
|
||||
describe('StackClearer', function() {
|
||||
it('works in an integrationy way', function(done) {
|
||||
const clearStack = privateUnderTest.getClearStack(
|
||||
const { clearStack } = privateUnderTest.getStackClearer(
|
||||
jasmineUnderTest.getGlobal()
|
||||
);
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('ClearStack', function() {
|
||||
queueMicrotask
|
||||
};
|
||||
|
||||
const clearStack = privateUnderTest.getClearStack(global);
|
||||
const { clearStack } = privateUnderTest.getStackClearer(global);
|
||||
|
||||
for (let i = 0; i < 9; i++) {
|
||||
clearStack(function() {});
|
||||
@@ -73,17 +73,6 @@ describe('ClearStack', function() {
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
describe('when MessageChannel is unavailable', function() {
|
||||
usesQueueMicrotaskWithSetTimeout(function() {
|
||||
return {
|
||||
navigator: {
|
||||
userAgent: 'CERN-LineMode/2.15 libwww/2.17b3',
|
||||
MessageChannel: undefined
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('in Node', function() {
|
||||
@@ -104,7 +93,7 @@ describe('ClearStack', function() {
|
||||
...makeGlobal(),
|
||||
MessageChannel: fakeMessageChannel
|
||||
};
|
||||
const clearStack = privateUnderTest.getClearStack(global);
|
||||
const { clearStack } = privateUnderTest.getStackClearer(global);
|
||||
let called = false;
|
||||
|
||||
clearStack(function() {
|
||||
@@ -125,7 +114,7 @@ describe('ClearStack', function() {
|
||||
return fakeChannel;
|
||||
}
|
||||
};
|
||||
const clearStack = privateUnderTest.getClearStack(global);
|
||||
const { clearStack } = privateUnderTest.getStackClearer(global);
|
||||
|
||||
for (let i = 0; i < 9; i++) {
|
||||
clearStack(function() {});
|
||||
@@ -150,7 +139,7 @@ describe('ClearStack', function() {
|
||||
setTimeout,
|
||||
MessageChannel: fakeMessageChannel
|
||||
};
|
||||
const clearStack = privateUnderTest.getClearStack(global);
|
||||
const { clearStack } = privateUnderTest.getStackClearer(global);
|
||||
const fn = jasmine.createSpy('second clearStack function');
|
||||
|
||||
clearStack(function() {
|
||||
@@ -170,7 +159,7 @@ describe('ClearStack', function() {
|
||||
fn();
|
||||
}
|
||||
};
|
||||
const clearStack = privateUnderTest.getClearStack(global);
|
||||
const { clearStack } = privateUnderTest.getStackClearer(global);
|
||||
let called = false;
|
||||
|
||||
clearStack(function() {
|
||||
@@ -180,30 +169,82 @@ describe('ClearStack', function() {
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
|
||||
it('uses setTimeout instead of queueMicrotask every 10 calls to make sure we release the CPU', function() {
|
||||
const queueMicrotask = jasmine.createSpy('queueMicrotask');
|
||||
const setTimeout = jasmine.createSpy('setTimeout');
|
||||
const global = {
|
||||
...makeGlobal(),
|
||||
queueMicrotask,
|
||||
setTimeout
|
||||
};
|
||||
const clearStack = privateUnderTest.getClearStack(global);
|
||||
function hasSetTimeoutBehavior(configure) {
|
||||
it('uses setTimeout instead of queueMicrotask every 10 calls', function() {
|
||||
const queueMicrotask = jasmine.createSpy('queueMicrotask');
|
||||
const setTimeout = jasmine.createSpy('setTimeout');
|
||||
const global = {
|
||||
...makeGlobal(),
|
||||
queueMicrotask,
|
||||
setTimeout
|
||||
};
|
||||
const stackClearer = privateUnderTest.getStackClearer(global);
|
||||
|
||||
for (let i = 0; i < 9; i++) {
|
||||
clearStack(function() {});
|
||||
}
|
||||
if (configure) {
|
||||
configure(stackClearer);
|
||||
}
|
||||
|
||||
expect(queueMicrotask).toHaveBeenCalled();
|
||||
expect(setTimeout).not.toHaveBeenCalled();
|
||||
for (let i = 0; i < 9; i++) {
|
||||
stackClearer.clearStack(function() {});
|
||||
}
|
||||
|
||||
clearStack(function() {});
|
||||
expect(queueMicrotask).toHaveBeenCalledTimes(9);
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
expect(queueMicrotask).toHaveBeenCalled();
|
||||
expect(setTimeout).not.toHaveBeenCalled();
|
||||
|
||||
clearStack(function() {});
|
||||
expect(queueMicrotask).toHaveBeenCalledTimes(10);
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
stackClearer.clearStack(function() {});
|
||||
expect(queueMicrotask).toHaveBeenCalledTimes(9);
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
|
||||
stackClearer.clearStack(function() {});
|
||||
expect(queueMicrotask).toHaveBeenCalledTimes(10);
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
}
|
||||
|
||||
hasSetTimeoutBehavior();
|
||||
|
||||
describe('With yield strategy explicitly set to count', function() {
|
||||
hasSetTimeoutBehavior(function(stackClearer) {
|
||||
stackClearer.setSafariYieldStrategy('count');
|
||||
});
|
||||
});
|
||||
|
||||
describe('With yield strategy set to time', function() {
|
||||
beforeEach(function() {
|
||||
jasmine.clock().install();
|
||||
jasmine.clock().mockDate();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
jasmine.clock().uninstall();
|
||||
});
|
||||
|
||||
it('uses setTimeout instead of queueMicrotask every 25 milliseconds', function() {
|
||||
const queueMicrotask = jasmine.createSpy('queueMicrotask');
|
||||
const setTimeout = jasmine.createSpy('setTimeout');
|
||||
const global = {
|
||||
...makeGlobal(),
|
||||
queueMicrotask,
|
||||
setTimeout
|
||||
};
|
||||
const stackClearer = privateUnderTest.getStackClearer(global);
|
||||
stackClearer.setSafariYieldStrategy('time');
|
||||
|
||||
// 10+ counts should not trigger a setTimeout if they happen fast enough
|
||||
jasmine.clock().tick(24);
|
||||
for (let i = 0; i < 11; i++) {
|
||||
stackClearer.clearStack(function() {});
|
||||
}
|
||||
|
||||
expect(queueMicrotask).toHaveBeenCalled();
|
||||
expect(setTimeout).not.toHaveBeenCalled();
|
||||
|
||||
queueMicrotask.calls.reset();
|
||||
jasmine.clock().tick(1);
|
||||
stackClearer.clearStack(function() {});
|
||||
expect(queueMicrotask).not.toHaveBeenCalled();
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -215,7 +256,7 @@ describe('ClearStack', function() {
|
||||
fn();
|
||||
}
|
||||
};
|
||||
const clearStack = privateUnderTest.getClearStack(global);
|
||||
const { clearStack } = privateUnderTest.getStackClearer(global);
|
||||
let called = false;
|
||||
|
||||
clearStack(function() {
|
||||
@@ -233,7 +274,7 @@ describe('ClearStack', function() {
|
||||
queueMicrotask,
|
||||
setTimeout
|
||||
};
|
||||
const clearStack = privateUnderTest.getClearStack(global);
|
||||
const { clearStack } = privateUnderTest.getStackClearer(global);
|
||||
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
@@ -409,12 +409,12 @@ describe('Suite', function() {
|
||||
});
|
||||
|
||||
describe('#setSuiteProperty', function() {
|
||||
it('throws if the key is not structured-cloneable', function() {
|
||||
it('throws if the key is not a string', function() {
|
||||
const suite = new privateUnderTest.Suite({});
|
||||
|
||||
expect(function() {
|
||||
suite.setSuiteProperty(new Promise(() => {}), '');
|
||||
}).toThrowError("Key can't be cloned");
|
||||
suite.setSuiteProperty({}, '');
|
||||
}).toThrowError('Key must be a string');
|
||||
});
|
||||
|
||||
it('throws if the value is not structured-cloneable', function() {
|
||||
@@ -424,6 +424,16 @@ describe('Suite', function() {
|
||||
suite.setSuiteProperty('k', new Promise(() => {}));
|
||||
}).toThrowError("Value can't be cloned");
|
||||
});
|
||||
|
||||
it('throws if the value is not JSON-serializable', function() {
|
||||
const suite = new privateUnderTest.Suite({});
|
||||
|
||||
expect(function() {
|
||||
const v = {};
|
||||
v.self = v;
|
||||
suite.setSuiteProperty('k', v);
|
||||
}).toThrowError("Value can't be cloned");
|
||||
});
|
||||
});
|
||||
|
||||
describe('#startedEvent', function() {
|
||||
|
||||
@@ -246,4 +246,53 @@ describe('TreeProcessor', function() {
|
||||
{ spec: leaf1 }
|
||||
]);
|
||||
});
|
||||
|
||||
describe("The returned ExecutionTree's numExcludedSpecs method", function() {
|
||||
it('counts filtered-out specs', function() {
|
||||
const included = new Leaf();
|
||||
const excluded1 = new Leaf();
|
||||
const excluded2 = new Leaf();
|
||||
const excluded3 = new Leaf();
|
||||
const topSuite = new Node({
|
||||
children: [
|
||||
excluded1,
|
||||
new Node({
|
||||
children: [included, excluded2, new Node({ children: [excluded3] })]
|
||||
})
|
||||
]
|
||||
});
|
||||
const processor = new privateUnderTest.TreeProcessor({
|
||||
tree: topSuite,
|
||||
runnableIds: [topSuite.id],
|
||||
excludeNode(node) {
|
||||
return node.id !== included.id;
|
||||
}
|
||||
});
|
||||
|
||||
const executionTree = processor.processTree();
|
||||
expect(executionTree.numExcludedSpecs()).toEqual(3);
|
||||
});
|
||||
|
||||
it("counts specs that aren't in or descendants of runnableIds", function() {
|
||||
const includedSuite = new Node({
|
||||
children: [new Node({ children: [new Leaf()] }), new Leaf()]
|
||||
});
|
||||
const directlyIncludedSpec = new Leaf();
|
||||
const topSuite = new Node({
|
||||
children: [
|
||||
includedSuite,
|
||||
new Node({
|
||||
children: [new Leaf(), directlyIncludedSpec]
|
||||
})
|
||||
]
|
||||
});
|
||||
const processor = new privateUnderTest.TreeProcessor({
|
||||
tree: topSuite,
|
||||
runnableIds: [includedSuite.id, directlyIncludedSpec.id]
|
||||
});
|
||||
|
||||
const executionTree = processor.processTree();
|
||||
expect(executionTree.numExcludedSpecs()).toEqual(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,20 +1,4 @@
|
||||
describe('util', function() {
|
||||
describe('isArray', function() {
|
||||
it('should return true if the argument is an array', function() {
|
||||
expect(privateUnderTest.isArray([])).toBe(true);
|
||||
expect(privateUnderTest.isArray(['a'])).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the argument is not an array', function() {
|
||||
expect(privateUnderTest.isArray(undefined)).toBe(false);
|
||||
expect(privateUnderTest.isArray({})).toBe(false);
|
||||
expect(privateUnderTest.isArray(function() {})).toBe(false);
|
||||
expect(privateUnderTest.isArray('foo')).toBe(false);
|
||||
expect(privateUnderTest.isArray(5)).toBe(false);
|
||||
expect(privateUnderTest.isArray(null)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isObject', function() {
|
||||
it('should return true if the argument is an object', function() {
|
||||
expect(privateUnderTest.isObject({})).toBe(true);
|
||||
|
||||
63
spec/core/asymmetric_equality/AllOfSpec.js
Normal file
63
spec/core/asymmetric_equality/AllOfSpec.js
Normal file
@@ -0,0 +1,63 @@
|
||||
describe('AllOf', function() {
|
||||
it('matches a single value', function() {
|
||||
const matchersUtil = new privateUnderTest.MatchersUtil();
|
||||
const allOf = new privateUnderTest.AllOf('foo');
|
||||
|
||||
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeTrue();
|
||||
});
|
||||
|
||||
it('matches a single matcher', function() {
|
||||
const matchersUtil = new privateUnderTest.MatchersUtil();
|
||||
const allOf = new privateUnderTest.AllOf(
|
||||
new privateUnderTest.StringContaining('oo')
|
||||
);
|
||||
|
||||
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeTrue();
|
||||
});
|
||||
|
||||
it('matches multiple matchers', function() {
|
||||
const matchersUtil = new privateUnderTest.MatchersUtil();
|
||||
const allOf = new privateUnderTest.AllOf(
|
||||
new privateUnderTest.StringContaining('o'),
|
||||
new privateUnderTest.StringContaining('f')
|
||||
);
|
||||
|
||||
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeTrue();
|
||||
});
|
||||
|
||||
it('does not match when value does not match', function() {
|
||||
const matchersUtil = new privateUnderTest.MatchersUtil();
|
||||
const allOf = new privateUnderTest.AllOf('bar');
|
||||
|
||||
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeFalse();
|
||||
});
|
||||
|
||||
it('does not match when any matchers fail', function() {
|
||||
const matchersUtil = new privateUnderTest.MatchersUtil();
|
||||
const allOf = new privateUnderTest.AllOf(
|
||||
new privateUnderTest.StringContaining('o'),
|
||||
new privateUnderTest.StringContaining('x')
|
||||
);
|
||||
|
||||
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeFalse();
|
||||
});
|
||||
|
||||
it('jasmineToStrings itself', function() {
|
||||
const matcher = new privateUnderTest.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 privateUnderTest.AllOf();
|
||||
}).toThrowError(
|
||||
TypeError,
|
||||
'jasmine.allOf() expects at least one argument to be passed.'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -170,8 +170,15 @@ describe('base helpers', function() {
|
||||
});
|
||||
|
||||
describe('debugLog', function() {
|
||||
beforeEach(function() {
|
||||
privateUnderTest.currentEnv_ = jasmine.createSpyObj('env', ['debugLog']);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
privateUnderTest.currentEnv_ = null;
|
||||
});
|
||||
|
||||
it("forwards to the current env's debugLog function", function() {
|
||||
spyOn(jasmineUnderTest.getEnv(), 'debugLog');
|
||||
jasmineUnderTest.debugLog('a message');
|
||||
expect(jasmineUnderTest.getEnv().debugLog).toHaveBeenCalledWith(
|
||||
'a message'
|
||||
|
||||
@@ -1114,8 +1114,17 @@ describe('Env integration', function() {
|
||||
global: {
|
||||
setTimeout: globalSetTimeout,
|
||||
clearTimeout: clearTimeout,
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
},
|
||||
// Enough Node globals to make getStackClearer() return the microtask
|
||||
// implementation, which is the easiest to mock
|
||||
process: {
|
||||
versions: {
|
||||
node: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1179,7 +1188,7 @@ describe('Env integration', function() {
|
||||
global: {
|
||||
setTimeout: function(cb, t) {
|
||||
const stack = new Error().stack;
|
||||
if (stack.indexOf('ClearStack') >= 0) {
|
||||
if (stack.indexOf('clearStack') >= 0) {
|
||||
return realSetTimeout(cb, t);
|
||||
} else {
|
||||
return setTimeout(cb, t);
|
||||
@@ -1188,8 +1197,17 @@ describe('Env integration', function() {
|
||||
clearTimeout: clearTimeout,
|
||||
setInterval: setInterval,
|
||||
clearInterval: clearInterval,
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
},
|
||||
// Enough Node globals to make getStackClearer() return the microtask
|
||||
// implementation, which is the easiest to mock
|
||||
process: {
|
||||
versions: {
|
||||
node: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1539,6 +1557,7 @@ describe('Env integration', function() {
|
||||
|
||||
expect(reporter.jasmineStarted).toHaveBeenCalledWith({
|
||||
totalSpecsDefined: 1,
|
||||
numExcludedSpecs: 0,
|
||||
order: { random: true, seed: jasmine.any(String) },
|
||||
parallel: false
|
||||
});
|
||||
@@ -1574,6 +1593,7 @@ describe('Env integration', function() {
|
||||
|
||||
expect(reporter.jasmineStarted).toHaveBeenCalledWith({
|
||||
totalSpecsDefined: 1,
|
||||
numExcludedSpecs: 0,
|
||||
order: { random: true, seed: jasmine.any(String) },
|
||||
parallel: false
|
||||
});
|
||||
@@ -1630,6 +1650,7 @@ describe('Env integration', function() {
|
||||
|
||||
expect(reporter.jasmineStarted).toHaveBeenCalledWith({
|
||||
totalSpecsDefined: 6,
|
||||
numExcludedSpecs: 3,
|
||||
order: { random: false },
|
||||
parallel: false
|
||||
});
|
||||
@@ -2043,6 +2064,7 @@ describe('Env integration', function() {
|
||||
|
||||
expect(reporter.jasmineStarted).toHaveBeenCalledWith({
|
||||
totalSpecsDefined: 1,
|
||||
numExcludedSpecs: 1,
|
||||
order: { random: true, seed: jasmine.any(String) },
|
||||
parallel: false
|
||||
});
|
||||
@@ -2274,6 +2296,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;
|
||||
@@ -2775,12 +2825,18 @@ describe('Env integration', function() {
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
},
|
||||
// Enough Node globals to make getStackClearer() return the microtask
|
||||
// implementation, which is the easiest to mock
|
||||
process: {
|
||||
versions: {
|
||||
node: ''
|
||||
}
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
const reporter = jasmine.createSpyObj('reporter', [
|
||||
'jasmineDone',
|
||||
'suiteDone',
|
||||
@@ -3228,13 +3284,11 @@ describe('Env integration', function() {
|
||||
});
|
||||
|
||||
it('is resolved after the stack is cleared', function(done) {
|
||||
const realClearStack = privateUnderTest.getClearStack(
|
||||
jasmineUnderTest.getGlobal()
|
||||
),
|
||||
clearStackSpy = jasmine
|
||||
.createSpy('clearStack')
|
||||
.and.callFake(realClearStack);
|
||||
spyOn(privateUnderTest, 'getClearStack').and.returnValue(clearStackSpy);
|
||||
const stackClearer = privateUnderTest.getStackClearer(
|
||||
jasmineUnderTest.getGlobal()
|
||||
);
|
||||
spyOn(stackClearer, 'clearStack').and.callThrough();
|
||||
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(stackClearer);
|
||||
|
||||
// Create a new env that has the clearStack defined above
|
||||
env.cleanup_();
|
||||
@@ -3245,10 +3299,10 @@ describe('Env integration', function() {
|
||||
});
|
||||
|
||||
env.execute(null).then(function() {
|
||||
expect(clearStackSpy).toHaveBeenCalled(); // (many times)
|
||||
clearStackSpy.calls.reset();
|
||||
expect(stackClearer.clearStack).toHaveBeenCalled(); // (many times)
|
||||
stackClearer.clearStack.calls.reset();
|
||||
setTimeout(function() {
|
||||
expect(clearStackSpy).not.toHaveBeenCalled();
|
||||
expect(stackClearer.clearStack).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -3337,13 +3391,11 @@ describe('Env integration', function() {
|
||||
});
|
||||
|
||||
it('is called after the stack is cleared', async function() {
|
||||
const realClearStack = privateUnderTest.getClearStack(
|
||||
jasmineUnderTest.getGlobal()
|
||||
),
|
||||
clearStackSpy = jasmine
|
||||
.createSpy('clearStack')
|
||||
.and.callFake(realClearStack);
|
||||
spyOn(privateUnderTest, 'getClearStack').and.returnValue(clearStackSpy);
|
||||
const stackClearer = privateUnderTest.getStackClearer(
|
||||
jasmineUnderTest.getGlobal()
|
||||
);
|
||||
spyOn(stackClearer, 'clearStack').and.callThrough();
|
||||
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(stackClearer);
|
||||
|
||||
// Create a new env that has the clearStack defined above
|
||||
env.cleanup_();
|
||||
@@ -3355,12 +3407,12 @@ describe('Env integration', function() {
|
||||
|
||||
await env.execute();
|
||||
|
||||
expect(clearStackSpy).toHaveBeenCalled(); // (many times)
|
||||
clearStackSpy.calls.reset();
|
||||
expect(stackClearer.clearStack).toHaveBeenCalled(); // (many times)
|
||||
stackClearer.clearStack.calls.reset();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve));
|
||||
|
||||
expect(clearStackSpy).not.toHaveBeenCalled();
|
||||
expect(stackClearer.clearStack).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('is called after QueueRunner timeouts are cleared', async function() {
|
||||
@@ -3794,6 +3846,34 @@ describe('Env integration', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('pp', function() {
|
||||
it("pretty-prints using the current runable's custom object formatters", async function() {
|
||||
env.it('a spec', function() {
|
||||
env.addCustomObjectFormatter(function(x) {
|
||||
if (x === 1) {
|
||||
return 'hi!';
|
||||
}
|
||||
});
|
||||
env.expect(env.pp(1)).toEqual('hi!');
|
||||
});
|
||||
|
||||
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
|
||||
env.addReporter(reporter);
|
||||
|
||||
await env.execute();
|
||||
|
||||
expect(reporter.specDone).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
failedExpectations: []
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('works when there is no current runable', function() {
|
||||
expect(env.pp({ some: 'thing' })).toEqual("Object({ some: 'thing' })");
|
||||
});
|
||||
});
|
||||
|
||||
it('forbids duplicates when forbidDuplicateNames is true', function() {
|
||||
env.configure({ forbidDuplicateNames: true });
|
||||
env.it('a spec');
|
||||
|
||||
@@ -11,8 +11,8 @@ describe('Global error handling (integration)', function() {
|
||||
env.cleanup_();
|
||||
});
|
||||
|
||||
it('reports errors that occur during loading', async function() {
|
||||
const global = {
|
||||
function mockGlobal() {
|
||||
return {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
@@ -23,12 +23,21 @@ describe('Global error handling (integration)', function() {
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
},
|
||||
onerror: function() {}
|
||||
onerror: function() {},
|
||||
// Enough Node globals to make getStackClearer() return the microtask
|
||||
// implementation, which is the easiest to mock
|
||||
process: {
|
||||
versions: {
|
||||
node: ''
|
||||
}
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
}
|
||||
|
||||
it('reports errors that occur during loading', async function() {
|
||||
const global = mockGlobal();
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
const reporter = jasmine.createSpyObj('reporter', [
|
||||
'jasmineDone',
|
||||
'suiteDone',
|
||||
@@ -70,21 +79,9 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
describe('If suppressLoadErrors: true was passed', function() {
|
||||
it('does not install a global error handler during loading', async function() {
|
||||
const global = mockGlobal();
|
||||
const originalOnerror = jasmine.createSpy('original onerror');
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
},
|
||||
onerror: originalOnerror
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
global.onerror = originalOnerror;
|
||||
const globalErrors = new privateUnderTest.GlobalErrors(global);
|
||||
const onerror = jasmine.createSpy('onerror');
|
||||
globalErrors.pushListener(onerror);
|
||||
@@ -92,6 +89,7 @@ describe('Global error handling (integration)', function() {
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env({
|
||||
suppressLoadErrors: true,
|
||||
global,
|
||||
GlobalErrors: function() {
|
||||
return globalErrors;
|
||||
}
|
||||
@@ -115,21 +113,9 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
describe('Handling unhandled exceptions', function() {
|
||||
it('routes unhandled exceptions to the running spec', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
const global = mockGlobal();
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
const reporter = jasmine.createSpyObj('fakeReporter', [
|
||||
'specDone',
|
||||
'suiteDone'
|
||||
@@ -156,24 +142,12 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
describe('When the most recently running spec has reported specDone', function() {
|
||||
it('routes unhandled exceptions to an ancestor suite', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn) {
|
||||
clearTimeout(fn);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
|
||||
const realClearStack = privateUnderTest.getClearStack(global);
|
||||
const global = mockGlobal();
|
||||
const stackClearer = privateUnderTest.getStackClearer(global);
|
||||
const realClearStack = stackClearer.clearStack;
|
||||
const clearStackCallbacks = {};
|
||||
let clearStackCallCount = 0;
|
||||
spyOn(privateUnderTest, 'getClearStack').and.returnValue(function(fn) {
|
||||
spyOn(stackClearer, 'clearStack').and.callFake(function(fn) {
|
||||
clearStackCallCount++;
|
||||
|
||||
if (clearStackCallbacks[clearStackCallCount]) {
|
||||
@@ -182,9 +156,12 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
realClearStack(fn);
|
||||
});
|
||||
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(
|
||||
stackClearer
|
||||
);
|
||||
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
|
||||
let suiteErrors = [];
|
||||
env.addReporter({
|
||||
@@ -220,21 +197,9 @@ describe('Global error handling (integration)', function() {
|
||||
});
|
||||
|
||||
it('routes unhandled exceptions to the running suite', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
const global = mockGlobal();
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
const reporter = jasmine.createSpyObj('fakeReporter', [
|
||||
'specDone',
|
||||
'suiteDone'
|
||||
@@ -273,24 +238,12 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
describe('When the most recently suite has reported suiteDone', function() {
|
||||
it('routes unhandled exceptions to an ancestor suite', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
|
||||
const realClearStack = privateUnderTest.getClearStack(global);
|
||||
const global = mockGlobal();
|
||||
const stackClearer = privateUnderTest.getStackClearer(global);
|
||||
const realClearStack = stackClearer.clearStack;
|
||||
const clearStackCallbacks = {};
|
||||
let clearStackCallCount = 0;
|
||||
spyOn(privateUnderTest, 'getClearStack').and.returnValue(function(fn) {
|
||||
spyOn(stackClearer, 'clearStack').and.callFake(function(fn) {
|
||||
clearStackCallCount++;
|
||||
|
||||
if (clearStackCallbacks[clearStackCallCount]) {
|
||||
@@ -299,9 +252,12 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
realClearStack(fn);
|
||||
});
|
||||
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(
|
||||
stackClearer
|
||||
);
|
||||
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
|
||||
let suiteErrors = [];
|
||||
env.addReporter({
|
||||
@@ -341,21 +297,9 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
describe('When the env has started reporting jasmineDone', function() {
|
||||
it('logs the error to the console', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
const global = mockGlobal();
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
|
||||
spyOn(console, 'error');
|
||||
|
||||
@@ -384,26 +328,14 @@ describe('Global error handling (integration)', function() {
|
||||
});
|
||||
|
||||
it('routes all errors that occur during stack clearing somewhere', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn) {
|
||||
clearTimeout(fn);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
|
||||
const realClearStack = privateUnderTest.getClearStack(global);
|
||||
const global = mockGlobal();
|
||||
const stackClearer = privateUnderTest.getStackClearer(global);
|
||||
const realClearStack = stackClearer.clearStack;
|
||||
let clearStackCallCount = 0;
|
||||
let jasmineDone = false;
|
||||
const expectedErrors = [];
|
||||
const expectedErrorsAfterJasmineDone = [];
|
||||
spyOn(privateUnderTest, 'getClearStack').and.returnValue(function(fn) {
|
||||
spyOn(stackClearer, 'clearStack').and.callFake(function(fn) {
|
||||
clearStackCallCount++;
|
||||
const msg = `Error in clearStack #${clearStackCallCount}`;
|
||||
|
||||
@@ -416,10 +348,11 @@ describe('Global error handling (integration)', function() {
|
||||
dispatchErrorEvent(global, 'error', { error: msg });
|
||||
realClearStack(fn);
|
||||
});
|
||||
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(stackClearer);
|
||||
spyOn(console, 'error');
|
||||
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
|
||||
const receivedErrors = [];
|
||||
function logErrors(event) {
|
||||
@@ -459,21 +392,9 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
describe('Handling unhandled promise rejections', function() {
|
||||
it('routes unhandled promise rejections to the running spec', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
const global = mockGlobal();
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
const reporter = jasmine.createSpyObj('fakeReporter', [
|
||||
'specDone',
|
||||
'suiteDone'
|
||||
@@ -502,24 +423,12 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
describe('When the most recently running spec has reported specDone', function() {
|
||||
it('routes unhandled promise rejections to an ancestor suite', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn) {
|
||||
clearTimeout(fn);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
|
||||
const realClearStack = privateUnderTest.getClearStack(global);
|
||||
const global = mockGlobal();
|
||||
const stackClearer = privateUnderTest.getStackClearer(global);
|
||||
const realClearStack = stackClearer.clearStack;
|
||||
const clearStackCallbacks = {};
|
||||
let clearStackCallCount = 0;
|
||||
spyOn(privateUnderTest, 'getClearStack').and.returnValue(function(fn) {
|
||||
spyOn(stackClearer, 'clearStack').and.callFake(function(fn) {
|
||||
clearStackCallCount++;
|
||||
|
||||
if (clearStackCallbacks[clearStackCallCount]) {
|
||||
@@ -528,9 +437,12 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
realClearStack(fn);
|
||||
});
|
||||
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(
|
||||
stackClearer
|
||||
);
|
||||
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
|
||||
let suiteErrors = [];
|
||||
env.addReporter({
|
||||
@@ -566,21 +478,9 @@ describe('Global error handling (integration)', function() {
|
||||
});
|
||||
|
||||
it('routes unhandled promise rejections to the running suite', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
const global = mockGlobal();
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
const reporter = jasmine.createSpyObj('fakeReporter', [
|
||||
'specDone',
|
||||
'suiteDone'
|
||||
@@ -621,24 +521,12 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
describe('When the most recently suite has reported suiteDone', function() {
|
||||
it('routes unhandled promise rejections to an ancestor suite', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
|
||||
const realClearStack = privateUnderTest.getClearStack(global);
|
||||
const global = mockGlobal();
|
||||
const stackClearer = privateUnderTest.getStackClearer(global);
|
||||
const realClearStack = stackClearer.clearStack;
|
||||
const clearStackCallbacks = {};
|
||||
let clearStackCallCount = 0;
|
||||
spyOn(privateUnderTest, 'getClearStack').and.returnValue(function(fn) {
|
||||
spyOn(stackClearer, 'clearStack').and.callFake(function(fn) {
|
||||
clearStackCallCount++;
|
||||
|
||||
if (clearStackCallbacks[clearStackCallCount]) {
|
||||
@@ -647,9 +535,12 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
realClearStack(fn);
|
||||
});
|
||||
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(
|
||||
stackClearer
|
||||
);
|
||||
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
|
||||
let suiteErrors = [];
|
||||
env.addReporter({
|
||||
@@ -689,21 +580,9 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
describe('When the env has started reporting jasmineDone', function() {
|
||||
it('logs the rejection to the console', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
const global = mockGlobal();
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
|
||||
spyOn(console, 'error');
|
||||
|
||||
@@ -734,26 +613,14 @@ describe('Global error handling (integration)', function() {
|
||||
});
|
||||
|
||||
it('routes all unhandled promise rejections that occur during stack clearing somewhere', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn) {
|
||||
clearTimeout(fn);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
|
||||
const realClearStack = privateUnderTest.getClearStack(global);
|
||||
const global = mockGlobal();
|
||||
const stackClearer = privateUnderTest.getStackClearer(global);
|
||||
const realClearStack = stackClearer.clearStack;
|
||||
let clearStackCallCount = 0;
|
||||
let jasmineDone = false;
|
||||
const expectedErrors = [];
|
||||
const expectedErrorsAfterJasmineDone = [];
|
||||
spyOn(privateUnderTest, 'getClearStack').and.returnValue(function(fn) {
|
||||
spyOn(stackClearer, 'clearStack').and.callFake(function(fn) {
|
||||
clearStackCallCount++;
|
||||
const reason = `Error in clearStack #${clearStackCallCount}`;
|
||||
const expectedMsg = `Unhandled promise rejection: ${reason} thrown`;
|
||||
@@ -767,10 +634,11 @@ describe('Global error handling (integration)', function() {
|
||||
dispatchErrorEvent(global, 'unhandledrejection', { reason });
|
||||
realClearStack(fn);
|
||||
});
|
||||
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(stackClearer);
|
||||
spyOn(console, 'error');
|
||||
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
|
||||
const receivedErrors = [];
|
||||
function logErrors(event) {
|
||||
@@ -819,21 +687,9 @@ describe('Global error handling (integration)', function() {
|
||||
let global, reporter;
|
||||
|
||||
beforeEach(function() {
|
||||
global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
global = mockGlobal();
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
env.configure({ detectLateRejectionHandling: true });
|
||||
|
||||
reporter = jasmine.createSpyObj('fakeReporter', [
|
||||
@@ -966,21 +822,8 @@ describe('Global error handling (integration)', function() {
|
||||
|
||||
describe("When the unhandled rejection event doesn't have a promise", function() {
|
||||
it('reports the rejection', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
const global = mockGlobal();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
env.configure({ detectLateRejectionHandling: true });
|
||||
const reporter = jasmine.createSpyObj('fakeReporter', [
|
||||
'specDone',
|
||||
@@ -1012,21 +855,9 @@ describe('Global error handling (integration)', function() {
|
||||
});
|
||||
|
||||
it('works when the suite is run multiple times', async function() {
|
||||
const global = {
|
||||
...browserEventMethods(),
|
||||
setTimeout: function(fn, delay) {
|
||||
return setTimeout(fn, delay);
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
const global = mockGlobal();
|
||||
env.cleanup_();
|
||||
env = new privateUnderTest.Env();
|
||||
env = new privateUnderTest.Env({ global });
|
||||
env.configure({ autoCleanClosures: false });
|
||||
const reporter = jasmine.createSpyObj('fakeReporter', ['specDone']);
|
||||
|
||||
|
||||
@@ -13,7 +13,72 @@ describe('The jasmine namespace', function() {
|
||||
expect(setDifference(actualKeys, expectedKeys())).toEqual(new Set());
|
||||
});
|
||||
|
||||
function expectedKeys() {
|
||||
describe('Warning about monkey patching', function() {
|
||||
beforeEach(function() {
|
||||
spyOn(console, 'error');
|
||||
});
|
||||
|
||||
for (const key of expectedKeys(false)) {
|
||||
if (!key.startsWith('MAX_') && key !== 'private' && key !== 'getEnv') {
|
||||
describe(`jasmine.${key}`, function() {
|
||||
let orig;
|
||||
|
||||
beforeEach(function() {
|
||||
orig = jasmineUnderTest[key];
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
jasmineUnderTest[key] = orig;
|
||||
});
|
||||
|
||||
it('warns if monkey patched', function() {
|
||||
const patch = {};
|
||||
jasmineUnderTest[key] = patch;
|
||||
|
||||
verifyDeprecation();
|
||||
expect(jasmineUnderTest[key]).toBe(patch);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// These specs rely on jasmineRequire being exposed. That only happens
|
||||
// in browsers.
|
||||
if (typeof document !== 'undefined') {
|
||||
const statics = ['addMatchers', 'clock', 'createSpyObj'];
|
||||
|
||||
for (const name of statics) {
|
||||
describe(`jasmine.${name}`, function() {
|
||||
let bootedCore, env, orig;
|
||||
|
||||
beforeEach(function() {
|
||||
bootedCore = jasmineRequire.core(jasmineRequire);
|
||||
env = bootedCore.getEnv();
|
||||
jasmineRequire.interface(bootedCore, env);
|
||||
orig = bootedCore[name];
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
bootedCore[name] = orig;
|
||||
env.cleanup_();
|
||||
});
|
||||
|
||||
it(`warns if jasmine.${name} is monkey patched`, function() {
|
||||
const patch = {};
|
||||
bootedCore[name] = patch;
|
||||
|
||||
verifyDeprecation();
|
||||
expect(bootedCore[name]).toBe(patch);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function expectedKeys(includeHtml) {
|
||||
if (includeHtml === undefined) {
|
||||
includeHtml = typeof window !== 'undefined';
|
||||
}
|
||||
// Does not include properties added by requireInterface(), since that isn't
|
||||
// called by defineJasmineUnderTest.js/nodeDefineJasmineUnderTest.js.
|
||||
const result = new Set([
|
||||
@@ -30,6 +95,7 @@ describe('The jasmine namespace', function() {
|
||||
'version',
|
||||
|
||||
// Asymmetric equality testers
|
||||
'allOf',
|
||||
'any',
|
||||
'anything',
|
||||
'arrayContaining',
|
||||
@@ -50,7 +116,7 @@ describe('The jasmine namespace', function() {
|
||||
'getGlobal'
|
||||
]);
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
if (includeHtml) {
|
||||
// jasmine-html.js
|
||||
result.add('HtmlReporter');
|
||||
result.add('HtmlReporterV2');
|
||||
@@ -75,4 +141,15 @@ describe('The jasmine namespace', function() {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function verifyDeprecation() {
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.error).toHaveBeenCalledOnceWith(
|
||||
jasmine.stringContaining('DEPRECATION: Monkey patching detected.')
|
||||
);
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.error).toHaveBeenCalledOnceWith(
|
||||
jasmine.stringContaining('jasmineNamespaceSpec.js')
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -768,7 +768,16 @@ describe('matchersUtil', function() {
|
||||
a2[0] = 1;
|
||||
const diffBuilder = new privateUnderTest.DiffBuilder();
|
||||
expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(false);
|
||||
jasmine.debugLog('Diff: ' + diffBuilder.getMessage());
|
||||
jasmine.debugLog(
|
||||
'a1 keys: ' + jasmine.pp(privateUnderTest.MatchersUtil.keys(a1))
|
||||
);
|
||||
jasmine.debugLog(
|
||||
'a2 keys: ' + jasmine.pp(privateUnderTest.MatchersUtil.keys(a2))
|
||||
);
|
||||
jasmine.debugLog('a1 length:' + a1.length);
|
||||
jasmine.debugLog('a2 length:' + a2.length);
|
||||
jasmine.debugLog('a1[0]: ' + a1[0]);
|
||||
jasmine.debugLog('a2[0]: ' + a2[0]);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
5
spec/helpers/callerFilenameShim.js
Normal file
5
spec/helpers/callerFilenameShim.js
Normal file
@@ -0,0 +1,5 @@
|
||||
(function() {
|
||||
specHelpers.callerFilenameShim = function(fn) {
|
||||
return fn();
|
||||
};
|
||||
})();
|
||||
@@ -1,9 +1,12 @@
|
||||
describe('HtmlReporter', function() {
|
||||
let env;
|
||||
let env, deprecator;
|
||||
|
||||
beforeEach(function() {
|
||||
env = new privateUnderTest.Env();
|
||||
spyOn(env, 'deprecated');
|
||||
deprecator = jasmine.createSpyObj('deprecator', [
|
||||
'verboseDeprecations',
|
||||
'addDeprecationWarning'
|
||||
]);
|
||||
env = new privateUnderTest.Env({ deprecator });
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
@@ -21,8 +24,10 @@ describe('HtmlReporter', function() {
|
||||
});
|
||||
reporter.initialize();
|
||||
|
||||
expect(env.deprecated).toHaveBeenCalledWith(
|
||||
'HtmlReporter and HtmlSpecFilter are deprecated. Use HtmlReporterV2 instead.'
|
||||
expect(deprecator.addDeprecationWarning).toHaveBeenCalledWith(
|
||||
jasmine.anything(),
|
||||
'HtmlReporter and HtmlSpecFilter are deprecated. Use HtmlReporterV2 instead.',
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -14,9 +14,7 @@ describe('HtmlReporterV2', function() {
|
||||
function setup(options = {}) {
|
||||
return new jasmineUnderTest.HtmlReporterV2({
|
||||
env,
|
||||
getContainer() {
|
||||
return container;
|
||||
},
|
||||
container,
|
||||
urls: new jasmineUnderTest.HtmlReporterV2Urls(),
|
||||
queryString: new jasmineUnderTest.QueryString({
|
||||
getWindowLocation() {
|
||||
@@ -28,8 +26,7 @@ describe('HtmlReporterV2', function() {
|
||||
}
|
||||
|
||||
it('builds the initial DOM elements, including the title banner', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
setup();
|
||||
|
||||
// Main top-level elements
|
||||
expect(container.querySelector('div.jasmine_html-reporter')).toBeTruthy();
|
||||
@@ -50,17 +47,6 @@ describe('HtmlReporterV2', function() {
|
||||
expect(version.textContent).toEqual(jasmineUnderTest.version);
|
||||
});
|
||||
|
||||
it('builds a single reporter even if initialized multiple times', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
reporter.initialize();
|
||||
reporter.initialize();
|
||||
|
||||
expect(
|
||||
container.querySelectorAll('div.jasmine_html-reporter').length
|
||||
).toEqual(1);
|
||||
});
|
||||
|
||||
describe('when a spec is done', function() {
|
||||
describe('and no expectations ran', function() {
|
||||
let reporter;
|
||||
@@ -70,8 +56,6 @@ describe('HtmlReporterV2', function() {
|
||||
|
||||
spyOn(console, 'warn');
|
||||
spyOn(console, 'error');
|
||||
|
||||
reporter.initialize();
|
||||
});
|
||||
|
||||
it('logs a warning to the console when the spec passed', function() {
|
||||
@@ -103,7 +87,6 @@ describe('HtmlReporterV2', function() {
|
||||
|
||||
it('updates the progress bar', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
const progress = container.querySelector('progress');
|
||||
|
||||
reporter.specDone({
|
||||
@@ -125,7 +108,6 @@ describe('HtmlReporterV2', function() {
|
||||
|
||||
it('changes the progress bar status if the spec failed', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.specDone({
|
||||
id: 345,
|
||||
@@ -142,9 +124,8 @@ describe('HtmlReporterV2', function() {
|
||||
describe('when there are deprecation warnings', function() {
|
||||
it('displays the messages in their own alert bars', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
reporter.specDone({
|
||||
status: 'passed',
|
||||
fullName: 'a spec with a deprecation',
|
||||
@@ -167,25 +148,24 @@ describe('HtmlReporterV2', function() {
|
||||
'.jasmine-alert .jasmine-bar'
|
||||
);
|
||||
|
||||
expect(alertBars.length).toEqual(4);
|
||||
expect(alertBars[1].innerHTML).toMatch(
|
||||
expect(alertBars.length).toEqual(5);
|
||||
expect(alertBars[2].innerHTML).toMatch(
|
||||
/spec deprecation.*\(in spec: a spec with a deprecation\)/
|
||||
);
|
||||
expect(alertBars[1].getAttribute('class')).toEqual(
|
||||
expect(alertBars[2].getAttribute('class')).toEqual(
|
||||
'jasmine-bar jasmine-warning'
|
||||
);
|
||||
expect(alertBars[2].innerHTML).toMatch(
|
||||
expect(alertBars[3].innerHTML).toMatch(
|
||||
/suite deprecation.*\(in suite: a suite with a deprecation\)/
|
||||
);
|
||||
expect(alertBars[3].innerHTML).toMatch(/global deprecation/);
|
||||
expect(alertBars[3].innerHTML).not.toMatch(/in /);
|
||||
expect(alertBars[4].innerHTML).toMatch(/global deprecation/);
|
||||
expect(alertBars[4].innerHTML).not.toMatch(/in /);
|
||||
});
|
||||
|
||||
it('displays expandable stack traces', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
reporter.jasmineDone({
|
||||
deprecationWarnings: [
|
||||
{
|
||||
@@ -219,9 +199,8 @@ describe('HtmlReporterV2', function() {
|
||||
|
||||
it('omits the expander when there is no stack trace', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
reporter.jasmineDone({
|
||||
deprecationWarnings: [
|
||||
{
|
||||
@@ -238,9 +217,8 @@ describe('HtmlReporterV2', function() {
|
||||
|
||||
it('nicely formats the verboseDeprecations note', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
reporter.jasmineDone({
|
||||
deprecationWarnings: [
|
||||
{
|
||||
@@ -259,13 +237,228 @@ describe('HtmlReporterV2', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('The tab bar', function() {
|
||||
function checkHidden(tabs, expected) {
|
||||
const actual = Array.from(tabs).map(t =>
|
||||
t.classList.contains('jasmine-hidden')
|
||||
);
|
||||
expect(actual)
|
||||
.withContext('tab hiddenness')
|
||||
.toEqual(expected);
|
||||
}
|
||||
|
||||
describe('while Jasmine is running', function() {
|
||||
it('hides all tabs', function() {
|
||||
const reporter = setup();
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
const tabs = container.querySelectorAll('.jasmine-tab');
|
||||
expect(tabs.length).toEqual(3);
|
||||
expect(tabs[0].textContent).toEqual('Spec List');
|
||||
expect(tabs[1].textContent).toEqual('Failures');
|
||||
expect(tabs[2].textContent).toEqual('Performance');
|
||||
checkHidden(tabs, [true, true, true]);
|
||||
|
||||
// Results, even failures, should not show any tabs
|
||||
reporter.specDone({
|
||||
id: 1,
|
||||
description: 'a failing spec',
|
||||
fullName: 'a failing spec',
|
||||
status: 'failed',
|
||||
failedExpectations: [{}],
|
||||
passedExpectations: []
|
||||
});
|
||||
checkHidden(tabs, [true, true, true]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when Jasmine is done', function() {
|
||||
function hasSpecOrSuiteFailureBehavior(reportEvents) {
|
||||
let reporter;
|
||||
|
||||
beforeEach(function() {
|
||||
reporter = setup();
|
||||
reportEvents(reporter);
|
||||
});
|
||||
|
||||
it('shows all three tabs', function() {
|
||||
const tabs = container.querySelectorAll('.jasmine-tab');
|
||||
checkHidden(tabs, [false, false, false]);
|
||||
});
|
||||
|
||||
it('selects the Failures tab', function() {
|
||||
const reporterNode = container.querySelector(
|
||||
'.jasmine_html-reporter'
|
||||
);
|
||||
expect(reporterNode).toHaveClass('jasmine-failure-list');
|
||||
});
|
||||
|
||||
it('switches between failure details and the spec summary', function() {
|
||||
const tabs = container.querySelectorAll('.jasmine-tab');
|
||||
let specListLink = () => tabs[0].querySelector('a');
|
||||
let failuresLink = () => tabs[1].querySelector('a');
|
||||
const reporterNode = container.querySelector(
|
||||
'.jasmine_html-reporter'
|
||||
);
|
||||
expect(specListLink().textContent).toEqual('Spec List');
|
||||
expect(failuresLink())
|
||||
.withContext('failures link')
|
||||
.toBeFalsy();
|
||||
|
||||
specListLink().click();
|
||||
expect(reporterNode).toHaveClass('jasmine-spec-list');
|
||||
expect(reporterNode).not.toHaveClass('jasmine-failure-list');
|
||||
expect(specListLink())
|
||||
.withContext('spec list link')
|
||||
.toBeFalsy();
|
||||
expect(failuresLink().textContent).toEqual('Failures');
|
||||
|
||||
failuresLink().click();
|
||||
expect(reporterNode.getAttribute('class')).toMatch(
|
||||
'jasmine-failure-list'
|
||||
);
|
||||
expect(failuresLink())
|
||||
.withContext('failures link')
|
||||
.toBeFalsy();
|
||||
expect(specListLink().textContent).toEqual('Spec List');
|
||||
expect(reporterNode).toHaveClass('jasmine-failure-list');
|
||||
expect(reporterNode).not.toHaveClass('jasmine-spec-list');
|
||||
});
|
||||
}
|
||||
|
||||
function hasSpecAndSuiteSuccessBehavior(reportEvents) {
|
||||
let reporter;
|
||||
|
||||
beforeEach(function() {
|
||||
reporter = setup();
|
||||
reportEvents(reporter);
|
||||
});
|
||||
|
||||
it('shows the Spec List and Performance tabs', function() {
|
||||
const tabs = container.querySelectorAll('.jasmine-tab');
|
||||
checkHidden(tabs, [false, true, false]);
|
||||
});
|
||||
|
||||
it('shows the spec list view', function() {
|
||||
const reporterNode = container.querySelector(
|
||||
'.jasmine_html-reporter'
|
||||
);
|
||||
expect(reporterNode).toHaveClass('jasmine-spec-list');
|
||||
expect(reporterNode).not.toHaveClass('jasmine-failure-list');
|
||||
});
|
||||
}
|
||||
|
||||
describe('with spec failures', function() {
|
||||
hasSpecOrSuiteFailureBehavior(function(reporter) {
|
||||
reporter.jasmineStarted({
|
||||
totalSpecsDefined: 0,
|
||||
numExcludedSpecs: 0
|
||||
});
|
||||
reporter.specDone({
|
||||
id: 1,
|
||||
description: 'a failing spec',
|
||||
fullName: 'a failing spec',
|
||||
status: 'failed',
|
||||
failedExpectations: [{}],
|
||||
passedExpectations: []
|
||||
});
|
||||
reporter.specDone({
|
||||
id: 2,
|
||||
description: 'a passing spec',
|
||||
fullName: 'a passing spec',
|
||||
status: 'passed',
|
||||
failedExpectations: [],
|
||||
passedExpectations: []
|
||||
});
|
||||
reporter.jasmineDone({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with suite failures', function() {
|
||||
hasSpecOrSuiteFailureBehavior(function(reporter) {
|
||||
reporter.jasmineStarted({
|
||||
totalSpecsDefined: 0,
|
||||
numExcludedSpecs: 0
|
||||
});
|
||||
reporter.specDone({
|
||||
id: 1,
|
||||
description: 'a failing spec',
|
||||
fullName: 'a failing spec',
|
||||
status: 'failed',
|
||||
failedExpectations: [{}],
|
||||
passedExpectations: []
|
||||
});
|
||||
reporter.specDone({
|
||||
id: 2,
|
||||
description: 'a passing spec',
|
||||
fullName: 'a passing spec',
|
||||
status: 'passed',
|
||||
failedExpectations: [],
|
||||
passedExpectations: []
|
||||
});
|
||||
reporter.jasmineDone({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('without any failures', function() {
|
||||
hasSpecAndSuiteSuccessBehavior(function(reporter) {
|
||||
reporter.jasmineStarted({
|
||||
totalSpecsDefined: 0,
|
||||
numExcludedSpecs: 0
|
||||
});
|
||||
reporter.specDone({
|
||||
id: 1,
|
||||
description: 'a passing spec',
|
||||
fullName: 'a passing spec',
|
||||
status: 'passed',
|
||||
failedExpectations: [],
|
||||
passedExpectations: []
|
||||
});
|
||||
reporter.suiteDone({ id: 1 });
|
||||
reporter.jasmineDone({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with only top suite failures', function() {
|
||||
// Top suite failures are displayed in their own alert bars, so they
|
||||
// don't cause the failures tab to be shown.
|
||||
hasSpecAndSuiteSuccessBehavior(function(reporter) {
|
||||
reporter.jasmineStarted({
|
||||
totalSpecsDefined: 0,
|
||||
numExcludedSpecs: 0
|
||||
});
|
||||
reporter.jasmineDone({
|
||||
failedExpectations: [{}]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the slow spec view when the Performance tab is clicked', function() {
|
||||
const reporter = setup();
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
reporter.specDone({
|
||||
duration: 1.2,
|
||||
failedExpectations: [],
|
||||
passedExpectations: []
|
||||
});
|
||||
reporter.jasmineDone({});
|
||||
const tabs = container.querySelectorAll('.jasmine-tab');
|
||||
let perfLink = tabs[2].querySelector('a');
|
||||
const reporterNode = container.querySelector('.jasmine_html-reporter');
|
||||
expect(perfLink.textContent).toEqual('Performance');
|
||||
perfLink.click();
|
||||
expect(reporterNode).toHaveClass('jasmine-performance');
|
||||
expect(reporterNode.innerHTML).toContain('<h2>Performance</h2>');
|
||||
expect(reporterNode.innerHTML).toContain('<td>1.2ms</td>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when Jasmine is done', function() {
|
||||
it('adds a warning to the link title of specs that have no expectations', function() {
|
||||
const reporter = setup();
|
||||
spyOn(console, 'error');
|
||||
|
||||
reporter.initialize();
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
reporter.suiteStarted({ id: 1 });
|
||||
reporter.specDone({
|
||||
id: 1,
|
||||
@@ -287,9 +480,8 @@ describe('HtmlReporterV2', function() {
|
||||
|
||||
it('reports the run time', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
|
||||
reporter.jasmineDone({ totalTime: 100 });
|
||||
|
||||
@@ -305,9 +497,8 @@ describe('HtmlReporterV2', function() {
|
||||
return '?foo=bar&' + key + '=' + value;
|
||||
}
|
||||
});
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
reporter.suiteStarted({
|
||||
id: 1,
|
||||
description: 'A Suite',
|
||||
@@ -412,7 +603,6 @@ describe('HtmlReporterV2', function() {
|
||||
|
||||
it('has an options menu', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const trigger = container.querySelector(
|
||||
@@ -436,9 +626,8 @@ describe('HtmlReporterV2', function() {
|
||||
describe('when there are global errors', function() {
|
||||
it('displays the exceptions in their own alert bars', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
reporter.jasmineDone({
|
||||
failedExpectations: [
|
||||
{
|
||||
@@ -449,28 +638,24 @@ describe('HtmlReporterV2', function() {
|
||||
]
|
||||
});
|
||||
|
||||
const alertBars = container.querySelectorAll(
|
||||
'.jasmine-alert .jasmine-bar'
|
||||
const errorBars = container.querySelectorAll(
|
||||
'.jasmine-alert .jasmine-bar.jasmine-errored'
|
||||
);
|
||||
|
||||
expect(alertBars.length).toEqual(3);
|
||||
expect(alertBars[1].getAttribute('class')).toEqual(
|
||||
'jasmine-bar jasmine-errored'
|
||||
);
|
||||
expect(alertBars[1].innerHTML).toMatch(
|
||||
expect(errorBars.length).toEqual(2);
|
||||
expect(errorBars[0].innerHTML).toMatch(
|
||||
/AfterAll Global After All Failure/
|
||||
);
|
||||
expect(alertBars[2].innerHTML).toMatch(
|
||||
expect(errorBars[1].innerHTML).toMatch(
|
||||
/Error during loading: Your JS is borken/
|
||||
);
|
||||
expect(alertBars[2].innerHTML).not.toMatch(/line/);
|
||||
expect(errorBars[1].innerHTML).not.toMatch(/line/);
|
||||
});
|
||||
|
||||
it('does not display the "AfterAll" prefix for other error types', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
reporter.jasmineDone({
|
||||
failedExpectations: [
|
||||
{ message: 'load error', globalErrorType: 'load' },
|
||||
@@ -482,25 +667,24 @@ describe('HtmlReporterV2', function() {
|
||||
]
|
||||
});
|
||||
|
||||
const alertBars = container.querySelectorAll(
|
||||
'.jasmine-alert .jasmine-bar'
|
||||
const errorBars = container.querySelectorAll(
|
||||
'.jasmine-alert .jasmine-bar.jasmine-errored'
|
||||
);
|
||||
|
||||
expect(alertBars.length).toEqual(4);
|
||||
expect(alertBars[1].textContent).toContain('load error');
|
||||
expect(alertBars[2].textContent).toContain('lateExpectation error');
|
||||
expect(alertBars[3].textContent).toContain('lateError error');
|
||||
expect(errorBars.length).toEqual(3);
|
||||
expect(errorBars[0].textContent).toContain('load error');
|
||||
expect(errorBars[1].textContent).toContain('lateExpectation error');
|
||||
expect(errorBars[2].textContent).toContain('lateError error');
|
||||
|
||||
for (let bar of alertBars) {
|
||||
for (let bar of errorBars) {
|
||||
expect(bar.textContent).not.toContain('AfterAll');
|
||||
}
|
||||
});
|
||||
|
||||
it('displays file and line information if available', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
reporter.jasmineDone({
|
||||
failedExpectations: [
|
||||
{
|
||||
@@ -512,12 +696,12 @@ describe('HtmlReporterV2', function() {
|
||||
]
|
||||
});
|
||||
|
||||
const alertBars = container.querySelectorAll(
|
||||
'.jasmine-alert .jasmine-bar'
|
||||
const alertBar = container.querySelector(
|
||||
'.jasmine-alert .jasmine-bar.jasmine-errored'
|
||||
);
|
||||
|
||||
expect(alertBars.length).toEqual(2);
|
||||
expect(alertBars[1].innerHTML).toMatch(
|
||||
expect(alertBar).toBeTruthy();
|
||||
expect(alertBar.innerHTML).toMatch(
|
||||
/Error during loading: Your JS is borken in some\/file.js line 42/
|
||||
);
|
||||
});
|
||||
@@ -526,7 +710,6 @@ describe('HtmlReporterV2', function() {
|
||||
describe('UI for stop on spec failure', function() {
|
||||
it('should be unchecked for full execution', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const stopOnFailureUI = container.querySelector('.jasmine-fail-fast');
|
||||
@@ -534,10 +717,9 @@ describe('HtmlReporterV2', function() {
|
||||
});
|
||||
|
||||
it('should be checked if stopping short', function() {
|
||||
const reporter = setup();
|
||||
env.configure({ stopOnSpecFailure: true });
|
||||
const reporter = setup();
|
||||
|
||||
reporter.initialize();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const stopOnFailureUI = container.querySelector('.jasmine-fail-fast');
|
||||
@@ -547,7 +729,6 @@ describe('HtmlReporterV2', function() {
|
||||
it('should navigate and turn the setting on', function() {
|
||||
const reporter = setup();
|
||||
|
||||
reporter.initialize();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const stopOnFailureUI = container.querySelector('.jasmine-fail-fast');
|
||||
@@ -557,10 +738,9 @@ describe('HtmlReporterV2', function() {
|
||||
});
|
||||
|
||||
it('should navigate and turn the setting off', function() {
|
||||
const reporter = setup();
|
||||
env.configure({ stopOnSpecFailure: true });
|
||||
const reporter = setup();
|
||||
|
||||
reporter.initialize();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const stopOnFailureUI = container.querySelector('.jasmine-fail-fast');
|
||||
@@ -573,7 +753,6 @@ describe('HtmlReporterV2', function() {
|
||||
describe('UI for throwing errors on expectation failures', function() {
|
||||
it('should be unchecked if not throwing', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const throwingExpectationsUI = container.querySelector(
|
||||
@@ -583,10 +762,9 @@ describe('HtmlReporterV2', function() {
|
||||
});
|
||||
|
||||
it('should be checked if throwing', function() {
|
||||
const reporter = setup();
|
||||
env.configure({ stopSpecOnExpectationFailure: true });
|
||||
const reporter = setup();
|
||||
|
||||
reporter.initialize();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const throwingExpectationsUI = container.querySelector(
|
||||
@@ -597,7 +775,6 @@ describe('HtmlReporterV2', function() {
|
||||
|
||||
it('should navigate and change the setting to on', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const throwingExpectationsUI = container.querySelector(
|
||||
@@ -609,11 +786,9 @@ describe('HtmlReporterV2', function() {
|
||||
});
|
||||
|
||||
it('should navigate and change the setting to off', function() {
|
||||
env.configure({ stopSpecOnExpectationFailure: true });
|
||||
const reporter = setup();
|
||||
|
||||
env.configure({ stopSpecOnExpectationFailure: true });
|
||||
|
||||
reporter.initialize();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const throwingExpectationsUI = container.querySelector(
|
||||
@@ -627,9 +802,8 @@ describe('HtmlReporterV2', function() {
|
||||
|
||||
describe('UI for running tests in random order', function() {
|
||||
it('should be unchecked if not randomizing', function() {
|
||||
const reporter = setup();
|
||||
env.configure({ random: false });
|
||||
reporter.initialize();
|
||||
const reporter = setup();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const randomUI = container.querySelector('.jasmine-random');
|
||||
@@ -637,9 +811,8 @@ describe('HtmlReporterV2', function() {
|
||||
});
|
||||
|
||||
it('should be checked if randomizing', function() {
|
||||
const reporter = setup();
|
||||
env.configure({ random: true });
|
||||
reporter.initialize();
|
||||
const reporter = setup();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const randomUI = container.querySelector('.jasmine-random');
|
||||
@@ -647,10 +820,9 @@ describe('HtmlReporterV2', function() {
|
||||
});
|
||||
|
||||
it('should navigate and change the setting to on', function() {
|
||||
env.configure({ random: false });
|
||||
const reporter = setup();
|
||||
|
||||
env.configure({ random: false });
|
||||
reporter.initialize();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const randomUI = container.querySelector('.jasmine-random');
|
||||
@@ -660,10 +832,9 @@ describe('HtmlReporterV2', function() {
|
||||
});
|
||||
|
||||
it('should navigate and change the setting to off', function() {
|
||||
env.configure({ random: true });
|
||||
const reporter = setup();
|
||||
|
||||
env.configure({ random: true });
|
||||
reporter.initialize();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const randomUI = container.querySelector('.jasmine-random');
|
||||
@@ -674,7 +845,6 @@ describe('HtmlReporterV2', function() {
|
||||
|
||||
it('should show the seed bar if randomizing', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
reporter.jasmineDone({
|
||||
order: {
|
||||
random: true,
|
||||
@@ -690,7 +860,6 @@ describe('HtmlReporterV2', function() {
|
||||
|
||||
it('should not show the current seed bar if not randomizing', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
reporter.jasmineDone({});
|
||||
|
||||
const seedBar = container.querySelector('.jasmine-seed-bar');
|
||||
@@ -700,13 +869,12 @@ describe('HtmlReporterV2', function() {
|
||||
it('includes the number of specs in the text of the jasmine-skipped link', function() {
|
||||
const reporter = setup();
|
||||
|
||||
reporter.initialize();
|
||||
const minimalSpecDone = {
|
||||
failedExpectations: [],
|
||||
passedExpectations: []
|
||||
};
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 3 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 3, numExcludedSpecs: 0 });
|
||||
reporter.specDone({ ...minimalSpecDone });
|
||||
reporter.specDone({ ...minimalSpecDone });
|
||||
reporter.specDone({ ...minimalSpecDone, status: 'excluded' });
|
||||
@@ -723,8 +891,7 @@ describe('HtmlReporterV2', function() {
|
||||
}
|
||||
});
|
||||
|
||||
reporter.initialize();
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 1 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 1, numExcludedSpecs: 0 });
|
||||
reporter.jasmineDone({ order: { random: true } });
|
||||
|
||||
const skippedLink = container.querySelector('.jasmine-skipped a');
|
||||
@@ -735,9 +902,8 @@ describe('HtmlReporterV2', function() {
|
||||
describe('and all specs pass', function() {
|
||||
beforeEach(function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 2 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 2, numExcludedSpecs: 0 });
|
||||
reporter.specDone({
|
||||
id: 123,
|
||||
description: 'with a spec',
|
||||
@@ -758,12 +924,12 @@ describe('HtmlReporterV2', function() {
|
||||
});
|
||||
|
||||
it('reports the specs counts', function() {
|
||||
const alertBars = container.querySelectorAll(
|
||||
'.jasmine-alert .jasmine-bar'
|
||||
const resultBar = container.querySelector(
|
||||
'.jasmine-alert .jasmine-bar.jasmine-overall-result'
|
||||
);
|
||||
|
||||
expect(alertBars.length).toEqual(1);
|
||||
expect(alertBars[0].innerHTML).toMatch(/2 specs, 0 failures/);
|
||||
expect(resultBar).toBeTruthy();
|
||||
expect(resultBar.innerHTML).toMatch(/2 specs, 0 failures/);
|
||||
});
|
||||
|
||||
it('reports no failure details', function() {
|
||||
@@ -802,8 +968,10 @@ describe('HtmlReporterV2', function() {
|
||||
}
|
||||
}
|
||||
});
|
||||
reporter.initialize();
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 1 });
|
||||
reporter.jasmineStarted({
|
||||
totalSpecsDefined: 1,
|
||||
numExcludedSpecs: 0
|
||||
});
|
||||
reporter.specDone(specStatus);
|
||||
reporter.jasmineDone({});
|
||||
});
|
||||
@@ -824,8 +992,10 @@ describe('HtmlReporterV2', function() {
|
||||
}
|
||||
}
|
||||
});
|
||||
reporter.initialize();
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 1 });
|
||||
reporter.jasmineStarted({
|
||||
totalSpecsDefined: 1,
|
||||
numExcludedSpecs: 0
|
||||
});
|
||||
reporter.specDone(specStatus);
|
||||
reporter.jasmineDone({});
|
||||
});
|
||||
@@ -859,9 +1029,8 @@ describe('HtmlReporterV2', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 1 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 1, numExcludedSpecs: 0 });
|
||||
});
|
||||
|
||||
it('reports the pending specs count', function() {
|
||||
@@ -912,9 +1081,8 @@ describe('HtmlReporterV2', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 1 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 1, numExcludedSpecs: 0 });
|
||||
reporter.suiteStarted({
|
||||
id: 1,
|
||||
description: 'A suite'
|
||||
@@ -1072,23 +1240,6 @@ describe('HtmlReporterV2', function() {
|
||||
)}`
|
||||
);
|
||||
});
|
||||
|
||||
it('allows switching between failure details and the spec summary', function() {
|
||||
const menuBar = container.querySelectorAll('.jasmine-bar')[1];
|
||||
|
||||
expect(menuBar.getAttribute('class')).not.toMatch(/hidden/);
|
||||
|
||||
const link = menuBar.querySelector('a');
|
||||
expect(link.innerHTML).toEqual('Failures');
|
||||
expect(link.getAttribute('href')).toEqual('#');
|
||||
});
|
||||
|
||||
it("sets the reporter to 'Failures List' mode", function() {
|
||||
const reporterNode = container.querySelector('.jasmine_html-reporter');
|
||||
expect(reporterNode.getAttribute('class')).toMatch(
|
||||
'jasmine-failure-list'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('counts failures that are reported in the jasmineDone event', function() {
|
||||
@@ -1097,9 +1248,8 @@ describe('HtmlReporterV2', function() {
|
||||
return '?' + key + '=' + value;
|
||||
}
|
||||
});
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 1 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 1, numExcludedSpecs: 0 });
|
||||
|
||||
const failingSpecResult = {
|
||||
id: 124,
|
||||
@@ -1139,7 +1289,6 @@ describe('HtmlReporterV2', function() {
|
||||
describe('When nothing has failed', function() {
|
||||
it('shows "Running..." and the has class jasmine-in-progress', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
const alertBar = container.querySelector('.jasmine-overall-result');
|
||||
|
||||
expect(alertBar.textContent).toEqual('Running...');
|
||||
@@ -1166,7 +1315,6 @@ describe('HtmlReporterV2', function() {
|
||||
describe('When a spec has failed', function() {
|
||||
it('shows "Failing..." and the has class jasmine-failed', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
const alertBar = container.querySelector('.jasmine-overall-result');
|
||||
|
||||
reporter.specDone({
|
||||
@@ -1185,7 +1333,6 @@ describe('HtmlReporterV2', function() {
|
||||
describe('When a suite has failed', function() {
|
||||
it('shows "Failing..." and the has class jasmine-failed', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
const alertBar = container.querySelector('.jasmine-overall-result');
|
||||
|
||||
reporter.suiteDone({
|
||||
@@ -1205,9 +1352,8 @@ describe('HtmlReporterV2', function() {
|
||||
describe("When the jasmineDone event's overallStatus is 'passed'", function() {
|
||||
it('has class jasmine-passed', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
reporter.jasmineDone({
|
||||
overallStatus: 'passed',
|
||||
failedExpectations: []
|
||||
@@ -1221,9 +1367,8 @@ describe('HtmlReporterV2', function() {
|
||||
describe("When the jasmineDone event's overallStatus is 'failed'", function() {
|
||||
it('has class jasmine-failed', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
reporter.jasmineDone({
|
||||
overallStatus: 'failed',
|
||||
failedExpectations: []
|
||||
@@ -1237,9 +1382,8 @@ describe('HtmlReporterV2', function() {
|
||||
describe("When the jasmineDone event's overallStatus is 'incomplete'", function() {
|
||||
it('has class jasmine-incomplete', function() {
|
||||
const reporter = setup();
|
||||
reporter.initialize();
|
||||
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0 });
|
||||
reporter.jasmineStarted({ totalSpecsDefined: 0, numExcludedSpecs: 0 });
|
||||
reporter.jasmineDone({
|
||||
overallStatus: 'incomplete',
|
||||
incompleteReason: 'because nope',
|
||||
|
||||
@@ -1,17 +1,29 @@
|
||||
describe('HtmlSpecFilter', function() {
|
||||
let env, deprecator;
|
||||
|
||||
beforeEach(function() {
|
||||
spyOn(jasmineUnderTest.getEnv(), 'deprecated');
|
||||
deprecator = jasmine.createSpyObj('deprecator', [
|
||||
'verboseDeprecations',
|
||||
'addDeprecationWarning'
|
||||
]);
|
||||
env = new privateUnderTest.Env({ deprecator });
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
env.cleanup_();
|
||||
});
|
||||
|
||||
it('emits a deprecation warning', function() {
|
||||
new jasmineUnderTest.HtmlSpecFilter();
|
||||
expect(jasmineUnderTest.getEnv().deprecated).toHaveBeenCalledWith(
|
||||
'HtmlReporter and HtmlSpecFilter are deprecated. Use HtmlReporterV2 instead.'
|
||||
new jasmineUnderTest.HtmlSpecFilter({ env });
|
||||
expect(deprecator.addDeprecationWarning).toHaveBeenCalledWith(
|
||||
jasmine.anything(),
|
||||
'HtmlReporter and HtmlSpecFilter are deprecated. Use HtmlReporterV2 instead.',
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
it('should match when no string is provided', function() {
|
||||
const specFilter = new jasmineUnderTest.HtmlSpecFilter();
|
||||
const specFilter = new jasmineUnderTest.HtmlSpecFilter({ env });
|
||||
|
||||
expect(specFilter.matches('foo')).toBe(true);
|
||||
expect(specFilter.matches('*bar')).toBe(true);
|
||||
@@ -19,6 +31,7 @@ describe('HtmlSpecFilter', function() {
|
||||
|
||||
it('should only match the provided string', function() {
|
||||
const specFilter = new jasmineUnderTest.HtmlSpecFilter({
|
||||
env,
|
||||
filterString: function() {
|
||||
return 'foo';
|
||||
}
|
||||
|
||||
@@ -1,48 +1,69 @@
|
||||
describe('HtmlSpecFilterV2', function() {
|
||||
it('matches everything when no string is provided', function() {
|
||||
const specFilter = new privateUnderTest.HtmlSpecFilterV2({
|
||||
filterString() {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
describe('When both query parameters are falsy', function() {
|
||||
it('matches everything', function() {
|
||||
const specFilter = new privateUnderTest.HtmlSpecFilterV2({
|
||||
filterParams() {
|
||||
return { path: '', spec: '' };
|
||||
}
|
||||
});
|
||||
|
||||
expect(specFilter.matches({})).toBeTrue();
|
||||
expect(specFilter.matches({})).toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
it('matches a spec with the exact same path', function() {
|
||||
const specFilter = new privateUnderTest.HtmlSpecFilterV2({
|
||||
filterString() {
|
||||
return '["a","b","c"]';
|
||||
}
|
||||
describe('When the path parameter is truthy', function() {
|
||||
it('matches a spec with the exact same path', function() {
|
||||
const specFilter = new privateUnderTest.HtmlSpecFilterV2({
|
||||
filterParams() {
|
||||
return { path: '["a","b","c"]', spec: '' };
|
||||
}
|
||||
});
|
||||
|
||||
expect(specFilter.matches(stubSpec(['a', 'b', 'c']))).toBeTrue();
|
||||
});
|
||||
|
||||
expect(specFilter.matches(stubSpec(['a', 'b', 'c']))).toBeTrue();
|
||||
});
|
||||
it('matches a spec whose path has the filter path as a prefix', function() {
|
||||
const specFilter = new privateUnderTest.HtmlSpecFilterV2({
|
||||
filterParams() {
|
||||
return { path: '["a","b"]', spec: '' };
|
||||
}
|
||||
});
|
||||
|
||||
it('matches a spec whose path has the filter path as a prefix', function() {
|
||||
const specFilter = new privateUnderTest.HtmlSpecFilterV2({
|
||||
filterString() {
|
||||
return '["a","b"]';
|
||||
}
|
||||
expect(specFilter.matches(stubSpec(['a', 'b', 'c']))).toBeTrue();
|
||||
});
|
||||
|
||||
expect(specFilter.matches(stubSpec(['a', 'b', 'c']))).toBeTrue();
|
||||
});
|
||||
it('does not match a spec with a different path', function() {
|
||||
const specFilter = new privateUnderTest.HtmlSpecFilterV2({
|
||||
filterParams() {
|
||||
return { path: '["a","b","c"]', spec: '' };
|
||||
}
|
||||
});
|
||||
|
||||
it('does not match a spec with a different path', function() {
|
||||
const specFilter = new privateUnderTest.HtmlSpecFilterV2({
|
||||
filterString() {
|
||||
return '["a","b","c"]';
|
||||
}
|
||||
expect(specFilter.matches(stubSpec(['a', 'd', 'c']))).toBeFalse();
|
||||
});
|
||||
|
||||
expect(specFilter.matches(stubSpec(['a', 'd', 'c']))).toBeFalse();
|
||||
});
|
||||
|
||||
function stubSpec(path) {
|
||||
describe('When the path parameter is falsy and the spec parameter is truthy', function() {
|
||||
it('matches specs with full names containing the parameter value', function() {
|
||||
const specFilter = new privateUnderTest.HtmlSpecFilterV2({
|
||||
filterParams() {
|
||||
return { path: '', spec: 'bar' };
|
||||
}
|
||||
});
|
||||
|
||||
expect(specFilter.matches(stubSpec('', 'foo bar baz'))).toBeTrue();
|
||||
expect(specFilter.matches(stubSpec('', 'foo baz'))).toBeFalse();
|
||||
expect(specFilter.matches(stubSpec('', 'sandbars'))).toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
function stubSpec(path, fullName) {
|
||||
return {
|
||||
getPath() {
|
||||
return path;
|
||||
},
|
||||
getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
107
spec/html/PerformanceViewSpec.js
Normal file
107
spec/html/PerformanceViewSpec.js
Normal file
@@ -0,0 +1,107 @@
|
||||
'use strict';
|
||||
|
||||
describe('PerformanceView', function() {
|
||||
it('shows specs ordered by execution time', function() {
|
||||
const stateBuilder = new privateUnderTest.ResultsStateBuilder();
|
||||
stateBuilder.suiteStarted({});
|
||||
stateBuilder.specDone({
|
||||
fullName: 'spec A',
|
||||
duration: 2
|
||||
});
|
||||
stateBuilder.suiteDone({});
|
||||
stateBuilder.specDone({
|
||||
fullName: 'spec B',
|
||||
duration: 1
|
||||
});
|
||||
stateBuilder.specDone({
|
||||
fullName: 'spec C',
|
||||
duration: 3
|
||||
});
|
||||
const subject = new privateUnderTest.PerformanceView();
|
||||
subject.addResults(stateBuilder.topResults);
|
||||
|
||||
const rows = Array.from(subject.rootEl.querySelectorAll('tbody tr'));
|
||||
const durations = rows.map(r => r.querySelectorAll('td')[0].textContent);
|
||||
const names = rows.map(r => r.querySelectorAll('td')[1].textContent);
|
||||
expect(names).toEqual(['spec C', 'spec A', 'spec B']);
|
||||
expect(durations).toEqual(['3ms', '2ms', '1ms']);
|
||||
});
|
||||
|
||||
it('shows at most 20 specs', function() {
|
||||
const stateBuilder = new privateUnderTest.ResultsStateBuilder();
|
||||
const subject = new privateUnderTest.PerformanceView();
|
||||
|
||||
for (let i = 0; i < 21; i++) {
|
||||
stateBuilder.specDone({
|
||||
fullName: `spec ${i}`,
|
||||
duration: i
|
||||
});
|
||||
}
|
||||
|
||||
subject.addResults(stateBuilder.topResults);
|
||||
|
||||
expect(subject.rootEl.querySelectorAll('tbody tr').length).toEqual(20);
|
||||
expect(subject.textContent).not.toContain('spec 0');
|
||||
});
|
||||
|
||||
it('shows mean and median run times for an odd number of specs', function() {
|
||||
const stateBuilder = new privateUnderTest.ResultsStateBuilder();
|
||||
const subject = new privateUnderTest.PerformanceView();
|
||||
|
||||
stateBuilder.specDone({ duration: 1 });
|
||||
stateBuilder.specDone({ duration: 2 });
|
||||
stateBuilder.specDone({ duration: 5 });
|
||||
subject.addResults(stateBuilder.topResults);
|
||||
|
||||
expect(subject.rootEl.textContent).toContain('Mean spec run time: 3ms');
|
||||
expect(subject.rootEl.textContent).toContain('Median spec run time: 2ms');
|
||||
});
|
||||
|
||||
it('shows mean and median run times for an even number of specs', function() {
|
||||
const stateBuilder = new privateUnderTest.ResultsStateBuilder();
|
||||
const subject = new privateUnderTest.PerformanceView();
|
||||
|
||||
stateBuilder.specDone({ duration: 1 });
|
||||
stateBuilder.specDone({ duration: 3 });
|
||||
stateBuilder.specDone({ duration: 10 });
|
||||
stateBuilder.specDone({ duration: 2 });
|
||||
subject.addResults(stateBuilder.topResults);
|
||||
|
||||
expect(subject.rootEl.textContent).toContain('Mean spec run time: 4ms');
|
||||
expect(subject.rootEl.textContent).toContain('Median spec run time: 2ms');
|
||||
});
|
||||
|
||||
it('copes with 0 specs', function() {
|
||||
const stateBuilder = new privateUnderTest.ResultsStateBuilder();
|
||||
const subject = new privateUnderTest.PerformanceView();
|
||||
|
||||
expect(function() {
|
||||
subject.addResults(stateBuilder.topResults);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('filters out excluded specs', function() {
|
||||
const stateBuilder = new privateUnderTest.ResultsStateBuilder();
|
||||
stateBuilder.specDone({
|
||||
fullName: 'spec A',
|
||||
duration: 2
|
||||
});
|
||||
stateBuilder.specDone({
|
||||
fullName: 'spec B',
|
||||
duration: 1,
|
||||
status: 'excluded'
|
||||
});
|
||||
stateBuilder.specDone({
|
||||
fullName: 'spec C',
|
||||
duration: 3
|
||||
});
|
||||
const subject = new privateUnderTest.PerformanceView();
|
||||
subject.addResults(stateBuilder.topResults);
|
||||
|
||||
const rows = Array.from(subject.rootEl.querySelectorAll('tbody tr'));
|
||||
const names = rows.map(r => r.querySelectorAll('td')[1].textContent);
|
||||
expect(names).toEqual(['spec C', 'spec A']);
|
||||
expect(subject.rootEl.textContent).toContain('Mean spec run time: 3ms');
|
||||
expect(subject.rootEl.textContent).toContain('Median spec run time: 2ms');
|
||||
});
|
||||
});
|
||||
97
spec/html/TabBarSpec.js
Normal file
97
spec/html/TabBarSpec.js
Normal file
@@ -0,0 +1,97 @@
|
||||
describe('TabBar', function() {
|
||||
it('initially renders but hides the tabs', function() {
|
||||
const subject = new privateUnderTest.TabBar([
|
||||
{ id: 'tab1', label: 'tab 1' }
|
||||
]);
|
||||
const tabs = subject.rootEl.querySelectorAll('.jasmine-tab');
|
||||
expect(tabs.length).toEqual(1);
|
||||
expect(tabs[0].id).toEqual('tab1');
|
||||
expect(tabs[0]).toHaveClass('jasmine-hidden');
|
||||
const link = tabs[0].querySelector('a');
|
||||
expect(link).toBeTruthy();
|
||||
expect(link.textContent).toEqual('tab 1');
|
||||
});
|
||||
|
||||
it('does not initially call the onSelect callback', function() {
|
||||
const onSelect = jasmine.createSpy('onSelect');
|
||||
new privateUnderTest.TabBar([{ id: 'tab1', label: '' }], onSelect);
|
||||
expect(onSelect).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('#showTab', function() {
|
||||
it('shows the specified tab', function() {
|
||||
const subject = new privateUnderTest.TabBar([
|
||||
{ id: 'tab1' },
|
||||
{ id: 'tab2' }
|
||||
]);
|
||||
|
||||
subject.showTab('tab2');
|
||||
|
||||
const tabs = subject.rootEl.querySelectorAll('.jasmine-tab');
|
||||
expect(tabs[0]).toHaveClass('jasmine-hidden');
|
||||
expect(tabs[1]).not.toHaveClass('jasmine-hidden');
|
||||
});
|
||||
|
||||
it('does not hide previously shown tabs', function() {
|
||||
const subject = new privateUnderTest.TabBar([
|
||||
{ id: 'tab1' },
|
||||
{ id: 'tab2' }
|
||||
]);
|
||||
|
||||
subject.showTab('tab1');
|
||||
subject.showTab('tab2');
|
||||
|
||||
const tabs = subject.rootEl.querySelectorAll('.jasmine-tab');
|
||||
expect(tabs[0]).not.toHaveClass('jasmine-hidden');
|
||||
});
|
||||
});
|
||||
|
||||
describe("When a tab's link is clicked", function() {
|
||||
it("calls the onSelect callback with the tab's id", function() {
|
||||
const onSelect = jasmine.createSpy('onSelect');
|
||||
const subject = new privateUnderTest.TabBar(
|
||||
[{ id: 'tab1', label: '' }],
|
||||
onSelect
|
||||
);
|
||||
|
||||
subject.rootEl.querySelector('.jasmine-tab a').click();
|
||||
|
||||
expect(onSelect).toHaveBeenCalledWith('tab1');
|
||||
});
|
||||
|
||||
it('shows links on all non-selected tabs only', function() {
|
||||
const subject = new privateUnderTest.TabBar(
|
||||
[
|
||||
{ id: 'tab1', label: 'tab 1' },
|
||||
{ id: 'tab2', label: 'tab 2' },
|
||||
{ id: 'tab3', label: 'tab 3' }
|
||||
],
|
||||
() => {}
|
||||
);
|
||||
|
||||
subject.rootEl.querySelectorAll('.jasmine-tab a')[1].click();
|
||||
let tabs = subject.rootEl.querySelectorAll('.jasmine-tab');
|
||||
expect(tabs[0].querySelector('a'))
|
||||
.withContext('tab 1')
|
||||
.toBeTruthy();
|
||||
expect(tabs[1].querySelector('a'))
|
||||
.withContext('tab 1')
|
||||
.toBeFalsy();
|
||||
expect(tabs[2].querySelector('a'))
|
||||
.withContext('tab 1')
|
||||
.toBeTruthy();
|
||||
|
||||
subject.rootEl.querySelectorAll('.jasmine-tab a')[0].click();
|
||||
tabs = subject.rootEl.querySelectorAll('.jasmine-tab');
|
||||
expect(tabs[0].querySelector('a'))
|
||||
.withContext('tab 1')
|
||||
.toBeFalsy();
|
||||
expect(tabs[1].querySelector('a'))
|
||||
.withContext('tab 1')
|
||||
.toBeTruthy();
|
||||
expect(tabs[2].querySelector('a'))
|
||||
.withContext('tab 1')
|
||||
.toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -23,11 +23,13 @@ module.exports = {
|
||||
'helpers/BrowserFlags.js',
|
||||
'helpers/domHelpers.js',
|
||||
'helpers/integrationMatchers.js',
|
||||
'helpers/callerFilenameShim.js',
|
||||
'helpers/defineJasmineUnderTest.js',
|
||||
'helpers/resetEnv.js'
|
||||
],
|
||||
env: {
|
||||
forbidDuplicateNames: true
|
||||
forbidDuplicateNames: true,
|
||||
safariYieldStrategy: 'time'
|
||||
},
|
||||
random: true,
|
||||
browser: {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -18,36 +18,23 @@
|
||||
const urls = new jasmine.HtmlReporterV2Urls();
|
||||
|
||||
/**
|
||||
* ## Reporters
|
||||
* The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
|
||||
* Configures Jasmine based on the current set of query parameters. This
|
||||
* supports all parameters set by the HTML reporter as well as
|
||||
* spec=partialPath, which filters out specs whose paths don't contain the
|
||||
* parameter.
|
||||
*/
|
||||
const htmlReporter = new jasmine.HtmlReporterV2({
|
||||
env,
|
||||
urls,
|
||||
getContainer() {
|
||||
return document.body;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
|
||||
*/
|
||||
env.addReporter(jsApiReporter);
|
||||
env.addReporter(htmlReporter);
|
||||
env.configure(urls.configFromCurrentUrl());
|
||||
|
||||
/**
|
||||
* ## Execution
|
||||
*
|
||||
* Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
|
||||
*/
|
||||
const currentWindowOnload = window.onload;
|
||||
|
||||
window.onload = function() {
|
||||
if (currentWindowOnload) {
|
||||
currentWindowOnload();
|
||||
}
|
||||
htmlReporter.initialize();
|
||||
|
||||
// The HTML reporter needs to be set up here so it can access the DOM. Other
|
||||
// reporters can be added at any time before env.execute() is called.
|
||||
const htmlReporter = new jasmine.HtmlReporterV2({ env, urls });
|
||||
env.addReporter(htmlReporter);
|
||||
env.execute();
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
getJasmineRequireObj().Clock = function() {
|
||||
getJasmineRequireObj().Clock = function(j$) {
|
||||
'use strict';
|
||||
|
||||
/* global process */
|
||||
@@ -191,6 +191,9 @@ callbacks to execute _before_ running the next one.
|
||||
clearTimeout[IsMockClockTimingFn] = true;
|
||||
setInterval[IsMockClockTimingFn] = true;
|
||||
clearInterval[IsMockClockTimingFn] = true;
|
||||
|
||||
j$.private.deprecateMonkeyPatching(this);
|
||||
|
||||
return this;
|
||||
|
||||
// Advances the Clock's time until the mode changes.
|
||||
|
||||
@@ -128,7 +128,45 @@ 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,
|
||||
|
||||
/**
|
||||
* The strategy to use in Safari and similar browsers to determine how often
|
||||
* to yield control by calling setTimeout. If set to "count", the default,
|
||||
* the frequency of setTimeout calls is based on the number of relevant
|
||||
* function calls. If set to "time", the frequency of setTimeout calls is
|
||||
* based on elapsed time. Using "time" may provide a significant performance
|
||||
* improvement, but as of 6.0 it hasn't been tested with a wide variety of
|
||||
* workloads and should be considered experimental.
|
||||
* @name Configuration#safariYieldStrategy
|
||||
* @since 6.0.0
|
||||
* @type 'count' | 'time'
|
||||
* @default 'count'
|
||||
*/
|
||||
safariYieldStrategy: 'count'
|
||||
};
|
||||
Object.freeze(defaultConfig);
|
||||
|
||||
@@ -179,6 +217,28 @@ getJasmineRequireObj().Configuration = function(j$) {
|
||||
if (typeof changes.seed !== 'undefined') {
|
||||
this.#values.seed = changes.seed;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (typeof changes.safariYieldStrategy !== 'undefined') {
|
||||
const v = changes.safariYieldStrategy;
|
||||
|
||||
if (v === 'count' || v === 'time') {
|
||||
this.#values.safariYieldStrategy = v;
|
||||
} else {
|
||||
throw new Error(
|
||||
"Invalid safariYieldStrategy value. Valid values are 'count' and 'time'."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
getJasmineRequireObj().Env = function(j$) {
|
||||
'use strict';
|
||||
|
||||
const DEFAULT_IT_DESCRIBE_STACK_DEPTH = 3;
|
||||
|
||||
/**
|
||||
* @class Env
|
||||
* @since 2.0.0
|
||||
@@ -18,7 +20,7 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
|
||||
const realSetTimeout = global.setTimeout;
|
||||
const realClearTimeout = global.clearTimeout;
|
||||
const clearStack = j$.private.getClearStack(global);
|
||||
const stackClearer = j$.private.getStackClearer(global);
|
||||
this.clock = new j$.private.Clock(
|
||||
global,
|
||||
function() {
|
||||
@@ -28,7 +30,7 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
);
|
||||
|
||||
const globalErrors = new GlobalErrors(
|
||||
undefined,
|
||||
global,
|
||||
// Configuration is late-bound because GlobalErrors needs to be constructed
|
||||
// before it's set to detect load-time errors in browsers
|
||||
() => this.configuration()
|
||||
@@ -97,6 +99,7 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
|
||||
config.update(changes);
|
||||
deprecator.verboseDeprecations(config.verboseDeprecations);
|
||||
stackClearer.setSafariYieldStrategy(config.safariYieldStrategy);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -293,13 +296,16 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
* @param {String|Error} deprecation The deprecation message
|
||||
* @param {Object} [options] Optional extra options, as described above
|
||||
*/
|
||||
this.deprecated = function(deprecation, options) {
|
||||
const runable = runner.currentRunable() || topSuite;
|
||||
deprecator.addDeprecationWarning(runable, deprecation, options);
|
||||
};
|
||||
Object.defineProperty(this, 'deprecated', {
|
||||
enumerable: true,
|
||||
value: function(deprecation, options) {
|
||||
const runable = runner.currentRunable() || topSuite;
|
||||
deprecator.addDeprecationWarning(runable, deprecation, options);
|
||||
}
|
||||
});
|
||||
|
||||
function runQueue(options) {
|
||||
options.clearStack = options.clearStack || clearStack;
|
||||
options.clearStack = options.clearStack || stackClearer;
|
||||
options.timeout = {
|
||||
setTimeout: realSetTimeout,
|
||||
clearTimeout: realClearTimeout
|
||||
@@ -323,7 +329,8 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
runQueue
|
||||
});
|
||||
topSuite = suiteBuilder.topSuite;
|
||||
const deprecator = new j$.private.Deprecator(topSuite);
|
||||
const deprecator =
|
||||
envOptions?.deprecator ?? new j$.private.Deprecator(topSuite);
|
||||
|
||||
/**
|
||||
* Provides the root suite, through which all suites and specs can be
|
||||
@@ -591,14 +598,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;
|
||||
};
|
||||
@@ -606,30 +613,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 SpecDoneEvent}
|
||||
* @name Env#getSpecProperty
|
||||
@@ -810,16 +825,26 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
}
|
||||
};
|
||||
|
||||
this.pp = function(value) {
|
||||
const pp = runner.currentRunable()
|
||||
? runableResources.makePrettyPrinter()
|
||||
: j$.private.basicPrettyPrinter;
|
||||
return pp(value);
|
||||
};
|
||||
|
||||
this.cleanup_ = function() {
|
||||
uninstallGlobalErrors();
|
||||
};
|
||||
|
||||
j$.private.deprecateMonkeyPatching(this, ['deprecated']);
|
||||
}
|
||||
|
||||
function callerCallerFilename() {
|
||||
function indirectCallerFilename(depth) {
|
||||
const frames = new j$.private.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;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
getJasmineRequireObj().JsApiReporter = function(j$) {
|
||||
'use strict';
|
||||
|
||||
// TODO: remove in 7.0.
|
||||
/**
|
||||
* @name jsApiReporter
|
||||
* @classdesc {@link Reporter} added by default in `boot.js` to record results for retrieval in javascript code. An instance is made available as `jsApiReporter` on the global object.
|
||||
* @class
|
||||
* @hideconstructor
|
||||
* @deprecated In most cases jsApiReporter can simply be removed. If necessary, it can be replaced with a {@link Reporter|custom reporter}.
|
||||
*/
|
||||
function JsApiReporter(options) {
|
||||
const timer = options.timer || new j$.Timer();
|
||||
|
||||
@@ -59,7 +59,7 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
|
||||
} else if (
|
||||
value.toString &&
|
||||
typeof value === 'object' &&
|
||||
!j$.private.isArray(value) &&
|
||||
!Array.isArray(value) &&
|
||||
hasCustomToString(value)
|
||||
) {
|
||||
try {
|
||||
@@ -71,15 +71,12 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
|
||||
} else if (this.seen.includes(value)) {
|
||||
this.emitScalar(
|
||||
'<circular reference: ' +
|
||||
(j$.private.isArray(value) ? 'Array' : 'Object') +
|
||||
(Array.isArray(value) ? 'Array' : 'Object') +
|
||||
'>'
|
||||
);
|
||||
} else if (
|
||||
j$.private.isArray(value) ||
|
||||
j$.private.isA('Object', value)
|
||||
) {
|
||||
} else if (Array.isArray(value) || j$.private.isA('Object', value)) {
|
||||
this.seen.push(value);
|
||||
if (j$.private.isArray(value)) {
|
||||
if (Array.isArray(value)) {
|
||||
this.emitArray(value);
|
||||
} else {
|
||||
this.emitObject(value);
|
||||
@@ -102,10 +99,7 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
|
||||
}
|
||||
|
||||
iterateObject(obj, fn) {
|
||||
const objKeys = j$.private.MatchersUtil.keys(
|
||||
obj,
|
||||
j$.private.isArray(obj)
|
||||
);
|
||||
const objKeys = j$.private.MatchersUtil.keys(obj, Array.isArray(obj));
|
||||
const length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
|
||||
@@ -51,11 +51,11 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
}
|
||||
|
||||
this.onComplete = attrs.onComplete || emptyFn;
|
||||
this.clearStack =
|
||||
attrs.clearStack ||
|
||||
function(fn) {
|
||||
this.clearStack = attrs.clearStack || {
|
||||
clearStack(fn) {
|
||||
fn();
|
||||
};
|
||||
}
|
||||
};
|
||||
this.onException = attrs.onException || emptyFn;
|
||||
this.onMultipleDone = attrs.onMultipleDone || fallbackOnMultipleDone;
|
||||
this.userContext = attrs.userContext || new j$.private.UserContext();
|
||||
@@ -235,7 +235,7 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
}
|
||||
}
|
||||
|
||||
this.clearStack(() => {
|
||||
this.clearStack.clearStack(() => {
|
||||
this.globalErrors.popListener(this.handleFinalError);
|
||||
|
||||
if (this.errored_) {
|
||||
|
||||
@@ -96,7 +96,8 @@ getJasmineRequireObj().Runner = function(j$) {
|
||||
/**
|
||||
* Information passed to the {@link Reporter#jasmineStarted} event.
|
||||
* @typedef JasmineStartedInfo
|
||||
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite. Note that this property is not present when Jasmine is run in parallel mode.
|
||||
* @property {int} totalSpecsDefined - The total number of specs defined in this suite. Note that this property is not present when Jasmine is run in parallel mode.
|
||||
* @property {int} numExcludedSpecs - The number of specs that will be excluded from execution. Note that this property is not present when Jasmine is run in parallel mode.
|
||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite. Note that this property is not present when Jasmine is run in parallel mode.
|
||||
* @property {Boolean} parallel - Whether Jasmine is being run in parallel mode.
|
||||
* @since 2.0.0
|
||||
@@ -105,6 +106,7 @@ getJasmineRequireObj().Runner = function(j$) {
|
||||
// In parallel mode, the jasmineStarted event is separately dispatched
|
||||
// by jasmine-npm. This event only reaches reporters in non-parallel.
|
||||
totalSpecsDefined,
|
||||
numExcludedSpecs: this.#executionTree.numExcludedSpecs(),
|
||||
order: orderForReporting(order),
|
||||
parallel: false
|
||||
});
|
||||
|
||||
@@ -72,8 +72,11 @@ getJasmineRequireObj().Spec = function(j$) {
|
||||
// Key and value will eventually be cloned during reporting. The error
|
||||
// thrown at that point if they aren't cloneable isn't very helpful.
|
||||
// Throw a better one now.
|
||||
j$.private.util.assertReporterCloneable(key, 'Key');
|
||||
if (!j$.private.isString(key)) {
|
||||
throw new Error('Key must be a string');
|
||||
}
|
||||
j$.private.util.assertReporterCloneable(value, 'Value');
|
||||
|
||||
this.#executionState.properties = this.#executionState.properties || {};
|
||||
this.#executionState.properties[key] = value;
|
||||
}
|
||||
@@ -153,17 +156,16 @@ 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.
|
||||
* @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 {String} status - The result of this spec. May be 'passed', 'failed', 'pending', or 'excluded'.
|
||||
* @property {number} duration - The time in ms used by the spec execution, including any before/afterEach.
|
||||
* @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty}
|
||||
* @property {DebugLogEntry[]|null} debugLogs - Messages, if any, that were logged using {@link jasmine.debugLog} during a failing spec.
|
||||
@@ -340,7 +342,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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ getJasmineRequireObj().SpyFactory = function(j$) {
|
||||
|
||||
this.createSpyObj = function(baseName, methodNames, propertyNames) {
|
||||
const baseNameIsCollection =
|
||||
j$.private.isObject(baseName) || j$.private.isArray(baseName);
|
||||
j$.private.isObject(baseName) || Array.isArray(baseName);
|
||||
|
||||
if (baseNameIsCollection) {
|
||||
propertyNames = methodNames;
|
||||
@@ -67,7 +67,7 @@ getJasmineRequireObj().SpyFactory = function(j$) {
|
||||
|
||||
function normalizeKeyValues(object) {
|
||||
const result = [];
|
||||
if (j$.private.isArray(object)) {
|
||||
if (Array.isArray(object)) {
|
||||
for (let i = 0; i < object.length; i++) {
|
||||
result.push([object[i]]);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,48 @@
|
||||
getJasmineRequireObj().clearStack = function(j$) {
|
||||
getJasmineRequireObj().StackClearer = function(j$) {
|
||||
'use strict';
|
||||
|
||||
const maxInlineCallCount = 10;
|
||||
// 25ms gives a good balance of speed and UI responsiveness when running
|
||||
// jasmine-core's own tests in Safari 18. The exact value isn't critical.
|
||||
const safariYieldIntervalMs = 25;
|
||||
|
||||
function browserQueueMicrotaskImpl(global) {
|
||||
const unclampedSetTimeout = getUnclampedSetTimeout(global);
|
||||
const { queueMicrotask } = global;
|
||||
let currentCallCount = 0;
|
||||
return function clearStack(fn) {
|
||||
currentCallCount++;
|
||||
let yieldStrategy = 'count';
|
||||
let currentCallCount = 0; // for count strategy
|
||||
let nextSetTimeoutTime; // for time strategy
|
||||
|
||||
if (currentCallCount < maxInlineCallCount) {
|
||||
queueMicrotask(fn);
|
||||
} else {
|
||||
currentCallCount = 0;
|
||||
unclampedSetTimeout(fn);
|
||||
return {
|
||||
clearStack(fn) {
|
||||
currentCallCount++;
|
||||
let shouldSetTimeout;
|
||||
|
||||
if (yieldStrategy === 'time') {
|
||||
const now = new Date().getTime();
|
||||
shouldSetTimeout = now >= nextSetTimeoutTime;
|
||||
if (shouldSetTimeout) {
|
||||
nextSetTimeoutTime = now + safariYieldIntervalMs;
|
||||
}
|
||||
} else {
|
||||
shouldSetTimeout = currentCallCount >= maxInlineCallCount;
|
||||
if (shouldSetTimeout) {
|
||||
currentCallCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSetTimeout) {
|
||||
unclampedSetTimeout(fn);
|
||||
} else {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
},
|
||||
setSafariYieldStrategy(strategy) {
|
||||
yieldStrategy = strategy;
|
||||
|
||||
if (yieldStrategy === 'time') {
|
||||
nextSetTimeoutTime = new Date().getTime() + safariYieldIntervalMs;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -22,8 +50,11 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
function nodeQueueMicrotaskImpl(global) {
|
||||
const { queueMicrotask } = global;
|
||||
|
||||
return function(fn) {
|
||||
queueMicrotask(fn);
|
||||
return {
|
||||
clearStack(fn) {
|
||||
queueMicrotask(fn);
|
||||
},
|
||||
setSafariYieldStrategy() {}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -32,15 +63,19 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
const postMessage = getPostMessage(global);
|
||||
|
||||
let currentCallCount = 0;
|
||||
return function clearStack(fn) {
|
||||
currentCallCount++;
|
||||
|
||||
if (currentCallCount < maxInlineCallCount) {
|
||||
postMessage(fn);
|
||||
} else {
|
||||
currentCallCount = 0;
|
||||
setTimeout(fn);
|
||||
}
|
||||
return {
|
||||
clearStack(fn) {
|
||||
currentCallCount++;
|
||||
|
||||
if (currentCallCount < maxInlineCallCount) {
|
||||
postMessage(fn);
|
||||
} else {
|
||||
currentCallCount = 0;
|
||||
setTimeout(fn);
|
||||
}
|
||||
},
|
||||
setSafariYieldStrategy() {}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -88,7 +123,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
};
|
||||
}
|
||||
|
||||
function getClearStack(global) {
|
||||
function getStackClearer(global) {
|
||||
const NODE_JS =
|
||||
global.process &&
|
||||
global.process.versions &&
|
||||
@@ -106,12 +141,10 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
||||
// so we avoid the overhead.
|
||||
return nodeQueueMicrotaskImpl(global);
|
||||
} else if (SAFARI_OR_WIN_WEBKIT || !global.MessageChannel /* tests */) {
|
||||
} else if (SAFARI_OR_WIN_WEBKIT) {
|
||||
// queueMicrotask is dramatically faster than MessageChannel in Safari
|
||||
// and other WebKit-based browsers, such as the one distributed by Playwright
|
||||
// to test Safari-like behavior on Windows.
|
||||
// Some of our own integration tests provide a mock queueMicrotask in all
|
||||
// environments because it's simpler to mock than MessageChannel.
|
||||
return browserQueueMicrotaskImpl(global);
|
||||
} else {
|
||||
// MessageChannel is faster than queueMicrotask in supported browsers
|
||||
@@ -120,5 +153,5 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
}
|
||||
}
|
||||
|
||||
return getClearStack;
|
||||
return getStackClearer;
|
||||
};
|
||||
@@ -37,8 +37,11 @@ getJasmineRequireObj().Suite = function(j$) {
|
||||
// Key and value will eventually be cloned during reporting. The error
|
||||
// thrown at that point if they aren't cloneable isn't very helpful.
|
||||
// Throw a better one now.
|
||||
j$.private.util.assertReporterCloneable(key, 'Key');
|
||||
if (!j$.private.isString(key)) {
|
||||
throw new Error('Key must be a string');
|
||||
}
|
||||
j$.private.util.assertReporterCloneable(value, 'Value');
|
||||
|
||||
this.#result.properties = this.#result.properties || {};
|
||||
this.#result.properties[key] = value;
|
||||
}
|
||||
@@ -146,12 +149,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.
|
||||
@@ -359,6 +361,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -123,6 +123,23 @@ getJasmineRequireObj().TreeProcessor = function(j$) {
|
||||
const nodeStats = this.#stats[node.id];
|
||||
return node.children ? !nodeStats.willExecute : nodeStats.excluded;
|
||||
}
|
||||
|
||||
numExcludedSpecs(node) {
|
||||
if (!node) {
|
||||
return this.numExcludedSpecs(this.topSuite);
|
||||
} else if (node.children) {
|
||||
let result = 0;
|
||||
|
||||
for (const child of node.children) {
|
||||
result += this.numExcludedSpecs(child);
|
||||
}
|
||||
|
||||
return result;
|
||||
} else {
|
||||
const nodeStats = this.#stats[node.id];
|
||||
return nodeStats.willExecute ? 0 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function segmentChildren(node, orderedChildren, stats, executableIndex) {
|
||||
|
||||
@@ -49,7 +49,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.startedEvent()).then(next);
|
||||
};
|
||||
const resultCallback = (result, next) => {
|
||||
|
||||
29
src/core/asymmetric_equality/AllOf.js
Normal file
29
src/core/asymmetric_equality/AllOf.js
Normal file
@@ -0,0 +1,29 @@
|
||||
getJasmineRequireObj().AllOf = function(j$) {
|
||||
'use strict';
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -6,7 +6,7 @@ getJasmineRequireObj().ArrayContaining = function(j$) {
|
||||
}
|
||||
|
||||
ArrayContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
|
||||
if (!j$.private.isArray(this.sample)) {
|
||||
if (!Array.isArray(this.sample)) {
|
||||
throw new Error(
|
||||
'You must provide an array to arrayContaining, not ' +
|
||||
j$.private.basicPrettyPrinter(this.sample) +
|
||||
@@ -17,7 +17,7 @@ getJasmineRequireObj().ArrayContaining = function(j$) {
|
||||
// If the actual parameter is not an array, we can fail immediately, since it couldn't
|
||||
// possibly be an "array containing" anything. However, we also want an empty sample
|
||||
// array to match anything, so we need to double-check we aren't in that case
|
||||
if (!j$.private.isArray(other) && this.sample.length > 0) {
|
||||
if (!Array.isArray(other) && this.sample.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ getJasmineRequireObj().ArrayWithExactContents = function(j$) {
|
||||
other,
|
||||
matchersUtil
|
||||
) {
|
||||
if (!j$.private.isArray(this.sample)) {
|
||||
if (!Array.isArray(this.sample)) {
|
||||
throw new Error(
|
||||
'You must provide an array to arrayWithExactContents, not ' +
|
||||
j$.private.basicPrettyPrinter(this.sample) +
|
||||
|
||||
@@ -6,7 +6,7 @@ getJasmineRequireObj().Empty = function(j$) {
|
||||
Empty.prototype.asymmetricMatch = function(other) {
|
||||
if (
|
||||
j$.private.isString(other) ||
|
||||
j$.private.isArray(other) ||
|
||||
Array.isArray(other) ||
|
||||
j$.private.isTypedArray(other)
|
||||
) {
|
||||
return other.length === 0;
|
||||
|
||||
@@ -6,7 +6,7 @@ getJasmineRequireObj().NotEmpty = function(j$) {
|
||||
NotEmpty.prototype.asymmetricMatch = function(other) {
|
||||
if (
|
||||
j$.private.isString(other) ||
|
||||
j$.private.isArray(other) ||
|
||||
Array.isArray(other) ||
|
||||
j$.private.isTypedArray(other)
|
||||
) {
|
||||
return other.length !== 0;
|
||||
|
||||
@@ -67,16 +67,15 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
||||
* @function
|
||||
* @return {Env}
|
||||
*/
|
||||
j$.getEnv = function(options) {
|
||||
const env = (j$.private.currentEnv_ =
|
||||
j$.private.currentEnv_ || new j$.private.Env(options));
|
||||
//jasmine. singletons in here (setTimeout blah blah).
|
||||
return env;
|
||||
};
|
||||
|
||||
j$.private.isArray = function(value) {
|
||||
return j$.private.isA('Array', value);
|
||||
};
|
||||
Object.defineProperty(j$, 'getEnv', {
|
||||
enumerable: true,
|
||||
value: function(options) {
|
||||
const env = (j$.private.currentEnv_ =
|
||||
j$.private.currentEnv_ || new j$.private.Env(options));
|
||||
//jasmine. singletons in here (setTimeout blah blah).
|
||||
return env;
|
||||
}
|
||||
});
|
||||
|
||||
j$.private.isObject = function(value) {
|
||||
return (
|
||||
@@ -229,6 +228,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.
|
||||
|
||||
22
src/core/deprecateMonkeyPatching.js
Normal file
22
src/core/deprecateMonkeyPatching.js
Normal file
@@ -0,0 +1,22 @@
|
||||
getJasmineRequireObj().deprecateMonkeyPatching = function(j$) {
|
||||
return function deprecateMonkeyPatching(obj, keysToSkip) {
|
||||
for (const key of Object.keys(obj)) {
|
||||
if (!keysToSkip?.includes(key)) {
|
||||
let value = obj[key];
|
||||
|
||||
Object.defineProperty(obj, key, {
|
||||
enumerable: key in obj,
|
||||
get() {
|
||||
return value;
|
||||
},
|
||||
set(newValue) {
|
||||
j$.getEnv().deprecated(
|
||||
'Monkey patching detected. Code that overwrites parts of Jasmine, except globala and other properties that are documented as writeable, is not supported and will break in a future release.'
|
||||
);
|
||||
value = newValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -13,7 +13,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
|
||||
this.customTesters_ = options.customTesters || [];
|
||||
/**
|
||||
* Formats a value for use in matcher failure messages and similar contexts,
|
||||
* taking into account the current set of custom value formatters.
|
||||
* taking into account the current set of custom object formatters.
|
||||
* @function
|
||||
* @name MatchersUtil#pp
|
||||
* @since 3.6.0
|
||||
|
||||
@@ -20,18 +20,24 @@ var getJasmineRequireObj = (function() {
|
||||
}
|
||||
|
||||
getJasmineRequire().core = function(jRequire) {
|
||||
const j$ = { private: {} };
|
||||
const j$ = {};
|
||||
Object.defineProperty(j$, 'private', {
|
||||
enumerable: true,
|
||||
value: {}
|
||||
});
|
||||
|
||||
jRequire.base(j$, globalThis);
|
||||
j$.private.deprecateMonkeyPatching = jRequire.deprecateMonkeyPatching(j$);
|
||||
j$.private.util = jRequire.util(j$);
|
||||
j$.private.errors = jRequire.errors();
|
||||
j$.private.formatErrorMsg = jRequire.formatErrorMsg(j$);
|
||||
j$.private.AllOf = jRequire.AllOf(j$);
|
||||
j$.private.Any = jRequire.Any(j$);
|
||||
j$.private.Anything = jRequire.Anything(j$);
|
||||
j$.private.CallTracker = jRequire.CallTracker(j$);
|
||||
j$.private.MockDate = jRequire.MockDate(j$);
|
||||
j$.private.getClearStack = jRequire.clearStack(j$);
|
||||
j$.private.Clock = jRequire.Clock();
|
||||
j$.private.getStackClearer = jRequire.StackClearer(j$);
|
||||
j$.private.Clock = jRequire.Clock(j$);
|
||||
j$.private.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$);
|
||||
j$.private.Deprecator = jRequire.Deprecator(j$);
|
||||
j$.private.Configuration = jRequire.Configuration(j$);
|
||||
@@ -97,6 +103,20 @@ var getJasmineRequireObj = (function() {
|
||||
j$.private.loadedAsBrowserEsm =
|
||||
globalThis.document && !globalThis.document.currentScript;
|
||||
|
||||
j$.private.deprecateMonkeyPatching(j$, [
|
||||
// These are meant to be set by users.
|
||||
'DEFAULT_TIMEOUT_INTERVAL',
|
||||
'MAX_PRETTY_PRINT_ARRAY_LENGTH',
|
||||
'MAX_PRETTY_PRINT_CHARS',
|
||||
'MAX_PRETTY_PRINT_DEPTH',
|
||||
|
||||
// These are part of the deprecation warning mechanism. To avoid infinite
|
||||
// recursion, they're separately protected in a way that doesn't emit
|
||||
// deprecation warnings.
|
||||
'private',
|
||||
'getEnv'
|
||||
]);
|
||||
|
||||
return j$;
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 1.3.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {String} description Textual description of the group
|
||||
* @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs
|
||||
*/
|
||||
@@ -35,6 +36,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 1.3.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {String} description Textual description of the group
|
||||
* @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs
|
||||
*/
|
||||
@@ -51,6 +53,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 2.1.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {String} description Textual description of the group
|
||||
* @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs
|
||||
*/
|
||||
@@ -69,6 +72,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 1.3.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {String} description Textual description of what this spec is checking
|
||||
* @param {implementationCallback} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`.
|
||||
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec.
|
||||
@@ -86,6 +90,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 1.3.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {String} description Textual description of what this spec is checking.
|
||||
* @param {implementationCallback} [testFunction] Function that contains the code of your test. Will not be executed.
|
||||
*/
|
||||
@@ -101,6 +106,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 2.1.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {String} description Textual description of what this spec is checking.
|
||||
* @param {implementationCallback} testFunction Function that contains the code of your test.
|
||||
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec.
|
||||
@@ -116,6 +122,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 1.3.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {implementationCallback} [function] Function that contains the code to setup your specs.
|
||||
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach.
|
||||
* @see async
|
||||
@@ -130,6 +137,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 1.3.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {implementationCallback} [function] Function that contains the code to teardown your specs.
|
||||
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach.
|
||||
* @see async
|
||||
@@ -146,6 +154,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 2.1.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {implementationCallback} [function] Function that contains the code to setup your specs.
|
||||
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll.
|
||||
* @see async
|
||||
@@ -162,6 +171,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 2.1.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {implementationCallback} [function] Function that contains the code to teardown your specs.
|
||||
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll.
|
||||
* @see async
|
||||
@@ -175,6 +185,8 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @name getSpecProperty
|
||||
* @since 5.10.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {String} key The name of the property
|
||||
* @returns {*} The value of the property
|
||||
*/
|
||||
@@ -187,6 +199,8 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @name setSpecProperty
|
||||
* @since 3.6.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {String} key The name of the property
|
||||
* @param {*} value The value of the property
|
||||
*/
|
||||
@@ -199,6 +213,8 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @name setSuiteProperty
|
||||
* @since 3.6.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {String} key The name of the property
|
||||
* @param {*} value The value of the property
|
||||
*/
|
||||
@@ -212,6 +228,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 1.3.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {Object} actual - Actual computed value to test expectations against.
|
||||
* @return {matchers}
|
||||
*/
|
||||
@@ -228,6 +245,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 3.3.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {Object} actual - Actual computed value to test expectations against.
|
||||
* @return {async-matchers}
|
||||
* @example
|
||||
@@ -254,8 +272,8 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @name throwUnlessAsync
|
||||
* @since 5.1.0
|
||||
* @function
|
||||
* @param actual
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {Object} actual - Actual computed value to test expectations against.
|
||||
* @return {matchers}
|
||||
*/
|
||||
@@ -278,8 +296,8 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @name throwUnless
|
||||
* @since 5.1.0
|
||||
* @function
|
||||
* @param actual
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {Object} actual - Actual computed value to test expectations against.
|
||||
* @return {matchers}
|
||||
*/
|
||||
@@ -293,6 +311,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 2.0.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {String} [message] - Reason the spec is pending.
|
||||
*/
|
||||
pending: function() {
|
||||
@@ -305,6 +324,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 2.1.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {String|Error} [error] - Reason for the failure.
|
||||
*/
|
||||
fail: function() {
|
||||
@@ -317,6 +337,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 1.3.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {Object} obj - The object upon which to install the {@link Spy}.
|
||||
* @param {String} methodName - The name of the method to replace with a {@link Spy}.
|
||||
* @returns {Spy}
|
||||
@@ -331,6 +352,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 2.6.0
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {Object} obj - The object upon which to install the {@link Spy}
|
||||
* @param {String} propertyName - The name of the property to replace with a {@link Spy}.
|
||||
* @param {String} [accessType=get] - The access type (get|set) of the property to {@link Spy} on.
|
||||
@@ -346,6 +368,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @since 3.2.1
|
||||
* @function
|
||||
* @global
|
||||
* @overwritable
|
||||
* @param {Object} obj - The object upon which to install the {@link Spy}s
|
||||
* @param {boolean} includeNonEnumerable - Whether or not to add spies to non-enumerable properties
|
||||
* @returns {Object} the spied object
|
||||
@@ -369,6 +392,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
*/
|
||||
jasmine: jasmine
|
||||
};
|
||||
const existingKeys = Object.keys(jasmine);
|
||||
|
||||
/**
|
||||
* Add a custom equality tester for the current scope of specs.
|
||||
@@ -498,6 +522,21 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
return env.setDefaultSpyStrategy(defaultStrategyFn);
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats a value for display, taking into account the current set of
|
||||
* custom object formatters.
|
||||
*
|
||||
* @name jasmine.pp
|
||||
* @function
|
||||
* @since 6.0.0
|
||||
* @param {*} value The value to pretty-print
|
||||
* @return {string} The pretty-printed value
|
||||
* @see {MatchersUtil#pp}
|
||||
*/
|
||||
jasmine.pp = function(value) {
|
||||
return env.pp(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* {@link AsymmetricEqualityTester|Asymmetric equality testers} allow for
|
||||
* non-exact matching in matchers that use Jasmine's deep value equality
|
||||
@@ -520,5 +559,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
||||
* @namespace asymmetricEqualityTesters
|
||||
*/
|
||||
|
||||
jasmine.private.deprecateMonkeyPatching(jasmine, existingKeys);
|
||||
|
||||
return jasmineInterface;
|
||||
};
|
||||
|
||||
@@ -24,38 +24,6 @@ jasmineRequire.AlertsView = function(j$) {
|
||||
);
|
||||
}
|
||||
|
||||
addFailureToggle(onClickFailures, onClickSpecList) {
|
||||
const failuresLink = createDom(
|
||||
'a',
|
||||
{ className: 'jasmine-failures-menu', href: '#' },
|
||||
'Failures'
|
||||
);
|
||||
let specListLink = createDom(
|
||||
'a',
|
||||
{ className: 'jasmine-spec-list-menu', href: '#' },
|
||||
'Spec List'
|
||||
);
|
||||
|
||||
failuresLink.onclick = function() {
|
||||
onClickFailures();
|
||||
return false;
|
||||
};
|
||||
|
||||
specListLink.onclick = function() {
|
||||
onClickSpecList();
|
||||
return false;
|
||||
};
|
||||
|
||||
this.#createAndAdd('jasmine-menu jasmine-bar jasmine-spec-list', [
|
||||
createDom('span', {}, 'Spec List | '),
|
||||
failuresLink
|
||||
]);
|
||||
this.#createAndAdd('jasmine-menu jasmine-bar jasmine-failure-list', [
|
||||
specListLink,
|
||||
createDom('span', {}, ' | Failures ')
|
||||
]);
|
||||
}
|
||||
|
||||
addGlobalFailure(failure) {
|
||||
this.#createAndAdd(
|
||||
errorBarClassName,
|
||||
|
||||
@@ -142,16 +142,52 @@ jasmineRequire.HtmlReporter = function(j$) {
|
||||
results.appendChild(summary.rootEl);
|
||||
|
||||
if (this.#stateBuilder.anyNonTopSuiteFailures) {
|
||||
this.#alerts.addFailureToggle(
|
||||
() => this.#setMenuModeTo('jasmine-failure-list'),
|
||||
() => this.#setMenuModeTo('jasmine-spec-list')
|
||||
);
|
||||
|
||||
this.#addFailureToggle();
|
||||
this.#setMenuModeTo('jasmine-failure-list');
|
||||
this.#failures.show();
|
||||
}
|
||||
}
|
||||
|
||||
#addFailureToggle() {
|
||||
const onClickFailures = () => this.#setMenuModeTo('jasmine-failure-list');
|
||||
const onClickSpecList = () => this.#setMenuModeTo('jasmine-spec-list');
|
||||
const failuresLink = createDom(
|
||||
'a',
|
||||
{ className: 'jasmine-failures-menu', href: '#' },
|
||||
'Failures'
|
||||
);
|
||||
let specListLink = createDom(
|
||||
'a',
|
||||
{ className: 'jasmine-spec-list-menu', href: '#' },
|
||||
'Spec List'
|
||||
);
|
||||
|
||||
failuresLink.onclick = function() {
|
||||
onClickFailures();
|
||||
return false;
|
||||
};
|
||||
|
||||
specListLink.onclick = function() {
|
||||
onClickSpecList();
|
||||
return false;
|
||||
};
|
||||
|
||||
this.#alerts.addBar(
|
||||
createDom(
|
||||
'span',
|
||||
{ className: 'jasmine-menu jasmine-bar jasmine-spec-list' },
|
||||
[createDom('span', {}, 'Spec List | '), failuresLink]
|
||||
)
|
||||
);
|
||||
this.#alerts.addBar(
|
||||
createDom(
|
||||
'span',
|
||||
{ className: 'jasmine-menu jasmine-bar jasmine-failure-list' },
|
||||
[specListLink, createDom('span', {}, ' | Failures ')]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#find(selector) {
|
||||
return this.#getContainer().querySelector(
|
||||
'.jasmine_html-reporter ' + selector
|
||||
|
||||
@@ -3,6 +3,10 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
|
||||
const { createDom, noExpectations } = j$.private.htmlReporterUtils;
|
||||
|
||||
const specListTabId = 'jasmine-specListTab';
|
||||
const failuresTabId = 'jasmine-failuresTab';
|
||||
const perfTabId = 'jasmine-perfTab';
|
||||
|
||||
/**
|
||||
* @class HtmlReporterV2
|
||||
* @classdesc Displays results and allows re-running individual specs and suites.
|
||||
@@ -15,12 +19,12 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
* const reporter = new jasmine.HtmlReporterV2({
|
||||
* env,
|
||||
* urls,
|
||||
* container: document.body
|
||||
* // container is optional and defaults to document.body.
|
||||
* container: someElement
|
||||
* });
|
||||
*/
|
||||
class HtmlReporterV2 {
|
||||
#env;
|
||||
#getContainer;
|
||||
#container;
|
||||
#queryString;
|
||||
#urlBuilder;
|
||||
#filterSpecs;
|
||||
@@ -31,14 +35,13 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
// Sub-views
|
||||
#alerts;
|
||||
#statusBar;
|
||||
#tabBar;
|
||||
#progress;
|
||||
#banner;
|
||||
#failures;
|
||||
|
||||
constructor(options) {
|
||||
this.#env = options.env;
|
||||
|
||||
this.#getContainer = options.getContainer;
|
||||
this.#container = options.container || document.body;
|
||||
this.#queryString =
|
||||
options.queryString ||
|
||||
new j$.QueryString({
|
||||
@@ -51,16 +54,8 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
getSuiteById: id => this.#stateBuilder.suitesById[id]
|
||||
});
|
||||
this.#filterSpecs = options.urls.filteringSpecs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the reporter. Should be called before {@link Env#execute}.
|
||||
* @function
|
||||
* @name HtmlReporter#initialize
|
||||
*/
|
||||
initialize() {
|
||||
this.#clearPrior();
|
||||
this.#config = this.#env ? this.#env.configuration() : {};
|
||||
this.#config = options.env ? options.env.configuration() : {};
|
||||
|
||||
this.#stateBuilder = new j$.private.ResultsStateBuilder();
|
||||
|
||||
@@ -68,6 +63,25 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
this.#statusBar = new j$.private.OverallStatusBar(this.#urlBuilder);
|
||||
this.#statusBar.showRunning();
|
||||
this.#alerts.addBar(this.#statusBar.rootEl);
|
||||
|
||||
this.#tabBar = new j$.private.TabBar(
|
||||
[
|
||||
{ id: specListTabId, label: 'Spec List' },
|
||||
{ id: failuresTabId, label: 'Failures' },
|
||||
{ id: perfTabId, label: 'Performance' }
|
||||
],
|
||||
tabId => {
|
||||
if (tabId === specListTabId) {
|
||||
this.#setMenuModeTo('jasmine-spec-list');
|
||||
} else if (tabId === failuresTabId) {
|
||||
this.#setMenuModeTo('jasmine-failure-list');
|
||||
} else {
|
||||
this.#setMenuModeTo('jasmine-performance');
|
||||
}
|
||||
}
|
||||
);
|
||||
this.#alerts.addBar(this.#tabBar.rootEl);
|
||||
|
||||
this.#progress = new ProgressView();
|
||||
this.#banner = new j$.private.Banner(
|
||||
this.#queryString.navigateWithNewParam.bind(this.#queryString),
|
||||
@@ -82,13 +96,15 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
this.#alerts.rootEl,
|
||||
this.#failures.rootEl
|
||||
);
|
||||
this.#getContainer().appendChild(this.#htmlReporterMain);
|
||||
this.#container.appendChild(this.#htmlReporterMain);
|
||||
this.#failures.show();
|
||||
}
|
||||
|
||||
jasmineStarted(options) {
|
||||
this.#stateBuilder.jasmineStarted(options);
|
||||
this.#progress.start(options.totalSpecsDefined);
|
||||
this.#progress.start(
|
||||
options.totalSpecsDefined - options.numExcludedSpecs
|
||||
);
|
||||
}
|
||||
|
||||
suiteStarted(result) {
|
||||
@@ -158,32 +174,26 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
);
|
||||
summary.addResults(this.#stateBuilder.topResults);
|
||||
results.appendChild(summary.rootEl);
|
||||
const perf = new j$.private.PerformanceView();
|
||||
perf.addResults(this.#stateBuilder.topResults);
|
||||
results.appendChild(perf.rootEl);
|
||||
this.#tabBar.showTab(specListTabId);
|
||||
this.#tabBar.showTab(perfTabId);
|
||||
|
||||
if (this.#stateBuilder.anyNonTopSuiteFailures) {
|
||||
this.#alerts.addFailureToggle(
|
||||
() => this.#setMenuModeTo('jasmine-failure-list'),
|
||||
() => this.#setMenuModeTo('jasmine-spec-list')
|
||||
);
|
||||
|
||||
this.#setMenuModeTo('jasmine-failure-list');
|
||||
this.#failures.show();
|
||||
this.#tabBar.showTab(failuresTabId);
|
||||
this.#tabBar.selectTab(failuresTabId);
|
||||
} else {
|
||||
this.#tabBar.selectTab(specListTabId);
|
||||
}
|
||||
}
|
||||
|
||||
#find(selector) {
|
||||
return this.#getContainer().querySelector(
|
||||
return this.#container.querySelector(
|
||||
'.jasmine_html-reporter ' + selector
|
||||
);
|
||||
}
|
||||
|
||||
#clearPrior() {
|
||||
const oldReporter = this.#find('');
|
||||
|
||||
if (oldReporter) {
|
||||
this.#getContainer().removeChild(oldReporter);
|
||||
}
|
||||
}
|
||||
|
||||
#setMenuModeTo(mode) {
|
||||
this.#htmlReporterMain.setAttribute(
|
||||
'class',
|
||||
@@ -202,10 +212,11 @@ jasmineRequire.HtmlReporterV2 = function(j$) {
|
||||
}
|
||||
|
||||
specDone(result) {
|
||||
this.rootEl.value = this.rootEl.value + 1;
|
||||
if (result.status !== 'excluded') {
|
||||
this.rootEl.value = this.rootEl.value + 1;
|
||||
}
|
||||
|
||||
if (result.status === 'failed') {
|
||||
// TODO: also a non-color indicator
|
||||
this.rootEl.classList.add('failed');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,10 @@ jasmineRequire.HtmlReporterV2Urls = function(j$) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Configuration} from the current page's URL.
|
||||
* Creates a {@link Configuration} from the current page's URL. Supported
|
||||
* query string parameters include all those set by {@link HtmlReporterV2}
|
||||
* as well as spec=partialPath, which filters out specs whose paths don't
|
||||
* contain partialPath.
|
||||
* @returns {Configuration}
|
||||
* @example
|
||||
* const urls = new jasmine.HtmlReporterV2Urls();
|
||||
@@ -47,9 +50,10 @@ jasmineRequire.HtmlReporterV2Urls = function(j$) {
|
||||
}
|
||||
|
||||
const specFilter = new j$.private.HtmlSpecFilterV2({
|
||||
filterString: () => {
|
||||
return this.queryString.getParam('path');
|
||||
}
|
||||
filterParams: () => ({
|
||||
path: this.queryString.getParam('path'),
|
||||
spec: this.queryString.getParam('spec')
|
||||
})
|
||||
});
|
||||
|
||||
config.specFilter = function(spec) {
|
||||
|
||||
@@ -7,12 +7,14 @@ jasmineRequire.HtmlSpecFilter = function(j$) {
|
||||
* @deprecated Use {@link HtmlReporterV2Urls} instead.
|
||||
*/
|
||||
function HtmlSpecFilter(options) {
|
||||
j$.getEnv().deprecated(
|
||||
const env = options?.env ?? j$.getEnv();
|
||||
env.deprecated(
|
||||
'HtmlReporter and HtmlSpecFilter are deprecated. Use HtmlReporterV2 instead.'
|
||||
);
|
||||
|
||||
const filterString =
|
||||
options &&
|
||||
options.filterString &&
|
||||
options.filterString() &&
|
||||
options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
||||
const filterPattern = new RegExp(filterString);
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
jasmineRequire.HtmlSpecFilterV2 = function() {
|
||||
class HtmlSpecFilterV2 {
|
||||
#getFilterString;
|
||||
#getFilterParams;
|
||||
|
||||
constructor(options) {
|
||||
this.#getFilterString = options.filterString;
|
||||
this.#getFilterParams = options.filterParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the spec with the specified name should be executed.
|
||||
* @name HtmlSpecFilterV2#matches
|
||||
* @function
|
||||
* @param {Spec} spec
|
||||
* @returns {boolean}
|
||||
*/
|
||||
matches(spec) {
|
||||
const filterString = this.#getFilterString();
|
||||
const params = this.#getFilterParams();
|
||||
|
||||
if (!filterString) {
|
||||
return true;
|
||||
if (params.path) {
|
||||
return this.#matchesPath(spec, JSON.parse(params.path));
|
||||
} else if (params.spec) {
|
||||
// Like legacy HtmlSpecFilter, retained because it's convenient for
|
||||
// hand-constructing filter URLs
|
||||
return spec.getFullName().includes(params.spec);
|
||||
}
|
||||
|
||||
const filterPath = JSON.parse(this.#getFilterString());
|
||||
return true;
|
||||
}
|
||||
|
||||
#matchesPath(spec, path) {
|
||||
const specPath = spec.getPath();
|
||||
|
||||
if (filterPath.length > specPath.length) {
|
||||
if (path.length > specPath.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < filterPath.length; i++) {
|
||||
if (specPath[i] !== filterPath[i]) {
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
if (specPath[i] !== path[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
98
src/html/PerformanceView.js
Normal file
98
src/html/PerformanceView.js
Normal file
@@ -0,0 +1,98 @@
|
||||
jasmineRequire.PerformanceView = function(j$) {
|
||||
const createDom = j$.private.htmlReporterUtils.createDom;
|
||||
const MAX_SLOW_SPECS = 20;
|
||||
|
||||
class PerformanceView {
|
||||
#summary;
|
||||
#tbody;
|
||||
|
||||
constructor() {
|
||||
this.#tbody = document.createElement('tbody');
|
||||
this.#summary = document.createElement('div');
|
||||
this.rootEl = createDom(
|
||||
'div',
|
||||
{ className: 'jasmine-performance-view' },
|
||||
createDom('h2', {}, 'Performance'),
|
||||
this.#summary,
|
||||
createDom('h3', {}, 'Slowest Specs'),
|
||||
createDom(
|
||||
'table',
|
||||
{},
|
||||
createDom(
|
||||
'thead',
|
||||
{},
|
||||
createDom(
|
||||
'tr',
|
||||
{},
|
||||
createDom('th', {}, 'Duration'),
|
||||
createDom('th', {}, 'Spec Name')
|
||||
)
|
||||
),
|
||||
this.#tbody
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
addResults(resultsTree) {
|
||||
const specResults = [];
|
||||
getSpecResults(resultsTree, specResults);
|
||||
|
||||
if (specResults.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
specResults.sort(function(a, b) {
|
||||
if (a.duration < b.duration) {
|
||||
return 1;
|
||||
} else if (a.duration > b.duration) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
this.#populateSumary(specResults);
|
||||
this.#populateTable(specResults);
|
||||
}
|
||||
|
||||
#populateSumary(specResults) {
|
||||
const total = specResults.map(r => r.duration).reduce((a, b) => a + b, 0);
|
||||
const mean = total / specResults.length;
|
||||
const median = specResults[Math.floor(specResults.length / 2)].duration;
|
||||
this.#summary.appendChild(
|
||||
document.createTextNode(`Mean spec run time: ${mean.toFixed(0)}ms`)
|
||||
);
|
||||
this.#summary.appendChild(document.createElement('br'));
|
||||
this.#summary.appendChild(
|
||||
document.createTextNode(`Median spec run time: ${median}ms`)
|
||||
);
|
||||
}
|
||||
|
||||
#populateTable(specResults) {
|
||||
specResults = specResults.slice(0, MAX_SLOW_SPECS);
|
||||
|
||||
for (const r of specResults) {
|
||||
this.#tbody.appendChild(
|
||||
createDom(
|
||||
'tr',
|
||||
{},
|
||||
createDom('td', {}, `${r.duration}ms`),
|
||||
createDom('td', {}, r.fullName)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSpecResults(resultsTree, dest) {
|
||||
for (const node of resultsTree.children) {
|
||||
if (node.type === 'suite') {
|
||||
getSpecResults(node, dest);
|
||||
} else if (node.result.status !== 'excluded') {
|
||||
dest.push(node.result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PerformanceView;
|
||||
};
|
||||
77
src/html/TabBar.js
Normal file
77
src/html/TabBar.js
Normal file
@@ -0,0 +1,77 @@
|
||||
jasmineRequire.TabBar = function(j$) {
|
||||
const createDom = j$.private.htmlReporterUtils.createDom;
|
||||
|
||||
class TabBar {
|
||||
#tabs;
|
||||
#onSelectTab;
|
||||
|
||||
// tabSpecs should be an array of {id, label}.
|
||||
// All tabs are initially not visible and not selected.
|
||||
constructor(tabSpecs, onSelectTab) {
|
||||
this.#onSelectTab = onSelectTab;
|
||||
this.#tabs = [];
|
||||
this.#tabs = tabSpecs.map(ts => new Tab(ts, () => this.selectTab(ts.id)));
|
||||
|
||||
this.rootEl = createDom(
|
||||
'span',
|
||||
{ className: 'jasmine-menu jasmine-bar' },
|
||||
this.#tabs.map(t => t.rootEl)
|
||||
);
|
||||
}
|
||||
|
||||
showTab(id) {
|
||||
for (const tab of this.#tabs) {
|
||||
if (tab.rootEl.id === id) {
|
||||
tab.setVisibility(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectTab(id) {
|
||||
for (const tab of this.#tabs) {
|
||||
tab.setSelected(tab.rootEl.id === id);
|
||||
}
|
||||
|
||||
this.#onSelectTab(id);
|
||||
}
|
||||
}
|
||||
|
||||
class Tab {
|
||||
#spec;
|
||||
#onClick;
|
||||
|
||||
constructor(spec, onClick) {
|
||||
this.#spec = spec;
|
||||
this.#onClick = onClick;
|
||||
this.rootEl = createDom(
|
||||
'span',
|
||||
{ id: spec.id, className: 'jasmine-tab jasmine-hidden' },
|
||||
this.#createLink()
|
||||
);
|
||||
}
|
||||
|
||||
setVisibility(visible) {
|
||||
this.rootEl.classList.toggle('jasmine-hidden', !visible);
|
||||
}
|
||||
|
||||
setSelected(selected) {
|
||||
if (selected) {
|
||||
this.rootEl.textContent = this.#spec.label;
|
||||
} else {
|
||||
this.rootEl.textContent = '';
|
||||
this.rootEl.appendChild(this.#createLink());
|
||||
}
|
||||
}
|
||||
|
||||
#createLink() {
|
||||
const link = createDom('a', { href: '#' }, this.#spec.label);
|
||||
link.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
this.#onClick();
|
||||
});
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
||||
return TabBar;
|
||||
};
|
||||
@@ -23,7 +23,7 @@ $failing-mark: "\d7";
|
||||
$pending-mark: "*";
|
||||
$space: "\0020";
|
||||
|
||||
$font-size: 11px;
|
||||
$font-size: 12px;
|
||||
$large-font-size: 14px;
|
||||
|
||||
body {
|
||||
@@ -280,15 +280,25 @@ body {
|
||||
}
|
||||
|
||||
// simplify toggle control between the two menu bars
|
||||
// TODO: clean this up once HtmlReporter is removed
|
||||
&.jasmine-spec-list {
|
||||
.jasmine-bar.jasmine-menu.jasmine-failure-list,
|
||||
.jasmine-results .jasmine-failures {
|
||||
.jasmine-results .jasmine-failures,
|
||||
.jasmine-performance-view {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.jasmine-failure-list {
|
||||
.jasmine-bar.jasmine-menu.jasmine-spec-list,
|
||||
.jasmine-summary,
|
||||
.jasmine-performance-view {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.jasmine-performance {
|
||||
.jasmine-results .jasmine-failures,
|
||||
.jasmine-summary {
|
||||
display: none;
|
||||
}
|
||||
@@ -464,3 +474,26 @@ body {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jasmine-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jasmine-tab + .jasmine-tab:before {
|
||||
content: ' | ';
|
||||
}
|
||||
|
||||
.jasmine-performance-view {
|
||||
h2, h3 {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
table {
|
||||
border-spacing: 5px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ jasmineRequire.htmlReporterUtils = function(j$) {
|
||||
const el = document.createElement(type);
|
||||
let children;
|
||||
|
||||
if (j$.private.isArray(childrenArrayOrVarArgs)) {
|
||||
if (Array.isArray(childrenArrayOrVarArgs)) {
|
||||
children = childrenArrayOrVarArgs;
|
||||
} else {
|
||||
children = [];
|
||||
|
||||
@@ -11,6 +11,8 @@ jasmineRequire.html = function(j$) {
|
||||
j$.private.SymbolsView = jasmineRequire.SymbolsView(j$);
|
||||
j$.private.SummaryTreeView = jasmineRequire.SummaryTreeView(j$);
|
||||
j$.private.FailuresView = jasmineRequire.FailuresView(j$);
|
||||
j$.private.PerformanceView = jasmineRequire.PerformanceView(j$);
|
||||
j$.private.TabBar = jasmineRequire.TabBar(j$);
|
||||
j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
|
||||
j$.HtmlReporterV2Urls = jasmineRequire.HtmlReporterV2Urls(j$);
|
||||
j$.HtmlReporterV2 = jasmineRequire.HtmlReporterV2(j$);
|
||||
|
||||
Reference in New Issue
Block a user