jgiven-html-app
Version:
The HTML App of JGiven and JsGiven
759 lines (633 loc) • 23.1 kB
JavaScript
/**
* Main controller
*/
import { getTagKey,
getTagId,
nanosToReadableUnit,
getWordValue,
tagToString,
sortByDescription,
getTagName,
splitClassName,
deselectAll,
getArgumentInfos,
replaceArguments,
getThumbnailPath,
isImageType} from '../util.js'
jgivenReportApp.controller('JGivenReportCtrl', function ($scope, $rootScope, $document, $timeout, $sanitize, $location, $window, localStorageService,
dataService, tagService, classService, searchService, optionService) {
/**
* The list of all scenarios theoretically visible
*/
var scenarios = [];
/**
* Scenarios after an options filter has been applied
*/
var filteredScenarios = [];
$scope.currentPage = {};
$scope.nav = {};
$scope.bookmarks = [];
$scope.totalStatistics = undefined;
$scope.metaData = dataService.getMetaData();
// Pager
$scope.totalItems = 0;
$scope.currentPageNr = 1;
$scope.itemsPerPage = 0;
$scope.showOptions = true;
$scope.customNavigationLinks = dataService.getCustomNavigationLinks();
$scope.init = function () {
$scope.bookmarks = localStorageService.get('bookmarks') || [];
$scope.$watch('bookmarks', function () {
localStorageService.set('bookmarks', $scope.bookmarks);
}, true);
$scope.showSummaryPage();
};
var getAllScenarios = dataService.getAllScenarios;
$scope.showSummaryPage = function () {
$scope.currentPage = {
title: "Welcome",
breadcrumbs: [''],
scenarios: [],
groupedScenarios: [],
statistics: $scope.getTotalStatistics(),
summary: true
};
};
$scope.$on('$locationChangeSuccess', function (event) {
if ($scope.updatingLocation) {
$scope.updatingLocation = false;
return;
}
var search = $location.search();
var selectedOptions = optionService.getOptionsFromSearch(search);
var part = $location.path().split('/');
console.log("Location change: " + part);
$scope.currentPageNr = selectedOptions.page;
$scope.itemsPerPage = selectedOptions.itemsPerPage;
$scope.pagination = false;
$scope.showOptions = false;
if (part[1] === '') {
$scope.showSummaryPage();
} else if (part[1] === 'tag') {
var tag = tagService.getTagByKey(getTagKey({
name: part[2],
value: part[3]
}));
if (tag) {
$scope.updateCurrentPageToTag(tag, selectedOptions);
} else {
var tagNameNode = tagService.getTagNameNode(part[2]);
$scope.updateCurrentPageToTagNameNode(tagNameNode, selectedOptions);
}
} else if (part[1] === 'tagid') {
var tag = tagService.getTagByKey(getTagKey({
fullType: part[2],
value: part[3]
}));
$scope.updateCurrentPageToTag(tag, selectedOptions);
} else if (part[1] === 'class') {
$scope.updateCurrentPageToClassName(part[2], selectedOptions);
} else if (part[1] === 'package') {
$scope.updateCurrentPageToPackage(part[2], selectedOptions);
} else if (part[1] === 'scenario') {
$scope.showScenario(part[2], part[3], selectedOptions);
} else if (part[1] === 'all') {
$scope.showAllScenarios(selectedOptions);
} else if (part[1] === 'failed') {
$scope.showFailedScenarios(selectedOptions);
} else if (part[1] === 'pending') {
$scope.showPendingScenarios(selectedOptions);
} else if (part[1] === 'aborted') {
$scope.showAbortedScenarios(selectedOptions);
} else if (part[1] === 'search') {
$scope.search(part[2], selectedOptions);
}
$scope.currentPage.embed = search.embed;
$scope.currentPage.print = search.print;
});
$scope.setPage = function (pageNo) {
$scope.currentPageNr = pageNo;
$scope.applyOptionalPagination();
};
$scope.getTotalStatistics = function () {
if (!$scope.totalStatistics) {
$scope.totalStatistics = $scope.gatherStatistics(getAllScenarios());
}
return $scope.totalStatistics;
};
$scope.toggleBookmark = function () {
if ($scope.isBookmarked()) {
$scope.removeCurrentBookmark();
} else {
var name = $scope.currentPage.title;
if (name === 'Search Results') {
name = $scope.currentPage.description;
}
$scope.bookmarks.push({
name: name,
url: $window.location.hash,
search: $window.location.search
});
}
};
$scope.removeCurrentBookmark = function () {
$scope.removeBookmark($scope.findBookmarkIndex());
};
$scope.removeBookmark = function (index) {
$scope.bookmarks.splice(index, 1);
};
$scope.isBookmarked = function () {
return $scope.findBookmarkIndex() !== -1;
};
$scope.findBookmarkIndex = function () {
for (var i = 0; i < $scope.bookmarks.length; i++) {
if ($scope.bookmarks[i].url === $location.path()) {
return i;
}
}
return -1;
};
$scope.toggleTreeNode = function toggleTreeNode (node) {
node.expanded = !node.expanded;
// recursively open all packages that only have a single subpackage
if (node.leafs().length === 0 && node.childNodes().length === 1) {
toggleTreeNode(node.childNodes()[0]);
}
};
$scope.currentPath = function () {
return $location.path();
};
$scope.updateCurrentPageToClassName = function (className, options) {
$scope.updateCurrentPageToTestCase(classService.getTestCaseByClassName(className), options);
};
$scope.updateCurrentPageToPackage = function (packageName, options) {
$scope.currentPage = {
scenarios: [],
subtitle: "Package",
title: packageName,
breadcrumbs: packageName.split("."),
loading: true
};
$timeout(function () {
scenarios = classService.getScenariosOfPackage(packageName);
$scope.currentPage.loading = false;
$scope.currentPage.options = optionService.getOptions(scenarios, options);
$scope.applyOptions();
}, 0);
};
$scope.updateCurrentPageToTestCase = function (testCase, options) {
var className = splitClassName(testCase.className);
scenarios = sortByDescription(testCase.scenarios);
$scope.currentPage = {
subtitle: className.packageName + (testCase.name ? '.' + className.className : ''),
title: testCase.name ? testCase.name : className.className,
description: testCase.description,
breadcrumbs: className.packageName.split("."),
options: optionService.getOptions(scenarios, options)
};
$scope.applyOptions();
};
$scope.updateCurrentPageToTag = function (tag, options) {
var key = getTagKey(tag);
scenarios = sortByDescription(tagService.getScenariosByTag(tag));
console.log("Update current page to tag " + key);
$scope.currentPage = {
title: tag.value ? (tag.prependType ? getTagName(tag) + '-' : '') + tag.value : getTagName(tag),
subtitle: tag.value && !tag.prependType ? getTagName(tag) : undefined,
description: tag.description,
breadcrumbs: ['TAGS', getTagName(tag), tag.value],
options: optionService.getOptions(scenarios, options)
};
$scope.applyOptions();
};
$scope.updateCurrentPageToTagNameNode = function (tagNameNode, options) {
scenarios = sortByDescription(tagNameNode.scenarios());
$scope.currentPage = {
title: tagNameNode.nodeName(),
description: "",
breadcrumbs: ['TAGS', tagNameNode.nodeName()],
options: optionService.getOptions(scenarios, options)
};
$scope.applyOptions();
};
$scope.showScenario = function (className, methodName, options) {
scenarios = sortByDescription(_.filter(classService.getTestCaseByClassName(className).scenarios, function (x) {
return x.testMethodName === methodName;
}));
$scope.currentPage = {
title: scenarios[0].description.capitalize(),
subtitle: className,
description: scenarios[0].extendedDescription,
breadcrumbs: ['SCENARIO'].concat(className.split('.')).concat([methodName]),
options: optionService.getOptions(scenarios, options)
};
$scope.applyOptions();
};
$scope.showAllScenarios = function (options) {
$scope.currentPage = {
scenarios: [],
title: 'All Scenarios',
breadcrumbs: ['ALL SCENARIOS'],
loading: true
};
$timeout(function () {
scenarios = sortByDescription(getAllScenarios());
$scope.currentPage.loading = false;
$scope.currentPage.options = optionService.getOptions(scenarios, options);
$scope.applyOptions();
}, 0);
};
$scope.showPendingScenarios = function (options) {
scenarios = dataService.getPendingScenarios();
var description = getDescription(scenarios.length, "pending");
$scope.currentPage = {
title: "Pending Scenarios",
description: description,
breadcrumbs: ['PENDING SCENARIOS'],
options: optionService.getOptions(scenarios, options)
};
$scope.applyOptions();
};
$scope.showAbortedScenarios = function (options) {
scenarios = dataService.getAbortedScenarios();
var description = getDescription(scenarios.length, "aborted");
$scope.currentPage = {
title: "Aborted Scenarios",
description: description,
breadcrumbs: ['ABORTED SCENARIOS'],
options: optionService.getOptions(scenarios, options)
};
$scope.applyOptions();
};
$scope.applyOptions = function applyOptions () {
var page = $scope.currentPage;
var selectedSortOption = getSelectedSortOption(page);
filteredScenarios = selectedSortOption.apply(
_.filter(scenarios, getFilterFunction(page)));
$scope.showOptions = scenarios.length > 1;
$scope.applyOptionalPagination();
};
$scope.applyOptionalPagination = function () {
var page = $scope.currentPage;
$scope.totalItems = filteredScenarios.length;
var scenariosOfCurrentPage = filteredScenarios;
var groupOption = getSelectedGroupOption(page);
if (groupOption.name === 'None' && $scope.totalItems > $scope.itemsPerPage) {
$scope.pagination = true;
scenariosOfCurrentPage = $scope.applyPagination(filteredScenarios);
} else {
$scope.pagination = false;
}
console.time("Applying group option");
page.groupedScenarios = groupOption.apply(scenariosOfCurrentPage);
console.timeEnd("Applying group option");
page.statistics = $scope.gatherStatistics(filteredScenarios);
page.filtered = scenarios.length - filteredScenarios.length;
$scope.updateLocationSearchOptions();
};
$scope.applyPagination = function applyPagination (filteredScenarios) {
var start = ($scope.currentPageNr - 1) * $scope.itemsPerPage;
return filteredScenarios.slice(start, start + $scope.itemsPerPage);
};
$scope.updateLocationSearchOptions = function updateLocationSearchOptions () {
$scope.updatingLocation = true;
var selectedSortOption = getSelectedSortOption($scope.currentPage);
$location.search('sort', selectedSortOption.default ? null : selectedSortOption.id);
var selectedGroupOption = getSelectedGroupOption($scope.currentPage);
$location.search('group', selectedGroupOption.default ? null : selectedGroupOption.id);
var selectedTags = getSelectedOptions($scope.currentPage.options.tagOptions);
$location.search('tags', selectedTags.length > 0 ? _.map(selectedTags, 'name').join(";") : null);
var selectedStatus = getSelectedOptions($scope.currentPage.options.statusOptions);
$location.search('status', selectedStatus.length > 0 ? _.map(selectedStatus, 'id').join(";") : null);
var selectedClasses = getSelectedOptions($scope.currentPage.options.classOptions);
$location.search('classes', selectedClasses.length > 0 ? _.map(selectedClasses, 'name').join(";") : null);
$location.search('page', $scope.pagination ? $scope.currentPageNr : null);
$location.search('itemsPerPage', $scope.pagination ? $scope.itemsPerPage : null);
$scope.updatingLocation = false;
};
$scope.showFailedScenarios = function (options) {
scenarios = dataService.getFailedScenarios();
var description = getDescription(scenarios.length, "failed");
$scope.currentPage = {
title: "Failed Scenarios",
description: description,
breadcrumbs: ['FAILED SCENARIOS'],
options: optionService.getOptions(scenarios, options)
};
$scope.applyOptions();
};
function getDescription (count, status) {
if (count === 0) {
return "There are no " + status + " scenarios. Keep rocking!";
} else if (count === 1) {
return "There is only 1 " + status + " scenario. You nearly made it!";
} else {
return "There are " + count + " " + status + " scenarios";
}
}
$scope.toggleTagType = function (tagType) {
tagType.collapsed = !tagType.collapsed;
};
$scope.toggleScenario = function (scenario) {
scenario.expanded = !scenario.expanded;
};
$scope.searchSubmit = function () {
console.log("Searching for " + $scope.nav.search);
$location.path("search/" + $scope.nav.search);
};
$scope.search = function search (searchString, options) {
console.log("Searching for " + searchString);
$scope.currentPage = {
scenarios: [],
title: "Search Results",
description: "Searched for '" + searchString + "'",
breadcrumbs: ['Search', searchString],
loading: true
};
$timeout(function () {
scenarios = searchService.findScenarios(searchString);
$scope.currentPage.loading = false;
$scope.currentPage.options = optionService.getOptions(scenarios, options);
$scope.applyOptions();
}, 1);
};
$scope.gatherStatistics = function gatherStatistics (scenarios) {
var statistics = {
count: scenarios.length,
failed: 0,
pending: 0,
success: 0,
aborted: 0,
totalNanos: 0
};
_.forEach(scenarios, function (x) {
statistics.totalNanos += x.durationInNanos;
if (x.executionStatus === 'SUCCESS') {
statistics.success++;
} else if (x.executionStatus === 'FAILED') {
statistics.failed++;
} else if (x.executionStatus === 'ABORTED') {
statistics.aborted++;
} else {
statistics.pending++;
}
});
$timeout(function () {
statistics.chartData = [statistics.success, statistics.failed, statistics.pending,statistics.aborted];
}, 0);
return statistics;
};
$scope.printCurrentPage = function printCurrentPage () {
$location.search("print", true);
$timeout(printPage, 0);
};
function printPage () {
if ($scope.currentPage.loading) {
$timeout(printPage, 0);
} else {
window.print();
$timeout(function () {
$location.search("print", null);
}, 0);
}
}
$scope.expandAll = function expandAll () {
_.forEach(scenarios, function (x) {
x.expanded = true;
});
};
$scope.collapseAll = function collapseAll () {
_.forEach(scenarios, function (x) {
x.expanded = false;
});
};
$scope.sortOptionSelected = function sortOptionSelected (sortOption) {
deselectAll($scope.currentPage.options.sortOptions);
sortOption.selected = true;
$scope.applyOptions();
};
$scope.groupOptionSelected = function groupOptionSelected (groupOption) {
deselectAll($scope.currentPage.options.groupOptions);
groupOption.selected = true;
$scope.applyOptions();
};
$scope.filterOptionSelected = function filterOptionSelected (filterOption) {
filterOption.selected = !filterOption.selected;
$scope.applyOptions();
};
function getFilterFunction (page) {
var anyStatusMatches = anyOptionMatches(getSelectedOptions(page.options.statusOptions));
var anyTagMatches = allOptionMatches(getSelectedOptions(page.options.tagOptions));
var anyClassMatches = anyOptionMatches(getSelectedOptions(page.options.classOptions));
return function (scenario) {
return anyStatusMatches(scenario) && anyTagMatches(scenario) && anyClassMatches(scenario);
}
}
function anyOptionMatches (filterOptions) {
// by default nothing is filtered
if (filterOptions.length === 0) {
return function () {
return true;
};
}
return function (scenario) {
for (var i = 0; i < filterOptions.length; i++) {
if (filterOptions[i].apply(scenario)) {
return true;
}
}
return false;
}
}
function allOptionMatches (filterOptions) {
// by default nothing is filtered
if (filterOptions.length === 0) {
return function () {
return true;
};
}
return function (scenario) {
for (var i = 0; i < filterOptions.length; i++) {
if (!filterOptions[i].apply(scenario)) {
return false;
}
}
return true;
}
}
function getSelectedSortOption (page) {
return getSelectedOptions(page.options.sortOptions)[0];
}
function getSelectedGroupOption (page) {
return getSelectedOptions(page.options.groupOptions)[0];
}
function getSelectedOptions (options) {
return _.filter(options, 'selected');
}
$scope.nanosToReadableUnit = nanosToReadableUnit;
$scope.getWordValue = getWordValue;
$scope.tagIdToString = function tagIdToString (tagId) {
var tag = tagService.getTagByTagId(tagId);
return tagToString(tag);
};
$scope.tagToString = tagToString;
$scope.getUrlFromTagId = function getUrlFromTagId (tagId) {
var tag = $scope.getTagByTagId(tagId);
return $scope.getUrlFromTag(tag);
};
$scope.getUrlFromTag = function getUrlFromTag (tag) {
if (tag.href) {
return tag.href;
}
return '#tagid/' + getTagId(tag) +
(tag.value ? '/' + $window.encodeURIComponent(tag.value) : '');
};
$scope.getTagByTagId = function (tagId) {
return tagService.getTagByTagId(tagId);
};
$scope.getCssClassOfTag = function getCssClassOfTag (tagId) {
var tag = $scope.getTagByTagId(tagId);
if (tag.cssClass) {
return tag.cssClass;
}
return 'tag-' + getTagName(tag);
};
/**
* Returns the content of style attribute for the given tag
*/
$scope.getStyleOfTag = function getStyleOfTag (tagId) {
var tag = tagService.getTagByTagId(tagId);
var style = "";
if (tag.style) {
style = tag.style;
}
if (tag.color) {
style += ' background-color: ' + tag.color;
}
return style;
};
$scope.getScenarioTitleStatusClass = function (scenario) {
switch (scenario.executionStatus) {
case 'SUCCESS':
return '';
case 'FAILED':
return 'failed';
case 'ABORTED':
return 'aborted';
default:
return 'pending';
}
};
$scope.getNumberOfAbortedCases = function (scenario) {
var nCases = scenario.scenarioCases.length;
if (nCases === 1) {
return '';
}
var abortedCases = 0;
_.forEach(scenario.scenarioCases, function (aCase) {
if (aCase.status === 'ABORTED') {
abortedCases++;
}
});
if (abortedCases < nCases) {
return " " + abortedCases + " OF " + nCases + " CASES ";
} else {
return " ALL CASES";
}
}
$scope.getNumberOfFailedCases = function (scenario) {
var nCases = scenario.scenarioCases.length;
if (nCases === 1) {
return '';
}
var failedCases = 0;
_.forEach(scenario.scenarioCases, function (aCase) {
if (aCase.status === 'FAILED') {
failedCases++;
}
});
if (failedCases < nCases) {
return " " + failedCases + " OF " + nCases + " CASES ";
} else {
return " ALL CASES";
}
};
$scope.getScenarioCaseTitleStatusClass = function (scenarioCase) {
return scenarioCase.status === 'FAILED' ? 'failed' : '';
};
$scope.isHeaderCell = function (rowIndex, columnIndex, headerType) {
if (rowIndex === 0 && (headerType === 'HORIZONTAL' || headerType === 'BOTH')) {
return true;
}
return columnIndex === 0 && (headerType === 'VERTICAL' || headerType === 'BOTH');
};
/**
* Returns all but the intro words of the given array of words.
* It is assumed that only the first word can be an intro word
* @param words the array of all non-intro words of a step
*/
$scope.getNonIntroWords = function getNonIntroWords (words) {
if (words[0].isIntroWord) {
return words.slice(1);
}
return words;
};
function isRootPath($location) {
return $location.path() === '/' || $location.path() === '' || $location.path() === '/all'
}
$scope.showFailed = function () {
if (isRootPath($location)) {
$location.path('/failed');
} else {
$location.search('status', 'fail');
}
};
$scope.showPending = function () {
if (isRootPath($location)) {
$location.path('/pending');
} else {
$location.search('status', 'pending');
}
};
$scope.showAborted = function () {
if (isRootPath($location)) {
$location.path('/aborted');
} else {
$location.search('status', 'aborted');
}
}
$scope.showSuccessful = function () {
$scope.updatingLocation = true;
if ($location.path() === '/' || $location.path() === '') {
$location.path('/all');
}
$location.search('status', 'success');
$scope.updatingLocation = false;
};
$scope.clearFilter = function () {
if ($location.path() === '/') {
$location.path('/all');
} else {
$location.search('status', null);
$location.search('tags', null);
$location.search('classes', null);
}
};
$scope.replaceStepExtendedDescription = function replaceStepExtendedDescription (step) {
if (step.extendedDescription === undefined) { return ""; }
var enumArray = [];
var nameArray = [];
var description = step.extendedDescription;
var words = step.words;
[enumArray, nameArray] = getArgumentInfos(words);
return replaceArguments(description, enumArray, nameArray);
};
$scope.toThumbnailPath = function toThumbnailPath (path) {
return getThumbnailPath(path);
}
$scope.isImage = function isImage(attachment) {
var mimeType = attachment.mediaType;
return isImageType(mimeType);
}
$scope.init();
});