UNPKG

siesta-lite

Version:

Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers

1,165 lines (833 loc) 38.4 kB
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>The source code</title> <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" /> <script type="text/javascript" src="../resources/prettify/prettify.js"></script> <style type="text/css"> .highlight { display: block; background-color: #ddd; } </style> <script type="text/javascript"> function highlight() { document.getElementById(location.hash.replace(/#/, "")).className = "highlight"; } </script> </head> <body onload="prettyPrint(); highlight();"> <pre class="prettyprint lang-js">/* Siesta 5.6.1 Copyright(c) 2009-2022 Bryntum AB https://bryntum.com/contact https://bryntum.com/products/siesta/license */ // workaround for: http://www.sencha.com/forum/showthread.php?299660-5.1.0.107-Exception-thrown-when-opening-any-example-in-Chrome-touch-simulation-mode&amp;p=1094459#post1094459 // this condition seems to point that Chrome is opened in device simulation mode, w/o touch events enabled from command line // seems &quot;TouchEvents&quot; + &quot;!Touch&quot; confuses Ext and exception is thrown if (Ext.supports &amp;&amp; Ext.supports.TouchEvents &amp;&amp; !Ext.supports.Touch) { // w/o these, UI throws exceptions Ext.supports.Touch = false Ext.supports.touchScroll = false } // eof workaround Ext.define(&#39;Siesta.Project.Browser.UI.Viewport&#39;, { extend : &#39;Ext.container.Viewport&#39;, mixins : [ &#39;Siesta.Project.Browser.UI.CanFillAssertionsStore&#39; ], controller : &#39;viewport&#39;, requires : [ &#39;Siesta.Project.Browser.UI.ViewportController&#39;, &#39;Ext.state.LocalStorageProvider&#39;, &#39;Ext.state.CookieProvider&#39;, &#39;ExtX.Reference.Slot&#39;, &#39;Siesta.Project.Browser.UI.TestGrid&#39;, &#39;Siesta.Project.Browser.UI.ResultPanel&#39;, &#39;Siesta.Project.Browser.Model.AssertionTreeStore&#39;, &#39;Siesta.Project.Browser.Model.FilterableTreeStore&#39;, &#39;Siesta.Project.Browser.Model.TestFile&#39;, &#39;Siesta.Project.Browser.Model.Assertion&#39;, &#39;Siesta.Project.Browser.UI.VersionUpdateButton&#39;, &#39;Siesta.Project.Browser.UI.Override&#39;, // requiring &quot;Ext.Img&quot; for it to be in the &quot;siesta-all.js&quot; bundle // otherwise it is included into &quot;siesta-coverage-all.js&quot; bundle and when using &quot;siesta-no-ext-all.js&quot; setup // Ext.Img is double included &#39;Ext.Img&#39; ], title : null, project : null, // need to set stateful properties before `initComponent` stateful : false, layout : &#39;border&#39;, // stateful selection : null, filter : null, filterGroups : false, // eof stateful testsStore : null, contextMenu : null, enableVersionCheck : true, collapsedNodes : null, showSizeControls : false, expectedExtJSVersion : &#39;6.0.1.250&#39;, viewportSizes : [ [640, 480], [800, 600], [1024, 768], [1920, 1080], [2048, 1536] ], idGenSequence : 1, isReadOnlyReport : false, dataUrl : &#39;report-data.json&#39;, autoLaunch : false, initComponent : function () { var project if (this.isReadOnlyReport) { project = new Siesta.Project.Browser() project.configure({ stateful : false }) this.project = project } else project = this.project; Ext.getBody().addCls(&#39;siesta&#39;) Ext.getBody().on(&#39;keydown&#39;, this.onBodyKeyDown, this) Ext.setGlyphFontFamily(&#39;FontAwesome&#39;); Ext.state.Manager.setProvider(Ext.supports.LocalStorage ? new Ext.state.LocalStorageProvider() : new Ext.state.CookieProvider()) this.selection = {} // config given during viewport instantiation var filter = this.filter if (project.stateful) this.applyState(this.loadState()) // config given during viewport instantiation - comes from query string ?filter= overrides everything if (filter) { this.filter = filter this.filterGroups = false } var data = this.buildTreeData({ id : &#39;root&#39;, group : &#39;test suite&#39; + this.title, items : project.descriptors }).children; var testsStore = this.testsStore = new Siesta.Project.Browser.Model.FilterableTreeStore({ model : &#39;Siesta.Project.Browser.Model.TestFile&#39;, sortOnLoad : false, root : { expanded : true, children : data }, proxy : &#39;memory&#39;, listeners : { nodecollapse : this.saveState, nodeexpand : this.saveState, scope : this } }) Ext.apply(this, { slots : true, items : [ { region : &#39;west&#39;, xtype : &#39;testgrid&#39;, store : testsStore, stateful : project.stateful, slot : &#39;filesTree&#39;, id : project.id.replace(/\W/g, &#39;_&#39;) + &#39;-testTree&#39;, showSizeControls : this.showSizeControls, viewportSizes : this.viewportSizes, stateConfig : this.getState(), project : this.project, isReadOnlyReport : this.isReadOnlyReport, animate : !Ext.isIE, split : { size : 7 }, filter : this.filter, filterGroups : this.filterGroups, listeners : { viewready : this.onFilterApplied, scope : this } }, { xtype : &#39;resultpanel&#39;, region : &#39;center&#39;, slot : &#39;resultPanel&#39;, cls : &#39;resultPanel-panel&#39;, viewDOM : this.getOption(&#39;viewDOM&#39;), scaleToFit : this.getOption(&#39;scaleToFit&#39;), scaleToFitMode : this.getOption(&#39;scaleToFitMode&#39;), stateConfig : this.getState(), id : project.id.replace(/\W/g, &#39;_&#39;) + &#39;-resultpanel&#39;, project : project, recorderConfig : project.recorderConfig, isReadOnlyReport : this.isReadOnlyReport, maintainViewportSize : project.maintainViewportSize, domContainerRegion : project.domContainerRegion, listeners : { newjsonreport : this.onNewJsonReport, scope : this } } ] // eof main content area }) this.callParent() this.slots.filesTree.store.on({ &#39;filter-set&#39; : this.saveState, &#39;filter-clear&#39; : this.saveState, scope : this }) this.on(&#39;statechange&#39;, this.saveState, this) project.on(&#39;testendbubbling&#39;, this.onEveryTestEnd, this) project.on(&#39;testsuitelaunch&#39;, this.onTestSuiteLaunch, this) project.on(&#39;assertiondiscard&#39;, this.onAssertionDiscarded, this) if (window.location.href.match(&#39;^file:///&#39;) &amp;&amp; !(this.isReadOnlyReport &amp;&amp; Ext.browser.is(&#39;Firefox&#39;))) { var R = Siesta.Resource(&#39;Siesta.Project.Browser.UI.Viewport&#39;); Ext.Msg.alert(R.get(&#39;httpWarningTitle&#39;), R.get(&#39;httpWarningDesc&#39;)) } else if (this.isReadOnlyReport) this.fetchJsonReport() }, onNewJsonReport : function (field, report) { this.loadJsonReport(report) }, buildTreeData : function (descriptor) { var data = { id : descriptor.id, title : descriptor.group || descriptor.title || descriptor.name, descriptor : descriptor, // HACK, bypass Ext JS cloning nodeType : 1, cloneNode : function () { return this; } // EOF HACK } data.tooltip = descriptor.desc || data.title var me = this var prevId = data.id var collapsedNodes = this.collapsedNodes || {} if (descriptor.group) { var children = [] Ext.each(descriptor.items, function (desc) { children.push(me.buildTreeData(desc)) }) Ext.apply(data, { expanded : (collapsedNodes[prevId] != null || descriptor.expanded === false) ? false : true, // || false is required for TreeView - it checks that &quot;checked&quot; field contains Boolean checked : me.selection.hasOwnProperty(prevId) || false, folderStatus : &#39;yellow&#39;, children : children, leaf : false }) } else { Ext.apply(data, { url : descriptor.url, leaf : true, // || false is required for TreeView - it checks that &quot;checked&quot; field contains Boolean checked : me.selection.hasOwnProperty(prevId) || false, passCount : 0, failCount : 0, time : 0, assertionsStore : new Siesta.Project.Browser.Model.AssertionTreeStore({ //autoDestroy : true, model : &#39;Siesta.Project.Browser.Model.Assertion&#39;, proxy : &#39;memory&#39;, root : { id : &#39;__ROOT__&#39;, expanded : true } }) }) } return data }, onBodyKeyDown : function (e) { if (e.ctrlKey) { var project = this.project if (e.getKey() == Ext.event.Event[ project.rerunHotKey ]) { this.runTest(); e.preventDefault(); } if (e.getKey() == Ext.event.Event[ project.observerModeHotKey ]) { var mode = !project.observerMode var menuItem = this.down(&#39;#observerModeMenuItem&#39;) menuItem.setChecked(mode) menuItem.fireEvent(&#39;click&#39;, menuItem) e.preventDefault(); } } }, buildTreeDataFromJSONSReportNode : function (reportNode) { var data = { id : this.idGenSequence++, title : reportNode.group || reportNode.name || reportNode.url.replace(/(?:.*\/)?([^/]+)$/, &#39;$1&#39;), descriptor : reportNode, // HACK, bypass Ext JS cloning nodeType : 1, cloneNode : function () { return this; } } var me = this if (reportNode.group) { var children = [] Ext.each(reportNode.items, function (reportNode) { children.push(me.buildTreeDataFromJSONSReportNode(reportNode)) }) Ext.apply(data, { expanded : true, // disable checkbox selection checked : null, leaf : false, folderStatus : &#39;yellow&#39;, children : children }) } else { var parentTest = new Siesta.Test.Browser({ project : this.project, url : reportNode.url, name : reportNode.name, startDate : new Date(reportNode.startDate) }) Ext.apply(data, { url : reportNode.url, leaf : true, // disable checkbox selection checked : null, time : reportNode.endDate - reportNode.startDate, test : parentTest, assertionsStore : new Siesta.Project.Browser.Model.AssertionTreeStore({ model : &#39;Siesta.Project.Browser.Model.Assertion&#39;, proxy : &#39;memory&#39;, root : { id : &#39;__ROOT__&#39;, expanded : true, children : this.buildAssertionDataFromJSONSReportNode(reportNode.assertions, parentTest) } }) }) parentTest.endDate = new Date(reportNode.endDate) Ext.apply(data, { passCount : parentTest.getPassCount(), failCount : parentTest.getFailCount(), todoPassCount : parentTest.getTodoPassCount(), todoFailCount : parentTest.getTodoFailCount() }) } return data }, buildAssertionDataFromJSONSReportNode : function (assertions, parentTest) { var me = this var res = Joose.A.map(assertions, function (assertionNode) { var isSubTest = assertionNode.type == &#39;Siesta.Result.SubTest&#39; var result var data = { id : me.idGenSequence++, leaf : !isSubTest, expanded : isSubTest &amp;&amp; (assertionNode.bddSpecType != &#39;it&#39; || !assertionNode.passed) } if (isSubTest) { var subTest = new Siesta.Test.Browser({ trait : Siesta.Test.Sub, name : assertionNode.name, isTodo : assertionNode.isTodo, startDate : new Date(assertionNode.startDate), url : parentTest.url, parent : parentTest, specType : assertionNode.bddSpecType }) data.children = me.buildAssertionDataFromJSONSReportNode(assertionNode.assertions, subTest) subTest.endDate = new Date(assertionNode.endDate) result = subTest.getResults() } else { var resultCls = Joose.S.strToClass(assertionNode.type) result = new resultCls(assertionNode) } parentTest.addResult(result) data.result = result return data }) if (!parentTest.parent) { var summary = new Siesta.Result.Summary({ isFailed : parentTest.isFailed(), description : parentTest.getSummaryMessage() }) parentTest.addResult(summary) res.push({ id : this.idGenSequence++, result : summary, loaded : true, leaf : true, expanded : false }) } return res }, loadJsonReport : function (report) { this.title = report.testSuiteName || &#39;&#39; this.testsStore.setRootNode(this.buildTreeDataFromJSONSReportNode({ group : &#39;TOP&#39;, items : report.testCases })) this.testsStore.getRootNode().cascadeBy(function (node) { if (node.parentNode &amp;&amp; !node.isLeaf()) node.updateFolderStatus() }) this.updateStatusIndicator(); }, fetchJsonReport : function () { var me = this var loadingMask = new Ext.LoadMask({ target : this, msg : Siesta.Resource(&#39;Siesta.Project.Browser.UI.CoverageReport&#39;, &#39;loadingText&#39;) }); 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.loadJsonReport(Ext.JSON.decode(response.responseText)) }, failure : function () { loadingMask.hide() Ext.Msg.show({ title : Siesta.Resource(&#39;Siesta.Project.Browser.UI.CoverageReport&#39;, &#39;loadingErrorText&#39;), msg : Siesta.Resource(&#39;Siesta.Project.Browser.UI.CoverageReport&#39;, &#39;loadingErrorMessageText&#39;) + me.dataUrl, buttons : Ext.MessageBox.OK, icon : Ext.MessageBox.ERROR }) } }) }, onFilterApplied : function () { if (this.autoLaunch) { this.runAll() } else if (this.getOption(&#39;autoRun&#39;)) { var checked = this.getChecked() // either launch the suite for checked tests or for all this.project.launch(checked.length &amp;&amp; checked || this.project.descriptors) } }, setNodeChecked : function (testFile, checked, doNotCascade, skipSave) { var me = this var id = testFile.getId() if (checked) this.selection[id] = 1 else delete this.selection[id] testFile.set(&#39;checked&#39;, checked) // when unchecking the node - uncheck the parent node (folder) as well if (!checked &amp;&amp; testFile.parentNode) me.setNodeChecked(testFile.parentNode, false, true, true) // only cascade for folders and when `doNotCascade` is false if (!testFile.isLeaf() &amp;&amp; !doNotCascade) Ext.each(testFile.childNodes, function (childNode) { me.setNodeChecked(childNode, checked, false, true) }) if (!skipSave) this.saveState() }, forEachTestFile : function (func, scope) { scope = scope || this var testsStore = this.testsStore var root = testsStore.getRootNode() root.cascadeBy(function (node) { var isFilteredIn = testsStore.isNodeFilteredIn(node) if (node != root &amp;&amp; isFilteredIn) func.call(scope, node) // do not cascade for child nodes if parent is not filtered in - faster tree traversing return isFilteredIn }) }, getChecked : function () { var descriptors = [] this.forEachTestFile(function (testFileRecord) { if (testFileRecord.get(&#39;checked&#39;) &amp;&amp; testFileRecord.isLeaf()) descriptors.push(testFileRecord.get(&#39;descriptor&#39;)) }) return descriptors }, runChecked : function () { var checked = this.getChecked(); if (checked.length &gt; 0) { this.project.launch(this.getChecked()) } }, runFailed : function () { var descriptors = [] this.forEachTestFile(function (testFileRecord) { var test = testFileRecord.get(&#39;test&#39;) if (test &amp;&amp; test.isFailed()) descriptors.push(testFileRecord.get(&#39;descriptor&#39;)) }) if (descriptors.length &gt; 0) { this.project.launch(descriptors) } }, runAll : function () { var allDesc = [] this.forEachTestFile(function (testFile) { if (testFile.isLeaf()) allDesc.push(testFile.get(&#39;descriptor&#39;)) }) if (allDesc.length &gt; 0) { this.project.launch(allDesc) } }, // this method is never called currently, because there&#39;s no corresponding button in the test grid bottom bar stopSuite : function (button) { button.disable() this.project.stopCurrentLaunch(); setTimeout(function () { button.enable() }, 1000) }, onTestSuiteStop : function (sourceTest) { this.testsStore.forEach(function (testFileRecord) { if (testFileRecord.get(&#39;isStarting&#39;)) { testFileRecord.set(&#39;isStarting&#39;, false); } }); if (sourceTest) { var testRecord = this.testsStore.getNodeById(sourceTest.url) this.slots.filesTree.getSelectionModel().select(testRecord); } }, // looks less nice than setting it only after preload for some reason onBeforeScopePreload : function (scopeProvider, url) { var testRecord = this.testsStore.getNodeById(url) }, isTestRunningVisible : function (test) { // return false for test&#39;s running in popups (not iframes), since we can&#39;t show any visual accompaniment for them if (!(test.scopeProvider instanceof Scope.Provider.IFrame)) return false; // if there is a &quot;forced to be on top&quot; test then we only need to compare the tests instances if (this.project.testOfForcedIFrame) { return this.project.testOfForcedIFrame.isFromTheSameGeneration(test) } // otherwise the only possibly visible test is the one of the current assertion grid var resultPanel = this.slots.resultPanel; // if resultPanel has no testRecord it hasn&#39;t yet been assigned a test record if (!resultPanel.test || !resultPanel.test.isFromTheSameGeneration(test)) { return false; } // now we know that visible assertion grid is from our test and there is no &quot;forced on top&quot; test // we only need to check visibility (collapsed / expanded of the right panel return resultPanel.isFrameVisible() }, resetDescriptors : function (descriptors) { var testsStore = this.testsStore; Joose.A.each(this.project.flattenDescriptors(descriptors), function (descriptor) { var testRecord = testsStore.getNodeById(descriptor.id); testRecord.get(&#39;assertionsStore&#39;).removeAll(true) testRecord.reject(); // || false is required for TreeView - it checks that &quot;checked&quot; field contains Boolean testRecord.set(&#39;checked&#39;, this.selection.hasOwnProperty(descriptor.id) || false) }, this); }, // method is called when test suite (any several tests) starts - before caching the script contents // at this point we don&#39;t know yet about missing test files onTestSuiteStart : function (descriptors) { Ext.getBody().addCls(&#39;testsuite-running&#39;); var filesTree = this.slots.filesTree var selModel = filesTree.getSelectionModel() var prevSelection = selModel.getLastSelected() var testsStore = this.testsStore this.resetDescriptors(descriptors); // restore the selection after data reload if (prevSelection) selModel.select(testsStore.getNodeById(prevSelection.getId())) }, // method is called when test suite (any several tests) launches - after the caching the script contents // has completed and 1st test is about to start // at this point we know about missing files and `desc.isMissing` property is set onTestSuiteLaunch : function (event, descriptors) { var testsStore = this.testsStore var updated = {} Joose.A.each(this.project.flattenDescriptors(descriptors), function (descriptor) { var testRecord = testsStore.getNodeById(descriptor.id) testRecord.set({ isMissing : descriptor.isMissing, isStarting : true }) var groupNode = testRecord.parentNode if (groupNode &amp;&amp; !updated[groupNode.getId()]) { // trying hard to prevent extra updates for (var node = groupNode; node; node = node.parentNode) updated[node.getId()] = true groupNode.updateFolderStatus() } }) }, onTestSuiteEnd : function (descriptors) { Ext.getBody().removeCls(&#39;testsuite-running&#39;); this.updateStatusIndicator(); // Without this the keyboard hotkey won&#39;t work (since one of the frames will steal focus likely) if (Ext.isChrome) { window.focus(); } else { document.body.tabIndex = -1; document.body.focus(); } }, onTestStart : function (test) { var testRecord = this.testsStore.getNodeById(test.url) if (!this.isTestUpdateActual(test, testRecord)) return testRecord.beginEdit() // will trigger an update in grid testRecord.set({ test : test, isRunning : true }) testRecord.endEdit() var currentSelection = this.slots.filesTree.getSelectionModel().getLastSelected() // activate the assertions grid for currently selected row, or, if the main area is empty if (currentSelection &amp;&amp; currentSelection.getId() == test.url) { var resultPanel = this.slots.resultPanel resultPanel.showTest(test, testRecord.get(&#39;assertionsStore&#39;)) resultPanel.setInitializing(false); T = test } }, // this method checks that test update, coming from given `test` is actual // update may be not actual, if user has re-launched the test, so new test already presents isTestUpdateActual : function (test, testRecord) { return test.launchId == this.project.currentLaunchId }, onTestUpdate : function (test, result, parentResult) { var testRecord = this.testsStore.getNodeById(test.url) // need to check that test record contains the same test instance as the test in arguments (or its sub-test) // test instance may change if user has restarted a test for example if (this.isTestUpdateActual(test, testRecord)) { this.processNewResult(testRecord.get(&#39;assertionsStore&#39;), test, result, parentResult) } }, onAssertionDiscarded: function (event, test, result) { var testRecord = this.testsStore.getNodeById(test.url) // need to check that test record contains the same test instance as the test in arguments (or its sub-test) // test instance may change if user has restarted a test for example if (this.isTestUpdateActual(test, testRecord)) { var assertionStore = testRecord.get(&#39;assertionsStore&#39;) assertionStore.getNodeById(result.id).remove() } }, // only triggered for &quot;root&quot; tests onTestEnd : function (test) { var testRecord = this.testsStore.getNodeById(test.url) // need to check that test record contains the same test instance as the test in arguments (or its sub-test) // test instance may change if user has restarted a test for example if (this.isTestUpdateActual(test, testRecord)) { testRecord.beginEdit() testRecord.set({ &#39;passCount&#39; : test.getPassCount(), &#39;failCount&#39; : test.getFailCount(), &#39;todoPassCount&#39; : test.getTodoPassCount(), &#39;todoFailCount&#39; : test.getTodoFailCount(), &#39;time&#39; : test.getDuration() + &#39;ms&#39; }); testRecord.endEdit() testRecord.parentNode &amp;&amp; testRecord.parentNode.updateFolderStatus() } this.updateStatusIndicator() }, // is bubbling and thus triggered for all tests (including sub-tests) onEveryTestEnd : function (event, test) { var testRecord = this.testsStore.getNodeById(test.url) // need to check that test record contains the same test instance as the test in arguments (or its sub-test) // test instance may change if user has restarted a test for example if (this.isTestUpdateActual(test, testRecord)) { this.processEveryTestEnd(testRecord.get(&#39;assertionsStore&#39;), test) } }, onTestFail : function (test, exception, stack) { var testRecord = this.testsStore.getNodeById(test.url) // need to check that test record contains the same test instance as the test in arguments // test instance may change if user has restarted a test for example if (this.isTestUpdateActual(test, testRecord) &amp;&amp; !test.isTodo) { testRecord.set(&#39;isFailed&#39;, true) testRecord.parentNode &amp;&amp; testRecord.parentNode.updateFolderStatus() } }, getOption : function (name, onlyOwn) { switch (name) { case &#39;selection&#39; : return this.selection default : var project = this.project if (onlyOwn) return project.hasOwnProperty(name) ? project[ name ] : undefined return project[ name ] } }, setOption : function (name, value) { var project = this.project switch (name) { case &#39;selection&#39; : return this.selection = value || {} case &#39;collapsedNodes&#39; : return this.collapsedNodes = value case &#39;filter&#39; : return this.filter = value case &#39;filterGroups&#39; : return this.filterGroups = value case &#39;observerMode&#39; : project.setObserverMode(value) break // UI only option, does not correspond to viewport/project config case &#39;mouseSimSlow&#39; : if (value) { project[ &#39;speedRun&#39; ] = false project[ &#39;turboMode&#39; ] = false } break // UI only option, does not correspond to viewport/project config case &#39;mouseSimFast&#39; : if (value) { project[ &#39;speedRun&#39; ] = true project[ &#39;turboMode&#39; ] = false } break // UI only option, does not correspond to viewport/project config case &#39;mouseSimFastest&#39; : if (value) { project[ &#39;speedRun&#39; ] = true project[ &#39;turboMode&#39; ] = true } break default : return project[ name ] = value } }, getState : function (onlyOwnProperties) { // getState is also called early, when there&#39;s no child components // the absense of &quot;this.slots&quot; indicates this var domContainer = this.slots &amp;&amp; this.down(&#39;domcontainer&#39;) var state = { // UI configs scaleToFit : domContainer ? domContainer.scaleToFit : this.scaleToFit, scaleToFitMode : domContainer ? domContainer.scaleToFitMode : this.scaleToFitMode, selection : this.getCheckedNodes(), collapsedNodes : this.getCollapsedFolders(), filter : this.slots ? this.slots.filesTree.getFilterValue() : this.filter, filterGroups : this.slots ? this.slots.filesTree.getFilterGroups() : this.filterGroups } var me = this // project configs Joose.A.each([ &#39;autoRun&#39;, &#39;speedRun&#39;, &#39;turboMode&#39;, &#39;viewDOM&#39;, &#39;transparentEx&#39;, &#39;breakOnFail&#39;, &#39;debuggerOnFail&#39;, &#39;observerMode&#39; ], function (name) { var value = me.getOption(name, onlyOwnProperties) if (onlyOwnProperties) { if (value !== undefined) state[ name ] = value } else state[ name ] = value }) return state }, getCheckedNodes : function () { var checked = {} this.testsStore.forEach(function (treeNode) { if (treeNode.get(&#39;checked&#39;)) checked[treeNode.getId()] = 1 }) return checked }, getCollapsedFolders : function () { var collapsed = {} this.testsStore.forEach(function (treeNode) { if (!treeNode.isLeaf() &amp;&amp; !treeNode.isExpanded()) collapsed[treeNode.getId()] = 1 }) return collapsed }, applyState : function (state) { var me = this if (state) Joose.O.each(state, function (value, name) { me.setOption(name, value) }) }, getStateId : function () { return &#39;test-run-&#39; + this.title }, loadState : function () { var stateId = this.getStateId() var state = Ext.state.Manager.get(stateId) if (!state) return if (!state.collapsedNodes) state.collapsedNodes = Ext.state.Manager.get(stateId + &#39;-collapsed&#39;) if (!state.selection) state.selection = Ext.state.Manager.get(stateId + &#39;-selection&#39;) return state }, saveState : function () { var stateId = this.getStateId() var state = this.getState(true) Ext.state.Manager.set(stateId + &#39;-collapsed&#39;, state.collapsedNodes) Ext.state.Manager.set(stateId + &#39;-selection&#39;, state.selection) delete state.collapsedNodes delete state.selection Ext.state.Manager.set(stateId, state) }, uncheckAllExcept : function (testFile) { var me = this this.testsStore.forEach(function (node) { if (node != testFile) me.setNodeChecked(node, false, true) }) }, launchTest : function (testFile) { if (this.isReadOnlyReport) return var resultPanel = this.slots.resultPanel var isLeaf = testFile instanceof Ext.data.Model ? testFile.data.leaf : true; var descriptor = testFile instanceof Siesta.Test ? this.project.getScriptDescriptor(testFile) : testFile.get(&#39;descriptor&#39;); // clear the content of the result panel when launching a single test if (isLeaf) { Ext.History.add(descriptor.url); // assertions of the tests being launched will be cleared in the `onTestSuiteStart` method resultPanel.setInitializing(true); } this.project.launch([ descriptor ]) }, updateStatusIndicator : function () { // can remain neutral if all files are missing for example // var isNeutral = true // var allGreen = true // var hasFailures = false var totalPassed = 0 var totalFailed = 0 this.testsStore.forEach(function (testFileRecord) { var test = testFileRecord.get(&#39;test&#39;) // if there&#39;s at least one test - state is not neutral if (test &amp;&amp; test.isFinished()) { // isNeutral = false // allGreen = allGreen &amp;&amp; test.isPassed() // hasFailures = hasFailures || test.isFailed() totalPassed += test.getPassCount() totalFailed += test.getFailCount() } }) this.slots.filesTree.updateStatus(totalPassed, totalFailed); }, <span id='global-method-runTest'> /** </span> * Re-runs the latest executed test */ runTest : function () { if (this.isReadOnlyReport) return var toRun = this.slots.filesTree.getSelectionModel().getSelection()[0] || this.slots.resultPanel.test; if (toRun) { this.launchTest(toRun); } }, afterRender : function () { this.callParent(arguments); if (Ext.versions.extjs.isLessThan(this.expectedExtJSVersion) &amp;&amp; window.console) { console.log(&#39;Wrong Ext JS version detected.&#39;, &#39;Siesta expects that you use Ext JS version &gt;= &#39; + this.expectedExtJSVersion + &#39;. You may experience errors when using another version&#39;); } if (!this.project.isAutomated) setTimeout(Ext.Function.bind(this.deferredSetup, this), 1000); }, deferredSetup : function() { Ext.QuickTips &amp;&amp; Ext.QuickTips.init(); if (this.enableVersionCheck &amp;&amp; Siesta.Project.Browser.UI.VersionUpdateButton) { new Siesta.Project.Browser.UI.VersionUpdateButton(); } }, onManualCloseOfForcedIframe : function (test) { var domContainer = this.down(&#39;domcontainer&#39;) if (domContainer &amp;&amp; domContainer.test == test) domContainer.alignIFrame(true) } }) //eof Siesta.Project.Browser.UI.Viewport </pre> </body> </html>