Commit ed7c36a8 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Refactor tests view.

Extract BatchRunner component, responsible for actually running the
tests.

The Index view then simply commands the BatchRunner and listens events
from it.

All this resulting in much simpler and actually even shorter code.
parent d9dab690
Loading
Loading
Loading
Loading
+115 −0
Original line number Diff line number Diff line
/**
 * Component for running multiple tests in batch mode.
 *
 * Calling the #run method with bunch of Docs.model.Test records will
 * start the runner, firering #start event.  After the completion of
 * every test the #statuschange event is fired.  Finally #finish will
 * fire when all tests are done.
 */
Ext.define('Docs.view.tests.BatchRunner', {
    extend: 'Ext.container.Container',
    requires: ['Docs.view.examples.Inline'],

    initComponent: function() {
        this.addEvents(
            /**
             * @event
             * Fired when running started.
             */
            "start",
            /**
             * @event
             * Fired when running finished.
             */
            "finish",
            /**
             * @event
             * Fired after every test is run.
             * @param {Object} status The current runner status, which contains:
             * @param {Number} status.pass Number of successful tests so far.
             * @param {Number} status.fail Number of failed tests so far.
             * @param {Number} status.total Total number of tests ordered for run,
             * @param {Docs.model.Test[]} status.remaining Remaining tests to be run.
             */
            "statuschange"
        );

        this.callParent(arguments);
    },

    /**
     * Executes a batch of tests.
     *
     * @param {Docs.model.Test[]} tests Array of tests to run
     */
    run: function(tests) {
        this.fireEvent("start");

        this.runNext({
            pass: 0,
            fail: 0,
            total: tests.length,
            remaining: tests
        });
    },

    // Runs the next example
    runNext: function(config) {
        this.fireEvent("statuschange", config);

        // Exit when all done
        if (!config.remaining || config.remaining.length < 1) {
            this.fireEvent("finish");
            return;
        }

        // Take next test from queue
        var record = config.remaining.shift();

        // Disable the preview option (we activate the preview manually)
        var options = record.get('options');
        options.preview = false;

        // Override alert() with empty function, so we don't get
        // disturbing popups during test runs.
        var safeAlert = "var alert = function(){};\n";

        var example = Ext.create('Docs.view.examples.Inline', {
            cls: 'doc-test-preview',
            height: 0,
            value: safeAlert + record.get('code'),
            options: options,
            listeners: {
                previewsuccess: function(preview) {
                    this.onSuccess(record, config);
                },
                previewfailure: function(preview, error) {
                    this.onFailure(record, config, error);
                },
                scope: this
            }
        });

        // Replace previous example with new one
        this.removeAll();
        this.add(example);

        // Execute the example
        example.showPreview();
    },

    onSuccess: function(record, config) {
        record.set('status', 'success');
        record.commit();
        config.pass++;
        this.runNext(config);
    },

    onFailure: function(record, config, error) {
        record.set('status', 'failure');
        record.set('message', error.toString());
        record.commit();
        config.fail++;
        this.runNext(config);
    }
});
+38 −162
Original line number Diff line number Diff line
@@ -3,7 +3,10 @@
 */
Ext.define('Docs.view.tests.Index', {
    extend: 'Ext.container.Container',
    requires: ['Docs.model.Test'],
    requires: [
        'Docs.model.Test',
        'Docs.view.tests.BatchRunner'
    ],
    alias: 'widget.testsindex',

    layout: {
@@ -47,7 +50,23 @@ Ext.define('Docs.view.tests.Index', {
                    flex: 1,
                    dataIndex: 'message'
                }
            ]
            ],
            listeners: {
                itemdblclick: function(grid, record) {
                    this.batchRunner.run([record]);
                },
                scope: this
            }
        });

        this.batchRunner = Ext.create('Docs.view.tests.BatchRunner', {
            height: 0,
            listeners: {
                start: this.disable,
                finish: this.enable,
                statuschange: this.updateTestStatus,
                scope: this
            }
        });

        this.items = [
@@ -55,10 +74,6 @@ Ext.define('Docs.view.tests.Index', {
                html: '<h1>Inline examples test page</h1>',
                height: 30
            },
            {
                itemId: 'testrunner',
                height: 0
            },
            {
                itemId: 'testcontainer',

@@ -82,7 +97,11 @@ Ext.define('Docs.view.tests.Index', {
                                xtype: 'button',
                                itemId: 'run-selected-button',
                                text: 'Run Selected',
                                margin: 5
                                margin: 5,
                                handler: function() {
                                    this.batchRunner.run(this.grid.getSelectionModel().getSelection());
                                },
                                scope: this
                            },
                            {
                                html: 'or',
@@ -92,7 +111,11 @@ Ext.define('Docs.view.tests.Index', {
                                xtype: 'button',
                                itemId: 'run-all-button',
                                text: 'Run All Examples',
                                margin: 5
                                margin: 5,
                                handler: function() {
                                    this.batchRunner.run(this.store.getRange());
                                },
                                scope: this
                            },
                            {
                                itemId: 'testStatus',
@@ -102,14 +125,11 @@ Ext.define('Docs.view.tests.Index', {
                    },
                    this.grid
                ]
            }
            },
            this.batchRunner
        ];

        this.callParent(arguments);

        this.down("#run-all-button").on('click', this.runAll, this);
        this.down("#run-selected-button").on('click', this.runSelected, this);
        this.grid.on('itemdblclick', this.runSingle, this);
    },

    /**
@@ -130,70 +150,10 @@ Ext.define('Docs.view.tests.Index', {
        this.setStatus(true, this.store.getCount() + " examples loaded.");
    },

    /**
     * Executes an example.
     *
     * @param {Object} config The test configuration.
     * @private
     */
    runExample: function(config) {
        if (!config.examples || config.examples.length < 1) {
            return;
        }

        if (config.fail + config.pass === 0) {
            this.disable();
        }

        this.clearTestRunner();
        var testRunner = this.getComponent('testrunner');
        var record = config.examples.shift();
        var options = record.get('options');
        options.preview = false; // always disable the preview option

        // Override alert() with empty function, so we don't get
        // disturbing popups during test runs.
        var safeAlert = "var alert = function(){};\n";

        var example = testRunner.add(
            Ext.create('Docs.view.examples.Inline', {
                cls: 'doc-test-preview',
                height: 0,
                value: safeAlert + record.get('code'),
                options: options
            })
        );

        example.on('previewsuccess', Ext.bind(this.onPreviewSuccess, this, [record, config], true), this);
        example.on('previewfailure', Ext.bind(this.onPreviewFailure, this, [record, config], true), this);
        example.showPreview();
    },

    /**
     * Removes child elements from testrunner component.
     *
     * @private
     */
    clearTestRunner: function() {
        var testRunner = this.getComponent('testrunner');
        testRunner.removeAll();
    },

    /**
     * Renders test result to dom.
     *
     * @param {Object} config The test configuration.
     * @private
     */
    showResult: function(config) {
        var totalTested = config.pass+config.fail;
        this.setStatus(config.fail === 0, totalTested + '/' + config.total + ' examples tested, ' + config.fail + ' failures');

        if (config.examples.length < 1) {
            this.enable();
        } else {
            this.runExample(config);
        }
    // updates current running status of tests
    updateTestStatus: function(status) {
        var totalTested = status.pass + status.fail;
        this.setStatus(status.fail === 0, totalTested + '/' + status.total + ' examples tested, ' + status.fail + ' failures');
    },

    /**
@@ -205,90 +165,6 @@ Ext.define('Docs.view.tests.Index', {
    setStatus: function(ok, message) {
        var cls = ok ? 'doc-test-success' : 'doc-test-failure';
        this.down("#testStatus").update('<span class="' + cls + '">' + message + '</span>');
    },

    /**
     * Run link click handler.
     *
     * @param {Ext.grid.Panel} grid The grid that was clicked.
     * @param {Docs.model.Test} record The record that was clicked.
     * @private
     */
    runSingle: function(grid, record) {
        this.runExample({
            pass: 0,
            fail: 0,
            total: 1,
            examples: [record]
        });
    },

    /**
     * RunAll button click handler.
     *
     * @private
     */
    runAll: function() {
        var examples = [];
        this.store.each(function(record) {
            examples.push(record);
        });
        this.runExample({
            pass: 0,
            fail: 0,
            total: examples.length,
            examples: examples
        });
    },

    /**
     * RunSelected button click handler.
     *
     * @private
     */
    runSelected: function() {
        var examples = this.grid.getSelectionModel().getSelection();
        this.runExample({
            pass: 0,
            fail: 0,
            total: examples.length,
            examples: examples
        });
    },

    /**
     * previewsuccess event handler
     *
     * @param {Ext.Component} preview The preview component.
     * @param {Object} options The event options.
     * @param {Docs.model.Test} record The successful record.
     * @param {Object} config The test configuration.
     * @private
     */
    onPreviewSuccess: function(preview, options, record, config) {
        this.clearTestRunner();
        record.set('status', 'success');
        record.commit();
        config.pass++;
        this.showResult(config);
    },

    /**
     * previewfailure event handler
     *
     * @param {Ext.Component} preview The preview component.
     * @param {Error} error The Error thrown during the example.
     * @param {Object} options The event options.
     * @param {Docs.model.Test} record The successful record.
     * @param {Object} config The test configuration.
     * @private
     */
    onPreviewFailure: function(preview, e, obj, record, config) {
        this.clearTestRunner();
        record.set('status', 'failure');
        record.set('message', e.toString());
        record.commit();
        config.fail++;
        this.showResult(config);
    }

});