Move HtmlReporter components to their own files
This commit is contained in:
File diff suppressed because it is too large
Load Diff
208
src/html/AlertsView.js
Normal file
208
src/html/AlertsView.js
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
jasmineRequire.AlertsView = function(j$) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { createDom } = j$.private.htmlReporterUtils;
|
||||||
|
const errorBarClassName = 'jasmine-bar jasmine-errored';
|
||||||
|
const afterAllMessagePrefix = 'AfterAll ';
|
||||||
|
|
||||||
|
class AlertsView {
|
||||||
|
#urlBuilder;
|
||||||
|
|
||||||
|
constructor(urlBuilder) {
|
||||||
|
this.#urlBuilder = urlBuilder;
|
||||||
|
this.rootEl = createDom('div', { className: 'jasmine-alert' });
|
||||||
|
}
|
||||||
|
|
||||||
|
addDuration(ms) {
|
||||||
|
this.add('jasmine-duration', 'finished in ' + ms / 1000 + 's');
|
||||||
|
}
|
||||||
|
|
||||||
|
addSkipped(numExecuted, numDefined) {
|
||||||
|
// TODO: backfill tests for this
|
||||||
|
this.add(
|
||||||
|
'jasmine-bar jasmine-skipped',
|
||||||
|
createDom(
|
||||||
|
'a',
|
||||||
|
{ href: this.#urlBuilder.runAllHref(), title: 'Run all specs' },
|
||||||
|
`Ran ${numExecuted} of ${numDefined} specs - run all`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.add('jasmine-menu jasmine-bar jasmine-spec-list', [
|
||||||
|
createDom('span', {}, 'Spec List | '),
|
||||||
|
failuresLink
|
||||||
|
]);
|
||||||
|
this.add('jasmine-menu jasmine-bar jasmine-failure-list', [
|
||||||
|
specListLink,
|
||||||
|
createDom('span', {}, ' | Failures ')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
addGlobalFailure(failure) {
|
||||||
|
this.add(errorBarClassName, this.#globalFailureMessage(failure));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO check test coverage
|
||||||
|
addSeedBar(doneResult, stateBuilder, order) {
|
||||||
|
let statusBarMessage = '';
|
||||||
|
let statusBarClassName = 'jasmine-overall-result jasmine-bar ';
|
||||||
|
const globalFailures =
|
||||||
|
(doneResult && doneResult.failedExpectations) || [];
|
||||||
|
const failed = stateBuilder.failureCount + globalFailures.length > 0;
|
||||||
|
|
||||||
|
if (stateBuilder.totalSpecsDefined > 0 || failed) {
|
||||||
|
statusBarMessage +=
|
||||||
|
pluralize('spec', stateBuilder.specsExecuted) +
|
||||||
|
', ' +
|
||||||
|
pluralize('failure', stateBuilder.failureCount);
|
||||||
|
if (stateBuilder.pendingSpecCount) {
|
||||||
|
statusBarMessage +=
|
||||||
|
', ' + pluralize('pending spec', stateBuilder.pendingSpecCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doneResult.overallStatus === 'passed') {
|
||||||
|
statusBarClassName += ' jasmine-passed ';
|
||||||
|
} else if (doneResult.overallStatus === 'incomplete') {
|
||||||
|
statusBarClassName += ' jasmine-incomplete ';
|
||||||
|
statusBarMessage =
|
||||||
|
'Incomplete: ' +
|
||||||
|
doneResult.incompleteReason +
|
||||||
|
', ' +
|
||||||
|
statusBarMessage;
|
||||||
|
} else {
|
||||||
|
statusBarClassName += ' jasmine-failed ';
|
||||||
|
}
|
||||||
|
|
||||||
|
let seedBar;
|
||||||
|
if (order && order.random) {
|
||||||
|
seedBar = createDom(
|
||||||
|
'span',
|
||||||
|
{ className: 'jasmine-seed-bar' },
|
||||||
|
', randomized with seed ',
|
||||||
|
createDom(
|
||||||
|
'a',
|
||||||
|
{
|
||||||
|
title: 'randomized with seed ' + order.seed,
|
||||||
|
href: this.#urlBuilder.seedHref(order.seed)
|
||||||
|
},
|
||||||
|
order.seed
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.add(statusBarClassName, [statusBarMessage, seedBar]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO check test coverage
|
||||||
|
#globalFailureMessage(failure) {
|
||||||
|
if (failure.globalErrorType === 'load') {
|
||||||
|
const prefix = 'Error during loading: ' + failure.message;
|
||||||
|
|
||||||
|
if (failure.filename) {
|
||||||
|
return prefix + ' in ' + failure.filename + ' line ' + failure.lineno;
|
||||||
|
} else {
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
} else if (failure.globalErrorType === 'afterAll') {
|
||||||
|
return afterAllMessagePrefix + failure.message;
|
||||||
|
} else {
|
||||||
|
return failure.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addDeprecationWarning(dw) {
|
||||||
|
const children = [];
|
||||||
|
let context;
|
||||||
|
|
||||||
|
switch (dw.runnableType) {
|
||||||
|
case 'spec':
|
||||||
|
context = '(in spec: ' + dw.runnableName + ')';
|
||||||
|
break;
|
||||||
|
case 'suite':
|
||||||
|
context = '(in suite: ' + dw.runnableName + ')';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
context = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const line of dw.message.split('\n')) {
|
||||||
|
children.push(line);
|
||||||
|
children.push(createDom('br'));
|
||||||
|
}
|
||||||
|
|
||||||
|
children[0] = 'DEPRECATION: ' + children[0];
|
||||||
|
children.push(context);
|
||||||
|
|
||||||
|
if (dw.stack) {
|
||||||
|
children.push(this.#createExpander(dw.stack));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.add('jasmine-bar jasmine-warning', children);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO private?
|
||||||
|
add(className, children) {
|
||||||
|
this.rootEl.appendChild(createDom('span', { className }, children));
|
||||||
|
}
|
||||||
|
|
||||||
|
#createExpander(stackTrace) {
|
||||||
|
const expandLink = createDom('a', { href: '#' }, 'Show stack trace');
|
||||||
|
const root = createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-expander' },
|
||||||
|
expandLink,
|
||||||
|
createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-expander-contents jasmine-stack-trace' },
|
||||||
|
stackTrace
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
expandLink.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (root.classList.contains('jasmine-expanded')) {
|
||||||
|
root.classList.remove('jasmine-expanded');
|
||||||
|
expandLink.textContent = 'Show stack trace';
|
||||||
|
} else {
|
||||||
|
root.classList.add('jasmine-expanded');
|
||||||
|
expandLink.textContent = 'Hide stack trace';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pluralize(singular, count) {
|
||||||
|
const word = count == 1 ? singular : singular + 's';
|
||||||
|
|
||||||
|
return '' + count + ' ' + word;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AlertsView;
|
||||||
|
};
|
||||||
153
src/html/Banner.js
Normal file
153
src/html/Banner.js
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
jasmineRequire.Banner = function(j$) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { createDom } = j$.private.htmlReporterUtils;
|
||||||
|
|
||||||
|
class Banner {
|
||||||
|
#navigateWithNewParam;
|
||||||
|
|
||||||
|
constructor(navigateWithNewParam) {
|
||||||
|
this.#navigateWithNewParam = navigateWithNewParam;
|
||||||
|
this.rootEl = createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-banner' },
|
||||||
|
createDom('a', {
|
||||||
|
className: 'jasmine-title',
|
||||||
|
href: 'http://jasmine.github.io/',
|
||||||
|
target: '_blank'
|
||||||
|
}),
|
||||||
|
createDom('span', { className: 'jasmine-version' }, j$.version)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
showOptionsMenu(config) {
|
||||||
|
this.rootEl.appendChild(this.#optionsMenu(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
#optionsMenu(config) {
|
||||||
|
const optionsMenuDom = createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-run-options' },
|
||||||
|
createDom('span', { className: 'jasmine-trigger' }, 'Options'),
|
||||||
|
createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-payload' },
|
||||||
|
createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-stop-on-failure' },
|
||||||
|
createDom('input', {
|
||||||
|
className: 'jasmine-fail-fast',
|
||||||
|
id: 'jasmine-fail-fast',
|
||||||
|
type: 'checkbox'
|
||||||
|
}),
|
||||||
|
createDom(
|
||||||
|
'label',
|
||||||
|
{ className: 'jasmine-label', for: 'jasmine-fail-fast' },
|
||||||
|
'stop execution on spec failure'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-throw-failures' },
|
||||||
|
createDom('input', {
|
||||||
|
className: 'jasmine-throw',
|
||||||
|
id: 'jasmine-throw-failures',
|
||||||
|
type: 'checkbox'
|
||||||
|
}),
|
||||||
|
createDom(
|
||||||
|
'label',
|
||||||
|
{ className: 'jasmine-label', for: 'jasmine-throw-failures' },
|
||||||
|
'stop spec on expectation failure'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-random-order' },
|
||||||
|
createDom('input', {
|
||||||
|
className: 'jasmine-random',
|
||||||
|
id: 'jasmine-random-order',
|
||||||
|
type: 'checkbox'
|
||||||
|
}),
|
||||||
|
createDom(
|
||||||
|
'label',
|
||||||
|
{ className: 'jasmine-label', for: 'jasmine-random-order' },
|
||||||
|
'run tests in random order'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-hide-disabled' },
|
||||||
|
createDom('input', {
|
||||||
|
className: 'jasmine-disabled',
|
||||||
|
id: 'jasmine-hide-disabled',
|
||||||
|
type: 'checkbox'
|
||||||
|
}),
|
||||||
|
createDom(
|
||||||
|
'label',
|
||||||
|
{ className: 'jasmine-label', for: 'jasmine-hide-disabled' },
|
||||||
|
'hide disabled tests'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const failFastCheckbox = optionsMenuDom.querySelector(
|
||||||
|
'#jasmine-fail-fast'
|
||||||
|
);
|
||||||
|
failFastCheckbox.checked = config.stopOnSpecFailure;
|
||||||
|
failFastCheckbox.onclick = () => {
|
||||||
|
this.#navigateWithNewParam(
|
||||||
|
'stopOnSpecFailure',
|
||||||
|
!config.stopOnSpecFailure
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const throwCheckbox = optionsMenuDom.querySelector(
|
||||||
|
'#jasmine-throw-failures'
|
||||||
|
);
|
||||||
|
throwCheckbox.checked = config.stopSpecOnExpectationFailure;
|
||||||
|
throwCheckbox.onclick = () => {
|
||||||
|
this.#navigateWithNewParam(
|
||||||
|
'stopSpecOnExpectationFailure',
|
||||||
|
!config.stopSpecOnExpectationFailure
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const randomCheckbox = optionsMenuDom.querySelector(
|
||||||
|
'#jasmine-random-order'
|
||||||
|
);
|
||||||
|
randomCheckbox.checked = config.random;
|
||||||
|
randomCheckbox.onclick = () => {
|
||||||
|
this.#navigateWithNewParam('random', !config.random);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideDisabled = optionsMenuDom.querySelector(
|
||||||
|
'#jasmine-hide-disabled'
|
||||||
|
);
|
||||||
|
hideDisabled.checked = config.hideDisabled;
|
||||||
|
// TODO: backfill tests for this!
|
||||||
|
hideDisabled.onclick = () => {
|
||||||
|
this.#navigateWithNewParam('hideDisabled', !config.hideDisabled);
|
||||||
|
};
|
||||||
|
|
||||||
|
const optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'),
|
||||||
|
optionsPayload = optionsMenuDom.querySelector('.jasmine-payload'),
|
||||||
|
isOpen = /\bjasmine-open\b/;
|
||||||
|
|
||||||
|
optionsTrigger.onclick = function() {
|
||||||
|
if (isOpen.test(optionsPayload.className)) {
|
||||||
|
optionsPayload.className = optionsPayload.className.replace(
|
||||||
|
isOpen,
|
||||||
|
''
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
optionsPayload.className += ' jasmine-open';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return optionsMenuDom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Banner;
|
||||||
|
};
|
||||||
161
src/html/FailuresView.js
Normal file
161
src/html/FailuresView.js
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
jasmineRequire.FailuresView = function(j$) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { createDom } = j$.private.htmlReporterUtils;
|
||||||
|
|
||||||
|
class FailuresView {
|
||||||
|
#urlBuilder;
|
||||||
|
#failureEls;
|
||||||
|
|
||||||
|
constructor(urlBuilder) {
|
||||||
|
this.#urlBuilder = urlBuilder;
|
||||||
|
this.#failureEls = [];
|
||||||
|
this.rootEl = createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-results' },
|
||||||
|
createDom('div', { className: 'jasmine-failures' })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
append(result, parent) {
|
||||||
|
// TODO: Figure out why the reuslt is wrong if we build the DOM node later
|
||||||
|
this.#failureEls.push(this.#makeFailureEl(result, parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO move this to state builder or something
|
||||||
|
any() {
|
||||||
|
return this.#failureEls.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
const failureNode = this.rootEl.querySelector('.jasmine-failures');
|
||||||
|
|
||||||
|
for (const el of this.#failureEls) {
|
||||||
|
failureNode.appendChild(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#makeFailureEl(result, parent) {
|
||||||
|
const failure = createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-spec-detail jasmine-failed' },
|
||||||
|
this.#failureDescription(result, parent),
|
||||||
|
createDom('div', { className: 'jasmine-messages' })
|
||||||
|
);
|
||||||
|
const messages = failure.childNodes[1];
|
||||||
|
|
||||||
|
for (let i = 0; i < result.failedExpectations.length; i++) {
|
||||||
|
const expectation = result.failedExpectations[i];
|
||||||
|
messages.appendChild(
|
||||||
|
createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-result-message' },
|
||||||
|
expectation.message
|
||||||
|
)
|
||||||
|
);
|
||||||
|
messages.appendChild(
|
||||||
|
createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-stack-trace' },
|
||||||
|
expectation.stack
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.failedExpectations.length === 0) {
|
||||||
|
messages.appendChild(
|
||||||
|
createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-result-message' },
|
||||||
|
'Spec has no expectations'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.debugLogs) {
|
||||||
|
messages.appendChild(this.#debugLogTable(result.debugLogs));
|
||||||
|
}
|
||||||
|
|
||||||
|
return failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
#failureDescription(result, suite) {
|
||||||
|
const wrapper = createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-description' },
|
||||||
|
createDom(
|
||||||
|
'a',
|
||||||
|
{
|
||||||
|
title: result.description,
|
||||||
|
href: this.#urlBuilder.specHref(result)
|
||||||
|
},
|
||||||
|
result.description
|
||||||
|
)
|
||||||
|
);
|
||||||
|
let suiteLink;
|
||||||
|
|
||||||
|
while (suite && suite.parent) {
|
||||||
|
wrapper.insertBefore(
|
||||||
|
document.createTextNode(' > '),
|
||||||
|
wrapper.firstChild
|
||||||
|
);
|
||||||
|
suiteLink = createDom(
|
||||||
|
'a',
|
||||||
|
{ href: this.#urlBuilder.suiteHref(suite) },
|
||||||
|
suite.result.description
|
||||||
|
);
|
||||||
|
wrapper.insertBefore(suiteLink, wrapper.firstChild);
|
||||||
|
|
||||||
|
suite = suite.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
#debugLogTable(debugLogs) {
|
||||||
|
const tbody = createDom('tbody');
|
||||||
|
|
||||||
|
for (const entry of debugLogs) {
|
||||||
|
tbody.appendChild(
|
||||||
|
createDom(
|
||||||
|
'tr',
|
||||||
|
{},
|
||||||
|
createDom('td', {}, entry.timestamp.toString()),
|
||||||
|
createDom(
|
||||||
|
'td',
|
||||||
|
{ className: 'jasmine-debug-log-msg' },
|
||||||
|
entry.message
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-debug-log' },
|
||||||
|
createDom(
|
||||||
|
'div',
|
||||||
|
{ className: 'jasmine-debug-log-header' },
|
||||||
|
'Debug logs'
|
||||||
|
),
|
||||||
|
createDom(
|
||||||
|
'table',
|
||||||
|
{},
|
||||||
|
createDom(
|
||||||
|
'thead',
|
||||||
|
{},
|
||||||
|
createDom(
|
||||||
|
'tr',
|
||||||
|
{},
|
||||||
|
createDom('th', {}, 'Time (ms)'),
|
||||||
|
createDom('th', {}, 'Message')
|
||||||
|
)
|
||||||
|
),
|
||||||
|
tbody
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FailuresView;
|
||||||
|
};
|
||||||
@@ -1,80 +1,7 @@
|
|||||||
jasmineRequire.HtmlReporter = function(j$) {
|
jasmineRequire.HtmlReporter = function(j$) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
class ResultsStateBuilder {
|
const { createDom, noExpectations } = j$.private.htmlReporterUtils;
|
||||||
constructor() {
|
|
||||||
this.topResults = new j$.private.ResultsNode({}, '', null);
|
|
||||||
this.currentParent = this.topResults;
|
|
||||||
this.totalSpecsDefined = 0;
|
|
||||||
this.specsExecuted = 0;
|
|
||||||
this.failureCount = 0;
|
|
||||||
this.pendingSpecCount = 0;
|
|
||||||
this.deprecationWarnings = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
suiteStarted(result) {
|
|
||||||
this.currentParent.addChild(result, 'suite');
|
|
||||||
this.currentParent = this.currentParent.last();
|
|
||||||
}
|
|
||||||
|
|
||||||
suiteDone(result) {
|
|
||||||
this.currentParent.updateResult(result);
|
|
||||||
this.#addDeprecationWarnings(result, 'suite');
|
|
||||||
|
|
||||||
if (this.currentParent !== this.topResults) {
|
|
||||||
this.currentParent = this.currentParent.parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.status === 'failed') {
|
|
||||||
this.failureCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
specDone(result) {
|
|
||||||
this.currentParent.addChild(result, 'spec');
|
|
||||||
this.#addDeprecationWarnings(result, 'spec');
|
|
||||||
|
|
||||||
if (result.status !== 'excluded') {
|
|
||||||
this.specsExecuted++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.status === 'failed') {
|
|
||||||
this.failureCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.status == 'pending') {
|
|
||||||
this.pendingSpecCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jasmineStarted(result) {
|
|
||||||
this.totalSpecsDefined = result.totalSpecsDefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
jasmineDone(result) {
|
|
||||||
if (result.failedExpectations) {
|
|
||||||
this.failureCount += result.failedExpectations.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#addDeprecationWarnings(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
#addDeprecationWarnings(result, runnableType) {
|
|
||||||
if (result.deprecationWarnings) {
|
|
||||||
for (const dw of result.deprecationWarnings) {
|
|
||||||
this.deprecationWarnings.push({
|
|
||||||
message: dw.message,
|
|
||||||
stack: dw.stack,
|
|
||||||
runnableName: result.fullName,
|
|
||||||
runnableType: runnableType
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const errorBarClassName = 'jasmine-bar jasmine-errored';
|
|
||||||
const afterAllMessagePrefix = 'AfterAll ';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class HtmlReporter
|
* @class HtmlReporter
|
||||||
@@ -105,7 +32,7 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
this.#getContainer = options.getContainer;
|
this.#getContainer = options.getContainer;
|
||||||
this.#navigateWithNewParam =
|
this.#navigateWithNewParam =
|
||||||
options.navigateWithNewParam || function() {};
|
options.navigateWithNewParam || function() {};
|
||||||
this.#urlBuilder = new UrlBuilder(
|
this.#urlBuilder = new j$.private.UrlBuilder(
|
||||||
options.addToExistingQueryString || defaultQueryString
|
options.addToExistingQueryString || defaultQueryString
|
||||||
);
|
);
|
||||||
this.#filterSpecs = options.filterSpecs;
|
this.#filterSpecs = options.filterSpecs;
|
||||||
@@ -120,13 +47,13 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
this.#clearPrior();
|
this.#clearPrior();
|
||||||
this.#config = this.#env ? this.#env.configuration() : {};
|
this.#config = this.#env ? this.#env.configuration() : {};
|
||||||
|
|
||||||
this.#stateBuilder = new ResultsStateBuilder();
|
this.#stateBuilder = new j$.private.ResultsStateBuilder();
|
||||||
|
|
||||||
this.#alerts = new AlertsView(this.#urlBuilder);
|
this.#alerts = new j$.private.AlertsView(this.#urlBuilder);
|
||||||
this.#symbols = new SymbolsView();
|
this.#symbols = new j$.private.SymbolsView();
|
||||||
this.#banner = new Banner(this.#navigateWithNewParam);
|
this.#banner = new j$.private.Banner(this.#navigateWithNewParam);
|
||||||
this.#failures = new FailuresView(this.#urlBuilder);
|
this.#failures = new j$.private.FailuresView(this.#urlBuilder);
|
||||||
this.#htmlReporterMain = j$.private.createDom(
|
this.#htmlReporterMain = createDom(
|
||||||
'div',
|
'div',
|
||||||
{ className: 'jasmine_html-reporter' },
|
{ className: 'jasmine_html-reporter' },
|
||||||
this.#banner.rootEl,
|
this.#banner.rootEl,
|
||||||
@@ -202,7 +129,10 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const results = this.#find('.jasmine-results');
|
const results = this.#find('.jasmine-results');
|
||||||
const summary = new SummaryTreeView(this.#urlBuilder, this.#filterSpecs);
|
const summary = new j$.private.SummaryTreeView(
|
||||||
|
this.#urlBuilder,
|
||||||
|
this.#filterSpecs
|
||||||
|
);
|
||||||
summary.addResults(this.#stateBuilder.topResults);
|
summary.addResults(this.#stateBuilder.topResults);
|
||||||
results.appendChild(summary.rootEl);
|
results.appendChild(summary.rootEl);
|
||||||
|
|
||||||
@@ -239,701 +169,9 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasActiveSpec(resultNode) {
|
|
||||||
if (resultNode.type === 'spec' && resultNode.result.status !== 'excluded') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resultNode.type === 'suite') {
|
|
||||||
for (let i = 0, j = resultNode.children.length; i < j; i++) {
|
|
||||||
if (hasActiveSpec(resultNode.children[i])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function noExpectations(result) {
|
|
||||||
const allExpectations =
|
|
||||||
result.failedExpectations.length + result.passedExpectations.length;
|
|
||||||
|
|
||||||
return (
|
|
||||||
allExpectations === 0 &&
|
|
||||||
(result.status === 'passed' || result.status === 'failed')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function pluralize(singular, count) {
|
|
||||||
const word = count == 1 ? singular : singular + 's';
|
|
||||||
|
|
||||||
return '' + count + ' ' + word;
|
|
||||||
}
|
|
||||||
|
|
||||||
function defaultQueryString(key, value) {
|
function defaultQueryString(key, value) {
|
||||||
return '?' + key + '=' + value;
|
return '?' + key + '=' + value;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AlertsView {
|
|
||||||
#urlBuilder;
|
|
||||||
|
|
||||||
constructor(urlBuilder) {
|
|
||||||
this.#urlBuilder = urlBuilder;
|
|
||||||
this.rootEl = j$.private.createDom('div', { className: 'jasmine-alert' });
|
|
||||||
}
|
|
||||||
|
|
||||||
addDuration(ms) {
|
|
||||||
this.add('jasmine-duration', 'finished in ' + ms / 1000 + 's');
|
|
||||||
}
|
|
||||||
|
|
||||||
addSkipped(numExecuted, numDefined) {
|
|
||||||
// TODO: backfill tests for this
|
|
||||||
this.add(
|
|
||||||
'jasmine-bar jasmine-skipped',
|
|
||||||
j$.private.createDom(
|
|
||||||
'a',
|
|
||||||
{ href: this.#urlBuilder.runAllHref(), title: 'Run all specs' },
|
|
||||||
`Ran ${numExecuted} of ${numDefined} specs - run all`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
addFailureToggle(onClickFailures, onClickSpecList) {
|
|
||||||
const failuresLink = j$.private.createDom(
|
|
||||||
'a',
|
|
||||||
{ className: 'jasmine-failures-menu', href: '#' },
|
|
||||||
'Failures'
|
|
||||||
);
|
|
||||||
let specListLink = j$.private.createDom(
|
|
||||||
'a',
|
|
||||||
{ className: 'jasmine-spec-list-menu', href: '#' },
|
|
||||||
'Spec List'
|
|
||||||
);
|
|
||||||
|
|
||||||
failuresLink.onclick = function() {
|
|
||||||
onClickFailures();
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
specListLink.onclick = function() {
|
|
||||||
onClickSpecList();
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.add('jasmine-menu jasmine-bar jasmine-spec-list', [
|
|
||||||
j$.private.createDom('span', {}, 'Spec List | '),
|
|
||||||
failuresLink
|
|
||||||
]);
|
|
||||||
this.add('jasmine-menu jasmine-bar jasmine-failure-list', [
|
|
||||||
specListLink,
|
|
||||||
j$.private.createDom('span', {}, ' | Failures ')
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
addGlobalFailure(failure) {
|
|
||||||
this.add(errorBarClassName, this.#globalFailureMessage(failure));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO check test coverage
|
|
||||||
addSeedBar(doneResult, stateBuilder, order) {
|
|
||||||
let statusBarMessage = '';
|
|
||||||
let statusBarClassName = 'jasmine-overall-result jasmine-bar ';
|
|
||||||
const globalFailures =
|
|
||||||
(doneResult && doneResult.failedExpectations) || [];
|
|
||||||
const failed = stateBuilder.failureCount + globalFailures.length > 0;
|
|
||||||
|
|
||||||
if (stateBuilder.totalSpecsDefined > 0 || failed) {
|
|
||||||
statusBarMessage +=
|
|
||||||
pluralize('spec', stateBuilder.specsExecuted) +
|
|
||||||
', ' +
|
|
||||||
pluralize('failure', stateBuilder.failureCount);
|
|
||||||
if (stateBuilder.pendingSpecCount) {
|
|
||||||
statusBarMessage +=
|
|
||||||
', ' + pluralize('pending spec', stateBuilder.pendingSpecCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doneResult.overallStatus === 'passed') {
|
|
||||||
statusBarClassName += ' jasmine-passed ';
|
|
||||||
} else if (doneResult.overallStatus === 'incomplete') {
|
|
||||||
statusBarClassName += ' jasmine-incomplete ';
|
|
||||||
statusBarMessage =
|
|
||||||
'Incomplete: ' +
|
|
||||||
doneResult.incompleteReason +
|
|
||||||
', ' +
|
|
||||||
statusBarMessage;
|
|
||||||
} else {
|
|
||||||
statusBarClassName += ' jasmine-failed ';
|
|
||||||
}
|
|
||||||
|
|
||||||
let seedBar;
|
|
||||||
if (order && order.random) {
|
|
||||||
seedBar = j$.private.createDom(
|
|
||||||
'span',
|
|
||||||
{ className: 'jasmine-seed-bar' },
|
|
||||||
', randomized with seed ',
|
|
||||||
j$.private.createDom(
|
|
||||||
'a',
|
|
||||||
{
|
|
||||||
title: 'randomized with seed ' + order.seed,
|
|
||||||
href: this.#urlBuilder.seedHref(order.seed)
|
|
||||||
},
|
|
||||||
order.seed
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.add(statusBarClassName, [statusBarMessage, seedBar]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO check test coverage
|
|
||||||
#globalFailureMessage(failure) {
|
|
||||||
if (failure.globalErrorType === 'load') {
|
|
||||||
const prefix = 'Error during loading: ' + failure.message;
|
|
||||||
|
|
||||||
if (failure.filename) {
|
|
||||||
return prefix + ' in ' + failure.filename + ' line ' + failure.lineno;
|
|
||||||
} else {
|
|
||||||
return prefix;
|
|
||||||
}
|
|
||||||
} else if (failure.globalErrorType === 'afterAll') {
|
|
||||||
return afterAllMessagePrefix + failure.message;
|
|
||||||
} else {
|
|
||||||
return failure.message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addDeprecationWarning(dw) {
|
|
||||||
const children = [];
|
|
||||||
let context;
|
|
||||||
|
|
||||||
switch (dw.runnableType) {
|
|
||||||
case 'spec':
|
|
||||||
context = '(in spec: ' + dw.runnableName + ')';
|
|
||||||
break;
|
|
||||||
case 'suite':
|
|
||||||
context = '(in suite: ' + dw.runnableName + ')';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
context = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const line of dw.message.split('\n')) {
|
|
||||||
children.push(line);
|
|
||||||
children.push(j$.private.createDom('br'));
|
|
||||||
}
|
|
||||||
|
|
||||||
children[0] = 'DEPRECATION: ' + children[0];
|
|
||||||
children.push(context);
|
|
||||||
|
|
||||||
if (dw.stack) {
|
|
||||||
children.push(this.#createExpander(dw.stack));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.add('jasmine-bar jasmine-warning', children);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO private?
|
|
||||||
add(className, children) {
|
|
||||||
this.rootEl.appendChild(
|
|
||||||
j$.private.createDom('span', { className }, children)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#createExpander(stackTrace) {
|
|
||||||
const expandLink = j$.private.createDom(
|
|
||||||
'a',
|
|
||||||
{ href: '#' },
|
|
||||||
'Show stack trace'
|
|
||||||
);
|
|
||||||
const root = j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-expander' },
|
|
||||||
expandLink,
|
|
||||||
j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-expander-contents jasmine-stack-trace' },
|
|
||||||
stackTrace
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
expandLink.addEventListener('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if (root.classList.contains('jasmine-expanded')) {
|
|
||||||
root.classList.remove('jasmine-expanded');
|
|
||||||
expandLink.textContent = 'Show stack trace';
|
|
||||||
} else {
|
|
||||||
root.classList.add('jasmine-expanded');
|
|
||||||
expandLink.textContent = 'Hide stack trace';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Banner {
|
|
||||||
#navigateWithNewParam;
|
|
||||||
|
|
||||||
constructor(navigateWithNewParam) {
|
|
||||||
this.#navigateWithNewParam = navigateWithNewParam;
|
|
||||||
this.rootEl = j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-banner' },
|
|
||||||
j$.private.createDom('a', {
|
|
||||||
className: 'jasmine-title',
|
|
||||||
href: 'http://jasmine.github.io/',
|
|
||||||
target: '_blank'
|
|
||||||
}),
|
|
||||||
j$.private.createDom(
|
|
||||||
'span',
|
|
||||||
{ className: 'jasmine-version' },
|
|
||||||
j$.version
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
showOptionsMenu(config) {
|
|
||||||
this.rootEl.appendChild(this.#optionsMenu(config));
|
|
||||||
}
|
|
||||||
|
|
||||||
#optionsMenu(config) {
|
|
||||||
const optionsMenuDom = j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-run-options' },
|
|
||||||
j$.private.createDom(
|
|
||||||
'span',
|
|
||||||
{ className: 'jasmine-trigger' },
|
|
||||||
'Options'
|
|
||||||
),
|
|
||||||
j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-payload' },
|
|
||||||
j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-stop-on-failure' },
|
|
||||||
j$.private.createDom('input', {
|
|
||||||
className: 'jasmine-fail-fast',
|
|
||||||
id: 'jasmine-fail-fast',
|
|
||||||
type: 'checkbox'
|
|
||||||
}),
|
|
||||||
j$.private.createDom(
|
|
||||||
'label',
|
|
||||||
{ className: 'jasmine-label', for: 'jasmine-fail-fast' },
|
|
||||||
'stop execution on spec failure'
|
|
||||||
)
|
|
||||||
),
|
|
||||||
j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-throw-failures' },
|
|
||||||
j$.private.createDom('input', {
|
|
||||||
className: 'jasmine-throw',
|
|
||||||
id: 'jasmine-throw-failures',
|
|
||||||
type: 'checkbox'
|
|
||||||
}),
|
|
||||||
j$.private.createDom(
|
|
||||||
'label',
|
|
||||||
{ className: 'jasmine-label', for: 'jasmine-throw-failures' },
|
|
||||||
'stop spec on expectation failure'
|
|
||||||
)
|
|
||||||
),
|
|
||||||
j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-random-order' },
|
|
||||||
j$.private.createDom('input', {
|
|
||||||
className: 'jasmine-random',
|
|
||||||
id: 'jasmine-random-order',
|
|
||||||
type: 'checkbox'
|
|
||||||
}),
|
|
||||||
j$.private.createDom(
|
|
||||||
'label',
|
|
||||||
{ className: 'jasmine-label', for: 'jasmine-random-order' },
|
|
||||||
'run tests in random order'
|
|
||||||
)
|
|
||||||
),
|
|
||||||
j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-hide-disabled' },
|
|
||||||
j$.private.createDom('input', {
|
|
||||||
className: 'jasmine-disabled',
|
|
||||||
id: 'jasmine-hide-disabled',
|
|
||||||
type: 'checkbox'
|
|
||||||
}),
|
|
||||||
j$.private.createDom(
|
|
||||||
'label',
|
|
||||||
{ className: 'jasmine-label', for: 'jasmine-hide-disabled' },
|
|
||||||
'hide disabled tests'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const failFastCheckbox = optionsMenuDom.querySelector(
|
|
||||||
'#jasmine-fail-fast'
|
|
||||||
);
|
|
||||||
failFastCheckbox.checked = config.stopOnSpecFailure;
|
|
||||||
failFastCheckbox.onclick = () => {
|
|
||||||
this.#navigateWithNewParam(
|
|
||||||
'stopOnSpecFailure',
|
|
||||||
!config.stopOnSpecFailure
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const throwCheckbox = optionsMenuDom.querySelector(
|
|
||||||
'#jasmine-throw-failures'
|
|
||||||
);
|
|
||||||
throwCheckbox.checked = config.stopSpecOnExpectationFailure;
|
|
||||||
throwCheckbox.onclick = () => {
|
|
||||||
this.#navigateWithNewParam(
|
|
||||||
'stopSpecOnExpectationFailure',
|
|
||||||
!config.stopSpecOnExpectationFailure
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const randomCheckbox = optionsMenuDom.querySelector(
|
|
||||||
'#jasmine-random-order'
|
|
||||||
);
|
|
||||||
randomCheckbox.checked = config.random;
|
|
||||||
randomCheckbox.onclick = () => {
|
|
||||||
this.#navigateWithNewParam('random', !config.random);
|
|
||||||
};
|
|
||||||
|
|
||||||
const hideDisabled = optionsMenuDom.querySelector(
|
|
||||||
'#jasmine-hide-disabled'
|
|
||||||
);
|
|
||||||
hideDisabled.checked = config.hideDisabled;
|
|
||||||
// TODO: backfill tests for this!
|
|
||||||
hideDisabled.onclick = () => {
|
|
||||||
this.#navigateWithNewParam('hideDisabled', !config.hideDisabled);
|
|
||||||
};
|
|
||||||
|
|
||||||
const optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'),
|
|
||||||
optionsPayload = optionsMenuDom.querySelector('.jasmine-payload'),
|
|
||||||
isOpen = /\bjasmine-open\b/;
|
|
||||||
|
|
||||||
optionsTrigger.onclick = function() {
|
|
||||||
if (isOpen.test(optionsPayload.className)) {
|
|
||||||
optionsPayload.className = optionsPayload.className.replace(
|
|
||||||
isOpen,
|
|
||||||
''
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
optionsPayload.className += ' jasmine-open';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return optionsMenuDom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SymbolsView {
|
|
||||||
constructor() {
|
|
||||||
this.rootEl = j$.private.createDom('ul', {
|
|
||||||
className: 'jasmine-symbol-summary'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
append(result, config) {
|
|
||||||
this.rootEl.appendChild(
|
|
||||||
j$.private.createDom('li', {
|
|
||||||
className: this.#className(result, config),
|
|
||||||
id: 'spec_' + result.id,
|
|
||||||
title: result.fullName
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#className(result, config) {
|
|
||||||
if (noExpectations(result) && result.status === 'passed') {
|
|
||||||
return 'jasmine-empty';
|
|
||||||
} else if (result.status === 'excluded') {
|
|
||||||
if (config.hideDisabled) {
|
|
||||||
return 'jasmine-excluded-no-display';
|
|
||||||
} else {
|
|
||||||
return 'jasmine-excluded';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return 'jasmine-' + result.status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SummaryTreeView {
|
|
||||||
#urlBuilder;
|
|
||||||
#filterSpecs;
|
|
||||||
|
|
||||||
constructor(urlBuilder, filterSpecs) {
|
|
||||||
this.#urlBuilder = urlBuilder;
|
|
||||||
this.#filterSpecs = filterSpecs;
|
|
||||||
this.rootEl = j$.private.createDom('div', {
|
|
||||||
className: 'jasmine-summary'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addResults(resultsTree) {
|
|
||||||
this.#addResults(resultsTree, this.rootEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
#addResults(resultsTree, domParent) {
|
|
||||||
let specListNode;
|
|
||||||
for (let i = 0; i < resultsTree.children.length; i++) {
|
|
||||||
const resultNode = resultsTree.children[i];
|
|
||||||
if (this.#filterSpecs && !hasActiveSpec(resultNode)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (resultNode.type === 'suite') {
|
|
||||||
const suiteListNode = j$.private.createDom(
|
|
||||||
'ul',
|
|
||||||
{ className: 'jasmine-suite', id: 'suite-' + resultNode.result.id },
|
|
||||||
j$.private.createDom(
|
|
||||||
'li',
|
|
||||||
{
|
|
||||||
className:
|
|
||||||
'jasmine-suite-detail jasmine-' + resultNode.result.status
|
|
||||||
},
|
|
||||||
j$.private.createDom(
|
|
||||||
'a',
|
|
||||||
{ href: this.#urlBuilder.specHref(resultNode.result) },
|
|
||||||
resultNode.result.description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.#addResults(resultNode, suiteListNode);
|
|
||||||
domParent.appendChild(suiteListNode);
|
|
||||||
}
|
|
||||||
if (resultNode.type === 'spec') {
|
|
||||||
if (domParent.getAttribute('class') !== 'jasmine-specs') {
|
|
||||||
specListNode = j$.private.createDom('ul', {
|
|
||||||
className: 'jasmine-specs'
|
|
||||||
});
|
|
||||||
domParent.appendChild(specListNode);
|
|
||||||
}
|
|
||||||
let specDescription = resultNode.result.description;
|
|
||||||
if (noExpectations(resultNode.result)) {
|
|
||||||
specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
|
|
||||||
}
|
|
||||||
if (resultNode.result.status === 'pending') {
|
|
||||||
if (resultNode.result.pendingReason !== '') {
|
|
||||||
specDescription +=
|
|
||||||
' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
|
|
||||||
} else {
|
|
||||||
specDescription += ' PENDING';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
specListNode.appendChild(
|
|
||||||
j$.private.createDom(
|
|
||||||
'li',
|
|
||||||
{
|
|
||||||
className: 'jasmine-' + resultNode.result.status,
|
|
||||||
id: 'spec-' + resultNode.result.id
|
|
||||||
},
|
|
||||||
j$.private.createDom(
|
|
||||||
'a',
|
|
||||||
{ href: this.#urlBuilder.specHref(resultNode.result) },
|
|
||||||
specDescription
|
|
||||||
),
|
|
||||||
j$.private.createDom(
|
|
||||||
'span',
|
|
||||||
{ className: 'jasmine-spec-duration' },
|
|
||||||
'(' + resultNode.result.duration + 'ms)'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FailuresView {
|
|
||||||
#urlBuilder;
|
|
||||||
#failureEls;
|
|
||||||
|
|
||||||
constructor(urlBuilder) {
|
|
||||||
this.#urlBuilder = urlBuilder;
|
|
||||||
this.#failureEls = [];
|
|
||||||
this.rootEl = j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-results' },
|
|
||||||
j$.private.createDom('div', { className: 'jasmine-failures' })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
append(result, parent) {
|
|
||||||
// TODO: Figure out why the reuslt is wrong if we build the DOM node later
|
|
||||||
this.#failureEls.push(this.#makeFailureEl(result, parent));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO move this to state builder or something
|
|
||||||
any() {
|
|
||||||
return this.#failureEls.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
show() {
|
|
||||||
const failureNode = this.rootEl.querySelector('.jasmine-failures');
|
|
||||||
|
|
||||||
for (const el of this.#failureEls) {
|
|
||||||
failureNode.appendChild(el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#makeFailureEl(result, parent) {
|
|
||||||
const failure = j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-spec-detail jasmine-failed' },
|
|
||||||
this.#failureDescription(result, parent),
|
|
||||||
j$.private.createDom('div', { className: 'jasmine-messages' })
|
|
||||||
);
|
|
||||||
const messages = failure.childNodes[1];
|
|
||||||
|
|
||||||
for (let i = 0; i < result.failedExpectations.length; i++) {
|
|
||||||
const expectation = result.failedExpectations[i];
|
|
||||||
messages.appendChild(
|
|
||||||
j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-result-message' },
|
|
||||||
expectation.message
|
|
||||||
)
|
|
||||||
);
|
|
||||||
messages.appendChild(
|
|
||||||
j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-stack-trace' },
|
|
||||||
expectation.stack
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.failedExpectations.length === 0) {
|
|
||||||
messages.appendChild(
|
|
||||||
j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-result-message' },
|
|
||||||
'Spec has no expectations'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.debugLogs) {
|
|
||||||
messages.appendChild(this.#debugLogTable(result.debugLogs));
|
|
||||||
}
|
|
||||||
|
|
||||||
return failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
#failureDescription(result, suite) {
|
|
||||||
const wrapper = j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-description' },
|
|
||||||
j$.private.createDom(
|
|
||||||
'a',
|
|
||||||
{
|
|
||||||
title: result.description,
|
|
||||||
href: this.#urlBuilder.specHref(result)
|
|
||||||
},
|
|
||||||
result.description
|
|
||||||
)
|
|
||||||
);
|
|
||||||
let suiteLink;
|
|
||||||
|
|
||||||
while (suite && suite.parent) {
|
|
||||||
wrapper.insertBefore(
|
|
||||||
document.createTextNode(' > '),
|
|
||||||
wrapper.firstChild
|
|
||||||
);
|
|
||||||
suiteLink = j$.private.createDom(
|
|
||||||
'a',
|
|
||||||
{ href: this.#urlBuilder.suiteHref(suite) },
|
|
||||||
suite.result.description
|
|
||||||
);
|
|
||||||
wrapper.insertBefore(suiteLink, wrapper.firstChild);
|
|
||||||
|
|
||||||
suite = suite.parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
#debugLogTable(debugLogs) {
|
|
||||||
const tbody = j$.private.createDom('tbody');
|
|
||||||
|
|
||||||
for (const entry of debugLogs) {
|
|
||||||
tbody.appendChild(
|
|
||||||
j$.private.createDom(
|
|
||||||
'tr',
|
|
||||||
{},
|
|
||||||
j$.private.createDom('td', {}, entry.timestamp.toString()),
|
|
||||||
j$.private.createDom(
|
|
||||||
'td',
|
|
||||||
{ className: 'jasmine-debug-log-msg' },
|
|
||||||
entry.message
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-debug-log' },
|
|
||||||
j$.private.createDom(
|
|
||||||
'div',
|
|
||||||
{ className: 'jasmine-debug-log-header' },
|
|
||||||
'Debug logs'
|
|
||||||
),
|
|
||||||
j$.private.createDom(
|
|
||||||
'table',
|
|
||||||
{},
|
|
||||||
j$.private.createDom(
|
|
||||||
'thead',
|
|
||||||
{},
|
|
||||||
j$.private.createDom(
|
|
||||||
'tr',
|
|
||||||
{},
|
|
||||||
j$.private.createDom('th', {}, 'Time (ms)'),
|
|
||||||
j$.private.createDom('th', {}, 'Message')
|
|
||||||
)
|
|
||||||
),
|
|
||||||
tbody
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UrlBuilder {
|
|
||||||
#addToExistingQueryString;
|
|
||||||
|
|
||||||
constructor(addToExistingQueryString) {
|
|
||||||
this.#addToExistingQueryString = function(k, v) {
|
|
||||||
// include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906
|
|
||||||
return (
|
|
||||||
(window.location.pathname || '') + addToExistingQueryString(k, v)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
suiteHref(suite) {
|
|
||||||
const els = [];
|
|
||||||
|
|
||||||
while (suite && suite.parent) {
|
|
||||||
els.unshift(suite.result.description);
|
|
||||||
suite = suite.parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.#addToExistingQueryString('spec', els.join(' '));
|
|
||||||
}
|
|
||||||
|
|
||||||
specHref(result) {
|
|
||||||
return this.#addToExistingQueryString('spec', result.fullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
runAllHref() {
|
|
||||||
return this.#addToExistingQueryString('spec', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
seedHref(seed) {
|
|
||||||
return this.#addToExistingQueryString('seed', seed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return HtmlReporter;
|
return HtmlReporter;
|
||||||
};
|
};
|
||||||
|
|||||||
77
src/html/ResultsStateBuilder.js
Normal file
77
src/html/ResultsStateBuilder.js
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
jasmineRequire.ResultsStateBuilder = function(j$) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class ResultsStateBuilder {
|
||||||
|
constructor() {
|
||||||
|
this.topResults = new j$.private.ResultsNode({}, '', null);
|
||||||
|
this.currentParent = this.topResults;
|
||||||
|
this.totalSpecsDefined = 0;
|
||||||
|
this.specsExecuted = 0;
|
||||||
|
this.failureCount = 0;
|
||||||
|
this.pendingSpecCount = 0;
|
||||||
|
this.deprecationWarnings = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
suiteStarted(result) {
|
||||||
|
this.currentParent.addChild(result, 'suite');
|
||||||
|
this.currentParent = this.currentParent.last();
|
||||||
|
}
|
||||||
|
|
||||||
|
suiteDone(result) {
|
||||||
|
this.currentParent.updateResult(result);
|
||||||
|
this.#addDeprecationWarnings(result, 'suite');
|
||||||
|
|
||||||
|
if (this.currentParent !== this.topResults) {
|
||||||
|
this.currentParent = this.currentParent.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status === 'failed') {
|
||||||
|
this.failureCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
specDone(result) {
|
||||||
|
this.currentParent.addChild(result, 'spec');
|
||||||
|
this.#addDeprecationWarnings(result, 'spec');
|
||||||
|
|
||||||
|
if (result.status !== 'excluded') {
|
||||||
|
this.specsExecuted++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status === 'failed') {
|
||||||
|
this.failureCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status == 'pending') {
|
||||||
|
this.pendingSpecCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jasmineStarted(result) {
|
||||||
|
this.totalSpecsDefined = result.totalSpecsDefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
jasmineDone(result) {
|
||||||
|
if (result.failedExpectations) {
|
||||||
|
this.failureCount += result.failedExpectations.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#addDeprecationWarnings(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#addDeprecationWarnings(result, runnableType) {
|
||||||
|
if (result.deprecationWarnings) {
|
||||||
|
for (const dw of result.deprecationWarnings) {
|
||||||
|
this.deprecationWarnings.push({
|
||||||
|
message: dw.message,
|
||||||
|
stack: dw.stack,
|
||||||
|
runnableName: result.fullName,
|
||||||
|
runnableType: runnableType
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultsStateBuilder;
|
||||||
|
};
|
||||||
108
src/html/SummaryTreeView.js
Normal file
108
src/html/SummaryTreeView.js
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
jasmineRequire.SummaryTreeView = function(j$) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { createDom, noExpectations } = j$.private.htmlReporterUtils;
|
||||||
|
|
||||||
|
class SummaryTreeView {
|
||||||
|
#urlBuilder;
|
||||||
|
#filterSpecs;
|
||||||
|
|
||||||
|
constructor(urlBuilder, filterSpecs) {
|
||||||
|
this.#urlBuilder = urlBuilder;
|
||||||
|
this.#filterSpecs = filterSpecs;
|
||||||
|
this.rootEl = createDom('div', {
|
||||||
|
className: 'jasmine-summary'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addResults(resultsTree) {
|
||||||
|
this.#addResults(resultsTree, this.rootEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
#addResults(resultsTree, domParent) {
|
||||||
|
let specListNode;
|
||||||
|
for (let i = 0; i < resultsTree.children.length; i++) {
|
||||||
|
const resultNode = resultsTree.children[i];
|
||||||
|
if (this.#filterSpecs && !hasActiveSpec(resultNode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (resultNode.type === 'suite') {
|
||||||
|
const suiteListNode = createDom(
|
||||||
|
'ul',
|
||||||
|
{ className: 'jasmine-suite', id: 'suite-' + resultNode.result.id },
|
||||||
|
createDom(
|
||||||
|
'li',
|
||||||
|
{
|
||||||
|
className:
|
||||||
|
'jasmine-suite-detail jasmine-' + resultNode.result.status
|
||||||
|
},
|
||||||
|
createDom(
|
||||||
|
'a',
|
||||||
|
{ href: this.#urlBuilder.specHref(resultNode.result) },
|
||||||
|
resultNode.result.description
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.#addResults(resultNode, suiteListNode);
|
||||||
|
domParent.appendChild(suiteListNode);
|
||||||
|
}
|
||||||
|
if (resultNode.type === 'spec') {
|
||||||
|
if (domParent.getAttribute('class') !== 'jasmine-specs') {
|
||||||
|
specListNode = createDom('ul', {
|
||||||
|
className: 'jasmine-specs'
|
||||||
|
});
|
||||||
|
domParent.appendChild(specListNode);
|
||||||
|
}
|
||||||
|
let specDescription = resultNode.result.description;
|
||||||
|
if (noExpectations(resultNode.result)) {
|
||||||
|
specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
|
||||||
|
}
|
||||||
|
if (resultNode.result.status === 'pending') {
|
||||||
|
if (resultNode.result.pendingReason !== '') {
|
||||||
|
specDescription +=
|
||||||
|
' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
|
||||||
|
} else {
|
||||||
|
specDescription += ' PENDING';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
specListNode.appendChild(
|
||||||
|
createDom(
|
||||||
|
'li',
|
||||||
|
{
|
||||||
|
className: 'jasmine-' + resultNode.result.status,
|
||||||
|
id: 'spec-' + resultNode.result.id
|
||||||
|
},
|
||||||
|
createDom(
|
||||||
|
'a',
|
||||||
|
{ href: this.#urlBuilder.specHref(resultNode.result) },
|
||||||
|
specDescription
|
||||||
|
),
|
||||||
|
createDom(
|
||||||
|
'span',
|
||||||
|
{ className: 'jasmine-spec-duration' },
|
||||||
|
'(' + resultNode.result.duration + 'ms)'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasActiveSpec(resultNode) {
|
||||||
|
if (resultNode.type === 'spec' && resultNode.result.status !== 'excluded') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultNode.type === 'suite') {
|
||||||
|
for (let i = 0, j = resultNode.children.length; i < j; i++) {
|
||||||
|
if (hasActiveSpec(resultNode.children[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SummaryTreeView;
|
||||||
|
};
|
||||||
39
src/html/SymbolsView.js
Normal file
39
src/html/SymbolsView.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
jasmineRequire.SymbolsView = function(j$) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { createDom, noExpectations } = j$.private.htmlReporterUtils;
|
||||||
|
|
||||||
|
class SymbolsView {
|
||||||
|
constructor() {
|
||||||
|
this.rootEl = createDom('ul', {
|
||||||
|
className: 'jasmine-symbol-summary'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
append(result, config) {
|
||||||
|
this.rootEl.appendChild(
|
||||||
|
createDom('li', {
|
||||||
|
className: this.#className(result, config),
|
||||||
|
id: 'spec_' + result.id,
|
||||||
|
title: result.fullName
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#className(result, config) {
|
||||||
|
if (noExpectations(result) && result.status === 'passed') {
|
||||||
|
return 'jasmine-empty';
|
||||||
|
} else if (result.status === 'excluded') {
|
||||||
|
if (config.hideDisabled) {
|
||||||
|
return 'jasmine-excluded-no-display';
|
||||||
|
} else {
|
||||||
|
return 'jasmine-excluded';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 'jasmine-' + result.status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SymbolsView;
|
||||||
|
};
|
||||||
41
src/html/UrlBuilder.js
Normal file
41
src/html/UrlBuilder.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
jasmineRequire.UrlBuilder = function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class UrlBuilder {
|
||||||
|
#addToExistingQueryString;
|
||||||
|
|
||||||
|
constructor(addToExistingQueryString) {
|
||||||
|
this.#addToExistingQueryString = function(k, v) {
|
||||||
|
// include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906
|
||||||
|
return (
|
||||||
|
(window.location.pathname || '') + addToExistingQueryString(k, v)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
suiteHref(suite) {
|
||||||
|
const els = [];
|
||||||
|
|
||||||
|
while (suite && suite.parent) {
|
||||||
|
els.unshift(suite.result.description);
|
||||||
|
suite = suite.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.#addToExistingQueryString('spec', els.join(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
specHref(result) {
|
||||||
|
return this.#addToExistingQueryString('spec', result.fullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
runAllHref() {
|
||||||
|
return this.#addToExistingQueryString('spec', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
seedHref(seed) {
|
||||||
|
return this.#addToExistingQueryString('seed', seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UrlBuilder;
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
jasmineRequire.createDom = function(j$) {
|
jasmineRequire.htmlReporterUtils = function(j$) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function createDom(type, attrs, childrenArrayOrVarArgs) {
|
function createDom(type, attrs, childrenArrayOrVarArgs) {
|
||||||
@@ -38,5 +38,15 @@ jasmineRequire.createDom = function(j$) {
|
|||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
return createDom;
|
function noExpectations(result) {
|
||||||
|
const allExpectations =
|
||||||
|
result.failedExpectations.length + result.passedExpectations.length;
|
||||||
|
|
||||||
|
return (
|
||||||
|
allExpectations === 0 &&
|
||||||
|
(result.status === 'passed' || result.status === 'failed')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { createDom, noExpectations };
|
||||||
};
|
};
|
||||||
@@ -3,7 +3,14 @@ var jasmineRequire = window.jasmineRequire || require('./jasmine.js');
|
|||||||
|
|
||||||
jasmineRequire.html = function(j$) {
|
jasmineRequire.html = function(j$) {
|
||||||
j$.private.ResultsNode = jasmineRequire.ResultsNode();
|
j$.private.ResultsNode = jasmineRequire.ResultsNode();
|
||||||
j$.private.createDom = jasmineRequire.createDom(j$);
|
j$.private.ResultsStateBuilder = jasmineRequire.ResultsStateBuilder(j$);
|
||||||
|
j$.private.htmlReporterUtils = jasmineRequire.htmlReporterUtils(j$);
|
||||||
|
j$.private.AlertsView = jasmineRequire.AlertsView(j$);
|
||||||
|
j$.private.Banner = jasmineRequire.Banner(j$);
|
||||||
|
j$.private.SymbolsView = jasmineRequire.SymbolsView(j$);
|
||||||
|
j$.private.SummaryTreeView = jasmineRequire.SummaryTreeView(j$);
|
||||||
|
j$.private.FailuresView = jasmineRequire.FailuresView(j$);
|
||||||
|
j$.private.UrlBuilder = jasmineRequire.UrlBuilder();
|
||||||
j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
|
j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
|
||||||
j$.QueryString = jasmineRequire.QueryString();
|
j$.QueryString = jasmineRequire.QueryString();
|
||||||
j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
|
j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
|
||||||
|
|||||||
Reference in New Issue
Block a user