UNPKG

siesta-lite

Version:

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

1,145 lines (813 loc) 36.3 kB
/* 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&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 "TouchEvents" + "!Touch" confuses Ext and exception is thrown if (Ext.supports && Ext.supports.TouchEvents && !Ext.supports.Touch) { // w/o these, UI throws exceptions Ext.supports.Touch = false Ext.supports.touchScroll = false } // eof workaround Ext.define('Siesta.Project.Browser.UI.Viewport', { extend : 'Ext.container.Viewport', mixins : [ 'Siesta.Project.Browser.UI.CanFillAssertionsStore' ], controller : 'viewport', requires : [ 'Siesta.Project.Browser.UI.ViewportController', 'Ext.state.LocalStorageProvider', 'Ext.state.CookieProvider', 'ExtX.Reference.Slot', 'Siesta.Project.Browser.UI.TestGrid', 'Siesta.Project.Browser.UI.ResultPanel', 'Siesta.Project.Browser.Model.AssertionTreeStore', 'Siesta.Project.Browser.Model.FilterableTreeStore', 'Siesta.Project.Browser.Model.TestFile', 'Siesta.Project.Browser.Model.Assertion', 'Siesta.Project.Browser.UI.VersionUpdateButton', 'Siesta.Project.Browser.UI.Override', // requiring "Ext.Img" for it to be in the "siesta-all.js" bundle // otherwise it is included into "siesta-coverage-all.js" bundle and when using "siesta-no-ext-all.js" setup // Ext.Img is double included 'Ext.Img' ], title : null, project : null, // need to set stateful properties before `initComponent` stateful : false, layout : 'border', // stateful selection : null, filter : null, filterGroups : false, // eof stateful testsStore : null, contextMenu : null, enableVersionCheck : true, collapsedNodes : null, showSizeControls : false, expectedExtJSVersion : '6.0.1.250', viewportSizes : [ [640, 480], [800, 600], [1024, 768], [1920, 1080], [2048, 1536] ], idGenSequence : 1, isReadOnlyReport : false, dataUrl : 'report-data.json', 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('siesta') Ext.getBody().on('keydown', this.onBodyKeyDown, this) Ext.setGlyphFontFamily('FontAwesome'); 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 : 'root', group : 'test suite' + this.title, items : project.descriptors }).children; var testsStore = this.testsStore = new Siesta.Project.Browser.Model.FilterableTreeStore({ model : 'Siesta.Project.Browser.Model.TestFile', sortOnLoad : false, root : { expanded : true, children : data }, proxy : 'memory', listeners : { nodecollapse : this.saveState, nodeexpand : this.saveState, scope : this } }) Ext.apply(this, { slots : true, items : [ { region : 'west', xtype : 'testgrid', store : testsStore, stateful : project.stateful, slot : 'filesTree', id : project.id.replace(/\W/g, '_') + '-testTree', 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 : 'resultpanel', region : 'center', slot : 'resultPanel', cls : 'resultPanel-panel', viewDOM : this.getOption('viewDOM'), scaleToFit : this.getOption('scaleToFit'), scaleToFitMode : this.getOption('scaleToFitMode'), stateConfig : this.getState(), id : project.id.replace(/\W/g, '_') + '-resultpanel', 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({ 'filter-set' : this.saveState, 'filter-clear' : this.saveState, scope : this }) this.on('statechange', this.saveState, this) project.on('testendbubbling', this.onEveryTestEnd, this) project.on('testsuitelaunch', this.onTestSuiteLaunch, this) project.on('assertiondiscard', this.onAssertionDiscarded, this) if (window.location.href.match('^file:///') && !(this.isReadOnlyReport && Ext.browser.is('Firefox'))) { var R = Siesta.Resource('Siesta.Project.Browser.UI.Viewport'); Ext.Msg.alert(R.get('httpWarningTitle'), R.get('httpWarningDesc')) } 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 "checked" field contains Boolean checked : me.selection.hasOwnProperty(prevId) || false, folderStatus : 'yellow', children : children, leaf : false }) } else { Ext.apply(data, { url : descriptor.url, leaf : true, // || false is required for TreeView - it checks that "checked" 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 : 'Siesta.Project.Browser.Model.Assertion', proxy : 'memory', root : { id : '__ROOT__', 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('#observerModeMenuItem') menuItem.setChecked(mode) menuItem.fireEvent('click', menuItem) e.preventDefault(); } } }, buildTreeDataFromJSONSReportNode : function (reportNode) { var data = { id : this.idGenSequence++, title : reportNode.group || reportNode.name || reportNode.url.replace(/(?:.*\/)?([^/]+)$/, '$1'), 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 : 'yellow', 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 : 'Siesta.Project.Browser.Model.Assertion', proxy : 'memory', root : { id : '__ROOT__', 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 == 'Siesta.Result.SubTest' var result var data = { id : me.idGenSequence++, leaf : !isSubTest, expanded : isSubTest && (assertionNode.bddSpecType != 'it' || !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 || '' this.testsStore.setRootNode(this.buildTreeDataFromJSONSReportNode({ group : 'TOP', items : report.testCases })) this.testsStore.getRootNode().cascadeBy(function (node) { if (node.parentNode && !node.isLeaf()) node.updateFolderStatus() }) this.updateStatusIndicator(); }, fetchJsonReport : function () { var me = this 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.loadJsonReport(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 }) } }) }, onFilterApplied : function () { if (this.autoLaunch) { this.runAll() } else if (this.getOption('autoRun')) { var checked = this.getChecked() // either launch the suite for checked tests or for all this.project.launch(checked.length && 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('checked', checked) // when unchecking the node - uncheck the parent node (folder) as well if (!checked && testFile.parentNode) me.setNodeChecked(testFile.parentNode, false, true, true) // only cascade for folders and when `doNotCascade` is false if (!testFile.isLeaf() && !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 && 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('checked') && testFileRecord.isLeaf()) descriptors.push(testFileRecord.get('descriptor')) }) return descriptors }, runChecked : function () { var checked = this.getChecked(); if (checked.length > 0) { this.project.launch(this.getChecked()) } }, runFailed : function () { var descriptors = [] this.forEachTestFile(function (testFileRecord) { var test = testFileRecord.get('test') if (test && test.isFailed()) descriptors.push(testFileRecord.get('descriptor')) }) if (descriptors.length > 0) { this.project.launch(descriptors) } }, runAll : function () { var allDesc = [] this.forEachTestFile(function (testFile) { if (testFile.isLeaf()) allDesc.push(testFile.get('descriptor')) }) if (allDesc.length > 0) { this.project.launch(allDesc) } }, // this method is never called currently, because there'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('isStarting')) { testFileRecord.set('isStarting', 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's running in popups (not iframes), since we can't show any visual accompaniment for them if (!(test.scopeProvider instanceof Scope.Provider.IFrame)) return false; // if there is a "forced to be on top" 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'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 "forced on top" 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('assertionsStore').removeAll(true) testRecord.reject(); // || false is required for TreeView - it checks that "checked" field contains Boolean testRecord.set('checked', 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't know yet about missing test files onTestSuiteStart : function (descriptors) { Ext.getBody().addCls('testsuite-running'); 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 && !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('testsuite-running'); this.updateStatusIndicator(); // Without this the keyboard hotkey won'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 && currentSelection.getId() == test.url) { var resultPanel = this.slots.resultPanel resultPanel.showTest(test, testRecord.get('assertionsStore')) 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('assertionsStore'), 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('assertionsStore') assertionStore.getNodeById(result.id).remove() } }, // only triggered for "root" 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({ 'passCount' : test.getPassCount(), 'failCount' : test.getFailCount(), 'todoPassCount' : test.getTodoPassCount(), 'todoFailCount' : test.getTodoFailCount(), 'time' : test.getDuration() + 'ms' }); testRecord.endEdit() testRecord.parentNode && 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('assertionsStore'), 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) && !test.isTodo) { testRecord.set('isFailed', true) testRecord.parentNode && testRecord.parentNode.updateFolderStatus() } }, getOption : function (name, onlyOwn) { switch (name) { case 'selection' : 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 'selection' : return this.selection = value || {} case 'collapsedNodes' : return this.collapsedNodes = value case 'filter' : return this.filter = value case 'filterGroups' : return this.filterGroups = value case 'observerMode' : project.setObserverMode(value) break // UI only option, does not correspond to viewport/project config case 'mouseSimSlow' : if (value) { project[ 'speedRun' ] = false project[ 'turboMode' ] = false } break // UI only option, does not correspond to viewport/project config case 'mouseSimFast' : if (value) { project[ 'speedRun' ] = true project[ 'turboMode' ] = false } break // UI only option, does not correspond to viewport/project config case 'mouseSimFastest' : if (value) { project[ 'speedRun' ] = true project[ 'turboMode' ] = true } break default : return project[ name ] = value } }, getState : function (onlyOwnProperties) { // getState is also called early, when there's no child components // the absense of "this.slots" indicates this var domContainer = this.slots && this.down('domcontainer') 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([ 'autoRun', 'speedRun', 'turboMode', 'viewDOM', 'transparentEx', 'breakOnFail', 'debuggerOnFail', 'observerMode' ], 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('checked')) checked[treeNode.getId()] = 1 }) return checked }, getCollapsedFolders : function () { var collapsed = {} this.testsStore.forEach(function (treeNode) { if (!treeNode.isLeaf() && !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 'test-run-' + 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 + '-collapsed') if (!state.selection) state.selection = Ext.state.Manager.get(stateId + '-selection') return state }, saveState : function () { var stateId = this.getStateId() var state = this.getState(true) Ext.state.Manager.set(stateId + '-collapsed', state.collapsedNodes) Ext.state.Manager.set(stateId + '-selection', 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('descriptor'); // 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('test') // if there's at least one test - state is not neutral if (test && test.isFinished()) { // isNeutral = false // allGreen = allGreen && test.isPassed() // hasFailures = hasFailures || test.isFailed() totalPassed += test.getPassCount() totalFailed += test.getFailCount() } }) this.slots.filesTree.updateStatus(totalPassed, totalFailed); }, /** * 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) && window.console) { console.log('Wrong Ext JS version detected.', 'Siesta expects that you use Ext JS version >= ' + this.expectedExtJSVersion + '. You may experience errors when using another version'); } if (!this.project.isAutomated) setTimeout(Ext.Function.bind(this.deferredSetup, this), 1000); }, deferredSetup : function() { Ext.QuickTips && Ext.QuickTips.init(); if (this.enableVersionCheck && Siesta.Project.Browser.UI.VersionUpdateButton) { new Siesta.Project.Browser.UI.VersionUpdateButton(); } }, onManualCloseOfForcedIframe : function (test) { var domContainer = this.down('domcontainer') if (domContainer && domContainer.test == test) domContainer.alignIFrame(true) } }) //eof Siesta.Project.Browser.UI.Viewport