/*

Siesta 5.1.0
Copyright(c) 2009-2018 Bryntum AB
https://bryntum.com/contact
https://bryntum.com/products/siesta/license

*/
/**
@class Siesta.Test.ExtJS.Grid

This is a mixin, with helper methods for testing functionality relating to ExtJS grids. This mixin is being consumed by {@link Siesta.Test.ExtJS}

*/
Role('Siesta.Test.ExtJS.Grid', {

    requires        : [ 'waitFor', 'pass', 'fail', 'typeOf' ],


    methods : {
        /**
         * Waits for the rows of a gridpanel or tree panel (or view) to render and then calls the supplied callback. Please note, that if the store of the grid has no records,
         * the condition for this waiter will never be fullfilled.
         *
         * @param {Ext.view.Table/Ext.panel.Table/String} view The view or a ComponentQuery matching a view
         * @param {Function} callback A function to call when the condition has been met.
         * @param {Object} scope The scope for the callback
         * @param {Int} timeout The maximum amount of time to wait for the condition to be fulfilled. Defaults to the {@link Siesta.Test.ExtJS#waitForTimeout} value.
         */
        waitForRowsVisible : function(view, callback, scope, timeout) {
            if (typeof view === 'function') {
                timeout     = scope;
                scope       = callback;
                callback    = view;
                view       = this.Ext() && this.cq1('tableview') || 'tableview';
            }

            var cmp = this.Ext() && this.normalizeComponent(view, true);
            var me = this;

            if (!cmp && typeof view === 'string') {
                // Make sure CQ returns a result first
                return this.waitForCQ(view, function(result) { this.waitForRowsVisible(result[0], callback, scope, timeout); }, this);
            } else {
                var checkerFn;

                // Handle case of locking grid (Ext JS 4+ only)
                if (cmp.normalGrid) {
                    var selector = cmp.normalGrid.getView().itemSelector;

                    checkerFn = function() {
                        if (!cmp.rendered || !cmp.normalGrid.rendered || !cmp.lockedGrid.rendered) return;

                        var lockedResult = this.$(selector, cmp.lockedGrid.getView().getEl().dom);
                        var normalResult = this.$(selector, cmp.normalGrid.getView().getEl().dom);

                        if (lockedResult.length > 0 && normalResult.length > 0) {
                            return {
                                lockedRows : lockedResult,
                                normalRows : normalResult
                            };
                        }
                    }
                } else {
                    var view = (cmp.getView && cmp.getView()) || cmp;
                    var selector = view.itemSelector || view.rowSelector; // Handling Ext 4 + Ext 3 cases

                    checkerFn = function() {
                        if (!cmp.rendered) return;

                        var result = this.$(selector, view.el.dom);

                        if (result.length > 0) {
                            return result;
                        }
                    }
                }


                return this.waitFor({
                    method          : checkerFn,
                    callback        : function() {
                        // Grid might be refreshing itself multiple times during initialization which can
                        // break tests easily
                        var as = me.beginAsync();

                        me.global.setTimeout(function(){
                            me.endAsync(as);
                            callback.call(scope || me);
                        }, 100);
                    },
                    timeout         : timeout,
                    assertionName   : 'waitForRowsVisible',
                    description     : ' ' + Siesta.Resource('Siesta.Test.ExtJS.Grid').get('waitForRowsVisible') + ' "' + cmp.id + '"'
                });
            }
        },

        /**
         * Utility method which returns the first grid row element.
         *
         * Please also refer to the {@link #getRow} method for additional information about the buffered renderer case.
         *
         * @param {Ext.panel.Table/String} panel The panel or a ComponentQuery matching a panel
         * @return {Ext.Element} The element of the first row in the grid.
         */
        getFirstRow : function(grid) {
            grid = this.normalizeComponent(grid);

            return this.getRow(grid, 0);
        },

        /**
         * Utility method which returns the first grid cell element (the one at (0, 0) coordinates).
         *
         * Please also refer to the {@link #getRow} method for additional information about the buffered renderer case.
         *
         * @param {Ext.panel.Table/String} panel The panel or a ComponentQuery matching a panel
         *
         * @return {Ext.Element} The element of the first cell in the grid.
         */
        getFirstCell : function(panel) {
            panel = this.normalizeComponent(panel);

            return this.getCell(panel, 0, 0);
        },

        /**
         * Utility method which returns a grid row element. If the grid is locking, then the row element from the locked grid is returned
         * (from the left part).
         *
         * Please note, that if the grid uses buffered rendering, you need to make sure the row with the required index
         * is currently rendered (as buffered renderer only renders a part of the dataset). This can be done
         * with the `grid.ensureVisible()` call, please refer to ExtJS grid docs:
         *

    grid.ensureVisible(80, {
        callback : function() {
            var row     = t.getRow(80);
            var cell    = t.getCell(grid, 80, 0);
        }
    });

         *
         * @param {Ext.panel.Table/String} panel The table panel instance or a ComponentQuery matching a panel
         * @param {Int} index The row index in the whole dataset
         *
         * @return {Ext.Element} The element corresponding to the grid row.
         */
        getRow : function (grid, index) {
            var Ext         = this.Ext();

            grid            = this.normalizeComponent(grid);

            var domNode;

            // Sencha Modern
            if (Ext.grid.Grid && (grid instanceof Ext.grid.Grid)) {
                var rowCmp  = grid.getViewItems()[ index ];

                domNode     = rowCmp && rowCmp.element;
            } else {
                // Sencha Classic
                // if this is a locking grid, grab from locked grid
                grid        = grid.lockedGrid || grid;

                if (grid.getView().bufferedRenderer) {
                    var record  = grid.getStore().getRange(index, index + 1)[ 0 ]

                    if (record) {
                        domNode = grid.getView().getNode(record)
                    }
                } else
                    domNode     = grid && this.$(grid.getView().itemSelector, grid.getView().getEl().dom)[ index ];
            }

            return domNode && Ext.get(domNode);
        },

        /**
         * Utility method which returns the cell at the supplied row and col position.
         *
         * Please also refer to the {@link #getRow} method for additional information about the buffered renderer case.
         *
         * @param {Ext.panel.Table/String} panel The panel or a ComponentQuery matching a panel
         * @param {Int} row The row index
         * @param {Int} column The column index
         *
         * @return {Ext.Element} The element of the grid cell at specified position.
         */
        getCell : function(grid, row, col) {
            grid            = this.normalizeComponent(grid);

            var rowEl       = grid && this.getRow(grid, row);
            var cellNode    = rowEl && this.$(grid.view.cellSelector, rowEl.dom)[ col ];

            return cellNode && this.Ext().get(cellNode);
        },

        /**
         * Utility method which returns the last cell for the supplied row.
         *
         * @param {Ext.panel.Table/String} panel The panel or a ComponentQuery matching a panel
         * @param {Int} row The row index
         *
         * @return {Ext.Element} The element of the grid cell at specified position.
         */
        getLastCellInRow : function(grid, row) {
            grid = this.normalizeComponent(grid);

            return this.getCell(grid, row, grid.headerCt.getColumnCount() - 1);
        },

        /**
         * This assertion passes if the passed string is found in the passed grid's cell element.
         *
         * @param {Ext.panel.Table/String} panel The panel or a ComponentQuery matching a panel
         * @param {Int} row The row index
         * @param {Int} column The column index
         * @param {String/RegExp} string The string to find or RegExp to match
         * @param {String} [description] The description for the assertion
         */
        matchGridCellContent : function(grid, rowIndex, colIndex, string, description) {
            grid = this.normalizeComponent(grid);

            var view = grid.getView(),
                Ext = this.Ext(),
                cell = this.getCell(grid, rowIndex, colIndex).child('.' + Ext.baseCSSPrefix + 'grid-cell-inner');

            var isRegExp    = this.typeOf(string) == 'RegExp';
            var content     = cell.dom.innerHTML;

            if (isRegExp ? string.test(content) : content.indexOf(string) != -1) {
                this.pass(description, {
                    descTpl     : isRegExp ? 'Cell content {content} matches regexp {string}' : 'Cell content {content} has a string {string}',
                    content     : content,
                    string      : string
                });
            } else {
                this.fail(description, {
                    assertionName   : 'matchGridCellContent',

                    got         : cell.dom.innerHTML,
                    gotDesc     : 'Cell content',

                    need        : string,
                    needDesc    : 'String matching',

                    annotation  : 'Row index: ' + rowIndex + ', column index: ' + colIndex
                });
            }
        },


        /**
         * This method performs either a click or double click on the specified grid cell
         * (depending from the [clicksToEdit](http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.grid.plugin.Editing-cfg-clicksToEdit)
         * config of its editing plugin), then waits until the `input` selector appears under the cursor and calls the provided callback.
         * The callback will receive the DOM `<input> element as the 1st argument.
         *
         * In some browsers the editor is shown with delay, so its highly recommended to use this method when editing cells.
         * Typical usage will be:
         *

    t.chain(
        function (next) {
            t.clickToEditCell(grid, 0, 1, next)
        },
        function (next, inputEl) {
            t.type(inputEl, "my text", next)
        }
    )

         *
         *
         * @param {Ext.grid.Panel/String} grid The grid panel or a ComponentQuery matching a panel
         * @param {Int} rowIndex The row index
         * @param {Int} colIndex The column index
         * @param {Function} callback The callback to call once the `input` selector appears under the cursor
         * @param {String} selector Custom selector to wait for, instead of `input`.
         */
        clickToEditCell : function (grid, rowIndex, colIndex, callback, selector) {
            var Ext             = this.getExt()

            grid                = this.normalizeComponent(grid);

            var editingPlugin   = grid && grid.editingPlugin

            if (!editingPlugin || !(editingPlugin instanceof Ext.grid.plugin.CellEditing)) {
                this.fail("No grid, or grid has no editing plugin, or its not a Ext.grid.plugin.CellEditing plugin")

                callback && callback(null)

                return
            }

            var me      = this

            this[ editingPlugin.clicksToEdit == 2 ? 'doubleClick' : 'click' ](this.getCell(grid, rowIndex, colIndex), function () {
                // manually force editing if it didn't get started by the click
                if (!editingPlugin.getActiveEditor()) editingPlugin.startEditByPosition({ row : rowIndex, column : colIndex })

                me.waitForSelectorAtCursor(selector || '.x-editor,input', callback)
            })
        },

        getTrimmedCellContent : function(grid, row, column) {
            var cell = this.getCell(grid, row, column);

            return $.trim(this.$(cell.dom).text());
        },

        /**
         * Assertion method which passes if the grid cell is empty.
         *
         * @param {Ext.panel.Table/String} panel The panel or a ComponentQuery matching a panel
         * @param {Int} rowIndex The row index
         * @param {Int} colIndex The column index
         * @param {String} message The assertion message
         *
         * @return {Ext.Element} The element of the grid cell at specified position.
         */
        assertCellIsEmpty : function(grid, row, column, message) {
            this.is(this.getTrimmedCellContent(grid, row, column), '', message);
        },

        /**
         * Wait-for method which waits until the chosen grid cell is empty.
         *
         * @param {Ext.panel.Table/String} panel The panel or a ComponentQuery matching a panel
         * @param {Int} rowIndex The row index
         * @param {Int} colIndex The column index
         * @param {Object} scope The 'this' object for the callback
         * @param {Int} timeout The timeout in ms
         * @param {Function} callback The callback called when the condition is fulfilled
         *
         * @return {Ext.Element} The element of the grid cell at specified position.
         */
        waitForCellEmpty : function(grid, row, column, scope, timeout, callback) {
            if (typeof scope === 'function') {
                callback    = scope;
            } else if (typeof timeout === 'function') {
                callback    = timeout;
            }

            this.waitFor({
                method          : function() {
                    return this.getTrimmedCellContent(grid, row, column).length === 0;
                },
                callback        : function() {
                    callback.call(scope || this);
                },
                timeout         : timeout,
                assertionName   : 'waitForCellEmpty',
                description     : ' ' + Siesta.Resource('Siesta.Test.ExtJS.Grid').get('waitForCellEmpty')
            });
        }
    }
});