siesta-lite
Version:
Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers
424 lines (349 loc) • 16.3 kB
JavaScript
/*
Siesta 5.6.1
Copyright(c) 2009-2022 Bryntum AB
https://bryntum.com/contact
https://bryntum.com/products/siesta/license
*/
Ext.define('Siesta.Project.Browser.UI.CoverageReport', {
extend : 'Ext.Container',
alias : 'widget.coveragereport',
requires : [
'Ext.tree.Panel',
'Siesta.Project.Browser.UI.TreeFilterField',
'Siesta.Project.Browser.UI.CoverageChart',
'Siesta.Project.Browser.Model.FilterableTreeStore',
'Siesta.Project.Browser.UI.FilterableTreeView',
'Siesta.Project.Browser.Model.CoverageUnit',
'Ext.layout.container.Border'
],
cls : 'st-cov-report-panel',
layout : 'border',
htmlReport : null,
treePanel : null,
treeStore : null,
chart : null,
sourcePanel : null,
contentContainer : null,
toggleButtons : null,
standalone : false,
dataUrl : null,
project : null,
// Number of levels in the coverage class/file tree to expand by default
expandLevels : 2,
initComponent : function () {
var me = this
var standalone = this.standalone
var store = this.treeStore = new Siesta.Project.Browser.Model.FilterableTreeStore({
model : 'Siesta.Project.Browser.Model.CoverageUnit',
proxy : 'memory',
rootVisible : true
})
Ext.apply(this, {
border : !this.standalone,
items : [
{
xtype : 'treepanel',
border : false,
region : 'west',
enableColumnHide : false,
enableColumnMove : false,
enableColumnResize : false,
sortableColumns : false,
rowLines : false,
tbar : {
cls : 'siesta-toolbar',
height : 45,
border : false,
items : [
standalone ? null : {
text : Siesta.Resource('Siesta.Project.Browser.UI.CoverageReport', 'closeText'),
scale : 'medium',
style : 'margin:0 10px 0 0;',
handler : this.onBackToMainUI,
scope : this
},
{
xtype : 'treefilter',
cls : 'filterfield',
hasAndCheck : function () {
var states = Ext.pluck(me.toggleButtons, 'pressed')
return !states[0] || !states[1] || !states[2]
},
andChecker : this.levelFilter,
andCheckerScope : this,
margin : '3 0 0 0',
store : store,
filterField : 'text',
width : 180
},
'->',
{
xtype : 'label',
text : Siesta.Resource('Siesta.Project.Browser.UI.CoverageReport', 'showText'),
margin : '0 5 0 0'
},
{
text : Siesta.Resource('Siesta.Project.Browser.UI.CoverageReport', 'highText'),
level : 'high',
enableToggle : true,
pressed : true,
scale : 'medium',
cls : 'st-cov-level-high',
toggleHandler : this.toggleLevels,
scope : this,
margin : '0 5 0 0'
},
{
text : Siesta.Resource('Siesta.Project.Browser.UI.CoverageReport', 'mediumText'),
level : 'med',
enableToggle : true,
scale : 'medium',
pressed : true,
cls : 'st-cov-level-medium',
toggleHandler : this.toggleLevels,
scope : this,
margin : '0 5 0 0'
},
{
text : Siesta.Resource('Siesta.Project.Browser.UI.CoverageReport', 'lowText'),
level : 'low',
enableToggle : true,
scale : 'medium',
pressed : true,
cls : 'st-cov-level-low',
toggleHandler : this.toggleLevels,
scope : this,
style : 'margin:0 5px 0 0'
}
]
},
viewType : 'filterabletreeview',
viewConfig : {
store : store.nodeStore,
trackOver : false,
rootVisible : true
},
cls : 'st-cov-report-tree-panel',
flex : 1,
split : true,
rootVisible : true,
columns : [
{
xtype : 'treecolumn',
dataIndex : 'text',
menuDisabled : true,
flex : 3
},
{
text : Siesta.Resource('Siesta.Project.Browser.UI.CoverageReport', 'statementsText'),
flex : 1,
menuDisabled : true,
tdCls : 'cov-result-cell',
renderer : this.getMetricsRenderer('statements')
},
{
text : Siesta.Resource('Siesta.Project.Browser.UI.CoverageReport', 'branchesText'),
flex : 1,
menuDisabled : true,
tdCls : 'cov-result-cell',
renderer : this.getMetricsRenderer('branches')
},
{
text : Siesta.Resource('Siesta.Project.Browser.UI.CoverageReport', 'functionsText'),
flex : 1,
menuDisabled : true,
tdCls : 'cov-result-cell',
renderer : this.getMetricsRenderer('functions')
},
{
text : Siesta.Resource('Siesta.Project.Browser.UI.CoverageReport', 'linesText'),
flex : 1,
menuDisabled : true,
tdCls : 'cov-result-cell',
renderer : this.getMetricsRenderer('lines')
}
],
store : store,
listeners : {
selectionchange : this.onClassSelected,
scope : this
}
},
{
xtype : 'container',
cls : 'coveragechart-container',
slot : 'contentContainer',
region : 'center',
layout : {
type : 'card',
deferredRendering : true
},
items : [
{
xtype : 'coveragechart'
},
{
xtype : 'panel',
slot : 'sourcePanel',
flex : 1,
autoScroll : true,
bodyPadding : '4 0 4 10',
border : false,
cls : 'st-cov-report-source-panel'
}
]
}
]
})
this.callParent()
this.contentContainer = this.down('[slot=contentContainer]')
this.chart = this.down('chart')
this.sourcePanel = this.down('[slot=sourcePanel]')
this.toggleButtons = this.query('button[level]')
this.treePanel = this.down('treepanel')
if (standalone) {
// `loadingMask.show()` throws exception when called with non-rendered target
this.on('afterrender', function () {
var loadingMask = new Ext.LoadMask({
target : this,
msg : Siesta.Resource('Siesta.Project.Browser.UI.CoverageReport', 'loadingText')
});
loadingMask.show()
// use this code for testing
// setTimeout(function () {
// loadingMask.hide()
//
// me.loadHtmlReport(me.htmlReport)
// }, 2000)
Ext.Ajax.request({
url : this.dataUrl,
success : function (response) {
loadingMask.hide()
me.loadHtmlReport(Ext.JSON.decode(response.responseText))
},
failure : function () {
loadingMask.hide()
Ext.Msg.show({
title : Siesta.Resource('Siesta.Project.Browser.UI.CoverageReport', 'loadingErrorText'),
msg : Siesta.Resource('Siesta.Project.Browser.UI.CoverageReport', 'loadingErrorMessageText') + me.dataUrl,
buttons : Ext.MessageBox.OK,
icon : Ext.MessageBox.ERROR
})
}
})
}, this, { single : true })
} else if (this.htmlReport) {
this.loadHtmlReport(this.htmlReport);
}
// Refresh data is user reruns a test with coverage panel open
if (this.project) {
this.project.on('testsuiteend', this.onTestSuiteEnd, this)
}
},
onTestSuiteEnd : function (descriptors) {
if (this.project && this.isVisible()) {
// Load new data into coverage report
this.loadHtmlReport(this.project.generateCoverageHtmlReport(false));
}
},
loadHtmlReport : function (report) {
this.htmlReport = report
var treeStore = this.treeStore
var isFile = report.coverageUnit == 'file'
var treeFilter = this.down('treefilter')
var selModel = this.treePanel.getSelectionModel();
var selected = selModel.getSelected();
var oldSelectedId = selected.length > 0 ? selected.first().getId() : null;
treeStore.setRootNode(this.generateFilesTree(report.htmlReport.root))
if (oldSelectedId && treeStore.getNodeById(oldSelectedId)){
selModel.select(treeStore.getNodeById(oldSelectedId));
}
this.down('treecolumn').setText(isFile ? 'File name' : 'Class name')
treeFilter.emptyText = isFile ? 'Filter files' : 'Filter classes'
treeFilter.applyEmptyText();
this.loadChartData(treeStore.getRootNode());
},
levelFilter : function (node) {
var coverage = node.getStatementCoverage();
if (coverage === undefined) return false;
return this.toggleButtons[coverage >= 80 ? 0 : coverage >= 50 ? 1 : 2].pressed
},
toggleLevels : function () {
this.down('treefilter').refreshFilter()
},
getCoverageLevelClass : function (coveragePct) {
if (typeof coveragePct == 'number')
return 'st-cov-level-' + (coveragePct >= 80 ? 'high' : coveragePct >= 50 ? 'medium' : 'low');
else
return ''
},
getMetricsRenderer : function (property) {
var me = this
return function (value, metaData, preloadFile) {
var reportNode = preloadFile.get('reportNode')
if (!reportNode) return ''
var metrics = reportNode.metrics[property]
metaData.tdCls = me.getCoverageLevelClass(metrics.pct)
return '<span class="st-cov-stat-pct-' + property + '">' + Math.round(metrics.pct) + '%</span> ' +
'<span class="st-cov-stat-abs-' + property + '">(' + metrics.covered + '/' + metrics.total + ')</span>'
}
},
generateFilesTree : function (node, depth) {
var me = this
depth = depth || 0;
// in this method "node" should be treated as a raw JSON object and not an instance of Node from Istanbul
// (even that in UI usage scenario it will be a Node)
// since for the standalone report we load a JSON blob with the tree report
// so we should not call any methods on "node"
if (node.kind === 'dir') {
var text = node.relativeName || node.fullName
return {
id : node.fullName,
url : node.fullName,
leaf : false,
expanded : depth < this.expandLevels,
text : text == '/' && this.htmlReport.coverageUnit == 'extjs_class' ? Siesta.Resource('Siesta.Project.Browser.UI.CoverageReport', 'globalNamespaceText') : text,
reportNode : node,
children : Ext.Array.map(node.children, function (childNode) {
return me.generateFilesTree(childNode, depth + 1)
}).sort(this.treeSorterFn)
}
} else {
return {
id : node.fullName,
url : node.fullName,
leaf : true,
text : node.relativeName,
reportNode : node
}
}
},
treeSorterFn : function (node1, node2) {
if (!node1.leaf && node2.leaf) return -1;
if (node1.leaf && !node2.leaf) return 1;
if (node1.leaf === node2.leaf) return node1.text < node2.text ? -1 : 1;
},
onClassSelected : function (view, records) {
var record = records[0];
if (!record) return;
if (record.isLeaf() && !this.htmlReport.coverageNoSource) {
this.sourcePanel.update(record.get('reportNode').html)
prettyPrint()
this.contentContainer.getLayout().setActiveItem(1)
} else {
this.loadChartData(record)
this.contentContainer.getLayout().setActiveItem(0)
}
},
onBackToMainUI : function () {
this.fireEvent('backtomainui', this)
},
loadChartData : function (node) {
this.chart.store.loadData([
{ name : 'Statements', value : Math.round(node.getStatementCoverage()) },
{ name : 'Branches', value : Math.round(node.getBranchCoverage()) },
{ name : 'Functions', value : Math.round(node.getFunctionCoverage()) },
{ name : 'Lines', value : Math.round(node.getLineCoverage()) }
])
}
});