mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-04 06:37:55 +08:00 
			
		
		
		
	Updated Node (6 -> 20), NPM (6 -> 10), jsdom (11.11.0 -> 24.1.0), leche (2.2.3 -> 2.3.0), mocha (5.2.0 -> 6.2.3)
		
			
				
	
	
		
			1445 lines
		
	
	
		
			60 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1445 lines
		
	
	
		
			60 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
var assert = require('chai').assert;
 | 
						|
var sinon;
 | 
						|
var withData = require('leche').withData;
 | 
						|
var jsdom = require('mocha-jsdom');
 | 
						|
 | 
						|
var fs = require('fs');
 | 
						|
var vm = require('vm');
 | 
						|
 | 
						|
var StringUtils = {
 | 
						|
    /**
 | 
						|
     * Removes line breaks and redundant whitespaces from the given string. Used to compare HTML strings easier,
 | 
						|
     * regardless of the formatting.
 | 
						|
     * @param str Initial string to clean
 | 
						|
     * @returns {string} Cleaned string
 | 
						|
     */
 | 
						|
    cleanHTML: function (str) {
 | 
						|
        return str.replace(/\r?\n|\r|\s\s+/g, '');
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
describe('yii', function () {
 | 
						|
    var yiiPath = 'framework/assets/yii.js';
 | 
						|
    var jQueryPath = 'vendor/bower-asset/jquery/dist/jquery.js';
 | 
						|
    var pjaxPath = 'vendor/bower-asset/yii2-pjax/jquery.pjax.js';
 | 
						|
    var sandbox;
 | 
						|
    var $;
 | 
						|
    var yii;
 | 
						|
    var yiiGetBaseCurrentUrlStub;
 | 
						|
    var yiiGetCurrentUrlStub;
 | 
						|
 | 
						|
    function registerPjax() {
 | 
						|
        var code = fs.readFileSync(pjaxPath);
 | 
						|
        var script = new vm.Script(code);
 | 
						|
        var sandbox = {jQuery: $, window: window, navigator: window.navigator};
 | 
						|
        var context = new vm.createContext(sandbox);
 | 
						|
        script.runInContext(context);
 | 
						|
    }
 | 
						|
 | 
						|
    function registerTestableCode() {
 | 
						|
        registerPjax();
 | 
						|
 | 
						|
        var code = fs.readFileSync(yiiPath);
 | 
						|
        var script = new vm.Script(code);
 | 
						|
        sandbox = {window: window, document: window.document, XMLHttpRequest: window.XMLHttpRequest};
 | 
						|
        var context = new vm.createContext(sandbox);
 | 
						|
 | 
						|
        script.runInContext(context);
 | 
						|
        yii = sandbox.window.yii;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Mapping of pjax data attributes with according plugin options
 | 
						|
     * @type {{}}
 | 
						|
     */
 | 
						|
    var pjaxAttributes = {
 | 
						|
        'data-pjax-push-state': 'push',
 | 
						|
        'data-pjax-replace-state': 'replace',
 | 
						|
        'data-pjax-scrollto': 'scrollTo',
 | 
						|
        'data-pjax-push-redirect': 'pushRedirect',
 | 
						|
        'data-pjax-replace-redirect': 'replaceRedirect',
 | 
						|
        'data-pjax-skip-outer-containers': 'skipOuterContainers',
 | 
						|
        'data-pjax-timeout': 'timeout'
 | 
						|
    };
 | 
						|
 | 
						|
    /**
 | 
						|
     * Add pjax related attributes to all elements with "data-pjax" attribute. Used to prevent copy pasting and for
 | 
						|
     * better readability of the test HTML data.
 | 
						|
     */
 | 
						|
    function addPjaxAttributes() {
 | 
						|
        $.each(pjaxAttributes, function (name, value) {
 | 
						|
            $('[data-pjax]').attr(name, value);
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
    jsdom({
 | 
						|
        html: fs.readFileSync('tests/js/data/yii.html', 'utf-8'),
 | 
						|
        src: fs.readFileSync(jQueryPath, 'utf-8'),
 | 
						|
        url: "http://foo.bar"
 | 
						|
    });
 | 
						|
 | 
						|
    before(function () {
 | 
						|
        $ = window.$;
 | 
						|
        registerTestableCode();
 | 
						|
        sinon = require('sinon');
 | 
						|
        addPjaxAttributes();
 | 
						|
        yiiGetBaseCurrentUrlStub = sinon.stub(yii, 'getBaseCurrentUrl', function () {
 | 
						|
            return 'http://foo.bar';
 | 
						|
        });
 | 
						|
        yiiGetCurrentUrlStub = sinon.stub(yii, 'getCurrentUrl', function () {
 | 
						|
            return 'http://foo.bar/';
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    after(function () {
 | 
						|
        yiiGetBaseCurrentUrlStub.restore();
 | 
						|
        yiiGetCurrentUrlStub.restore();
 | 
						|
    });
 | 
						|
 | 
						|
    describe('getCsrfParam method', function () {
 | 
						|
        it('should return current CSRF parameter name', function () {
 | 
						|
            assert.equal(yii.getCsrfParam(), '_csrf');
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('getCsrfToken method', function () {
 | 
						|
        it('should return current CSRF parameter value', function () {
 | 
						|
            assert.equal(yii.getCsrfToken(), 'foobar');
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('CSRF modifying methods', function () {
 | 
						|
        var initialCsrfParam;
 | 
						|
        var initialCsrfToken;
 | 
						|
 | 
						|
        beforeEach(function () {
 | 
						|
            initialCsrfParam = $('meta[name="csrf-param"]').attr('content');
 | 
						|
            initialCsrfToken = $('meta[name="csrf-token"]').attr('content');
 | 
						|
        });
 | 
						|
 | 
						|
        // Restore CSRF parameter name and value to initial values because they are used in different tests
 | 
						|
 | 
						|
        afterEach(function () {
 | 
						|
            $('meta[name="csrf-param"]').attr('content', initialCsrfParam);
 | 
						|
            $('meta[name="csrf-token"]').attr('content', initialCsrfToken);
 | 
						|
        });
 | 
						|
 | 
						|
        describe('setCsrfToken method', function () {
 | 
						|
            it('should update CSRF parameter name and value with new values', function () {
 | 
						|
                yii.setCsrfToken('_csrf1', 'foobar1');
 | 
						|
 | 
						|
                assert.equal(yii.getCsrfParam(), '_csrf1');
 | 
						|
                assert.equal(yii.getCsrfToken(), 'foobar1');
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        describe('refreshCsrfToken method', function () {
 | 
						|
            it('should assign CSRF token values for all forms during initialization', function () {
 | 
						|
                assert.equal($('#form1').find('input[name="_csrf"]').val(), 'foobar');
 | 
						|
                assert.equal($('#form2').find('input[name="_csrf"]').val(), 'foobar');
 | 
						|
            });
 | 
						|
 | 
						|
            it('should update CSRF token values for all forms after modifying current CSRF token value', function () {
 | 
						|
                $('meta[name="csrf-token"]').attr('content', 'foobar1');
 | 
						|
                yii.refreshCsrfToken();
 | 
						|
 | 
						|
                assert.equal($('#form1').find('input[name="_csrf"]').val(), 'foobar1');
 | 
						|
                assert.equal($('#form2').find('input[name="_csrf"]').val(), 'foobar1');
 | 
						|
            });
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('confirm method', function () {
 | 
						|
        var windowConfirmStub;
 | 
						|
        var confirmed;
 | 
						|
        var okSpy;
 | 
						|
        var cancelSpy;
 | 
						|
 | 
						|
        beforeEach(function () {
 | 
						|
            windowConfirmStub = sinon.stub(window, 'confirm', function () {
 | 
						|
                return confirmed;
 | 
						|
            });
 | 
						|
            okSpy = sinon.spy();
 | 
						|
            cancelSpy = sinon.spy();
 | 
						|
        });
 | 
						|
 | 
						|
        afterEach(function () {
 | 
						|
            windowConfirmStub.restore();
 | 
						|
            okSpy.reset();
 | 
						|
            cancelSpy.reset();
 | 
						|
        });
 | 
						|
 | 
						|
        withData({
 | 
						|
            'ok and cancel not set, "OK" selected': [{
 | 
						|
                setOk: false,
 | 
						|
                setCancel: false,
 | 
						|
                confirmChoice: true,
 | 
						|
                expectOkCalled: false,
 | 
						|
                expectCancelCalled: false
 | 
						|
            }],
 | 
						|
            'ok and cancel not set, "Cancel" selected': [{
 | 
						|
                setOk: false,
 | 
						|
                setCancel: false,
 | 
						|
                confirmChoice: false,
 | 
						|
                expectOkCalled: false,
 | 
						|
                expectCancelCalled: false
 | 
						|
            }],
 | 
						|
            'ok set, "OK" selected': [{
 | 
						|
                setOk: true,
 | 
						|
                setCancel: false,
 | 
						|
                confirmChoice: true,
 | 
						|
                expectOkCalled: true,
 | 
						|
                expectCancelCalled: false
 | 
						|
            }],
 | 
						|
            'ok set, "Cancel" selected': [{
 | 
						|
                setOk: true,
 | 
						|
                setCancel: false,
 | 
						|
                confirmChoice: false,
 | 
						|
                expectOkCalled: false,
 | 
						|
                expectCancelCalled: false
 | 
						|
            }],
 | 
						|
            'cancel set, "OK" selected': [{
 | 
						|
                setOk: false,
 | 
						|
                setCancel: true,
 | 
						|
                confirmChoice: true,
 | 
						|
                expectOkCalled: false,
 | 
						|
                expectCancelCalled: false
 | 
						|
            }],
 | 
						|
            'cancel set, "Cancel" selected': [{
 | 
						|
                setOk: false,
 | 
						|
                setCancel: true,
 | 
						|
                confirmChoice: false,
 | 
						|
                expectOkCalled: false,
 | 
						|
                expectCancelCalled: true
 | 
						|
            }],
 | 
						|
            'ok and cancel set, "OK" selected': [{
 | 
						|
                setOk: true,
 | 
						|
                setCancel: true,
 | 
						|
                confirmChoice: true,
 | 
						|
                expectOkCalled: true,
 | 
						|
                expectCancelCalled: false
 | 
						|
            }],
 | 
						|
            'ok and cancel set, "Cancel" selected': [{
 | 
						|
                setOk: true,
 | 
						|
                setCancel: true,
 | 
						|
                confirmChoice: false,
 | 
						|
                expectOkCalled: false,
 | 
						|
                expectCancelCalled: true
 | 
						|
            }]
 | 
						|
        }, function (data) {
 | 
						|
            var setOk = data.setOk;
 | 
						|
            var setCancel = data.setCancel;
 | 
						|
            var confirmChoice = data.confirmChoice;
 | 
						|
            var expectOkCalled = data.expectOkCalled;
 | 
						|
            var expectCancelCalled = data.expectCancelCalled;
 | 
						|
 | 
						|
            var message = 'should return undefined, confirm should be called once with according message, ';
 | 
						|
            if (expectOkCalled && !expectCancelCalled) {
 | 
						|
                message += 'ok callback should be called once';
 | 
						|
            } else if (!expectOkCalled && expectCancelCalled) {
 | 
						|
                message += 'cancel callback should be called once';
 | 
						|
            } else if (!expectOkCalled && !expectCancelCalled) {
 | 
						|
                message += 'ok and cancel callbacks should not be called';
 | 
						|
            } else {
 | 
						|
                message += 'ok and cancel callbacks should be called once';
 | 
						|
            }
 | 
						|
 | 
						|
            it(message, function () {
 | 
						|
                confirmed = confirmChoice;
 | 
						|
 | 
						|
                var result = yii.confirm('Are you sure?', setOk ? okSpy : undefined, setCancel ? cancelSpy : undefined);
 | 
						|
 | 
						|
                assert.isUndefined(result);
 | 
						|
                assert.isTrue(windowConfirmStub.calledOnce);
 | 
						|
                assert.deepEqual(windowConfirmStub.getCall(0).args, ['Are you sure?']);
 | 
						|
                expectOkCalled ? assert.isTrue(okSpy.calledOnce) : assert.isFalse(okSpy.called);
 | 
						|
                expectCancelCalled ? assert.isTrue(cancelSpy.calledOnce) : assert.isFalse(cancelSpy.called);
 | 
						|
            });
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('handleAction method', function () {
 | 
						|
        var windowLocationAssignStub;
 | 
						|
        var pjaxClickStub;
 | 
						|
        var pjaxSubmitStub;
 | 
						|
        var formSubmitsCount;
 | 
						|
        var initialFormsCount;
 | 
						|
        var $savedSubmittedForm;
 | 
						|
 | 
						|
        beforeEach(function () {
 | 
						|
            windowLocationAssignStub = sinon.stub(window.location, 'assign');
 | 
						|
            pjaxClickStub = sinon.stub($.pjax, 'click');
 | 
						|
            pjaxSubmitStub = sinon.stub($.pjax, 'submit');
 | 
						|
            initialFormsCount = $('form').length;
 | 
						|
            countFormSubmits();
 | 
						|
        });
 | 
						|
 | 
						|
        afterEach(function () {
 | 
						|
            windowLocationAssignStub.restore();
 | 
						|
            pjaxClickStub.restore();
 | 
						|
            pjaxSubmitStub.restore();
 | 
						|
            formSubmitsCount = undefined;
 | 
						|
            initialFormsCount = undefined;
 | 
						|
            $savedSubmittedForm = undefined;
 | 
						|
            $(document).off('submit');
 | 
						|
            $('form').off('submit');
 | 
						|
        });
 | 
						|
 | 
						|
        function countFormSubmits() {
 | 
						|
            formSubmitsCount = 0;
 | 
						|
            $(document).on('submit', 'form', function () {
 | 
						|
                formSubmitsCount++;
 | 
						|
                $savedSubmittedForm = $(this).clone();
 | 
						|
 | 
						|
                return false;
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        function verifyNoActions() {
 | 
						|
            assert.isFalse(windowLocationAssignStub.called);
 | 
						|
            assert.isFalse(pjaxClickStub.called);
 | 
						|
 | 
						|
            assert.equal(formSubmitsCount, 0);
 | 
						|
            assert.isFalse(pjaxSubmitStub.called);
 | 
						|
            assert.equal($('form').length, initialFormsCount);
 | 
						|
        }
 | 
						|
 | 
						|
        function verifyPageLoad(url) {
 | 
						|
            assert.isTrue(windowLocationAssignStub.calledOnce);
 | 
						|
            assert.deepEqual(windowLocationAssignStub.getCall(0).args, [url]);
 | 
						|
            assert.isFalse(pjaxClickStub.called);
 | 
						|
 | 
						|
            assert.equal(formSubmitsCount, 0);
 | 
						|
            assert.isFalse(pjaxSubmitStub.called);
 | 
						|
            assert.equal($('form').length, initialFormsCount);
 | 
						|
        }
 | 
						|
 | 
						|
        function verifyPageLoadWithPjax($element, event, pjaxContainerId) {
 | 
						|
            assert.isFalse(windowLocationAssignStub.called);
 | 
						|
            assert.isTrue(pjaxClickStub.calledOnce);
 | 
						|
 | 
						|
            assert.equal(formSubmitsCount, 0);
 | 
						|
            assert.isFalse(pjaxSubmitStub.called);
 | 
						|
            assert.equal($('form').length, initialFormsCount);
 | 
						|
 | 
						|
            assert.strictEqual(pjaxClickStub.getCall(0).args[0], event);
 | 
						|
 | 
						|
            var pjaxOptions = pjaxClickStub.getCall(0).args[1];
 | 
						|
 | 
						|
            // container needs to be checked separately
 | 
						|
            assert.equal(pjaxOptions.container, pjaxContainerId || 'body');
 | 
						|
            delete pjaxOptions.container;
 | 
						|
 | 
						|
            assert.deepEqual(pjaxOptions, {
 | 
						|
                push: true,
 | 
						|
                replace: true,
 | 
						|
                scrollTo: 'scrollTo',
 | 
						|
                pushRedirect: 'pushRedirect',
 | 
						|
                replaceRedirect: 'replaceRedirect',
 | 
						|
                skipOuterContainers: 'skipOuterContainers',
 | 
						|
                timeout: 'timeout',
 | 
						|
                originalEvent: event,
 | 
						|
                originalTarget: $element
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        function verifyFormSubmit($form) {
 | 
						|
            assert.isFalse(windowLocationAssignStub.called);
 | 
						|
            assert.isFalse(pjaxClickStub.called);
 | 
						|
 | 
						|
            assert.equal(formSubmitsCount, 1);
 | 
						|
            assert.isFalse(pjaxSubmitStub.called);
 | 
						|
            assert.equal($('form').length, initialFormsCount);
 | 
						|
 | 
						|
            if ($form) {
 | 
						|
                assert.equal($form.attr('id'), $savedSubmittedForm.attr('id'));
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        function verifyFormSubmitWithPjax($element, event, $form) {
 | 
						|
            assert.isFalse(windowLocationAssignStub.called);
 | 
						|
            assert.isFalse(pjaxClickStub.called);
 | 
						|
 | 
						|
            assert.equal(formSubmitsCount, 1);
 | 
						|
            assert.isTrue(pjaxSubmitStub.calledOnce);
 | 
						|
            assert.equal($('form').length, initialFormsCount);
 | 
						|
 | 
						|
            if ($form) {
 | 
						|
                assert.equal($form.attr('id'), $savedSubmittedForm.attr('id'));
 | 
						|
            }
 | 
						|
 | 
						|
            var pjaxEvent = pjaxSubmitStub.getCall(0).args[0];
 | 
						|
            assert.instanceOf(pjaxEvent, $.Event);
 | 
						|
            assert.equal(pjaxEvent.type, 'submit');
 | 
						|
 | 
						|
            var pjaxOptions = pjaxSubmitStub.getCall(0).args[1];
 | 
						|
 | 
						|
            // container needs to be checked separately
 | 
						|
            if (typeof pjaxOptions.container === 'string') {
 | 
						|
                assert.equal(pjaxOptions.container, 'body');
 | 
						|
            } else {
 | 
						|
                assert.instanceOf(pjaxOptions.container, $);
 | 
						|
                assert.equal(pjaxOptions.container.attr('id'), 'body');
 | 
						|
            }
 | 
						|
 | 
						|
            delete pjaxOptions.container;
 | 
						|
 | 
						|
            assert.deepEqual(pjaxOptions, {
 | 
						|
                push: true,
 | 
						|
                replace: true,
 | 
						|
                scrollTo: 'scrollTo',
 | 
						|
                pushRedirect: 'pushRedirect',
 | 
						|
                replaceRedirect: 'replaceRedirect',
 | 
						|
                skipOuterContainers: 'skipOuterContainers',
 | 
						|
                timeout: 'timeout',
 | 
						|
                originalEvent: event,
 | 
						|
                originalTarget: $element
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        describe('with no data-method', function () {
 | 
						|
            var noActionsMessage = 'should not do any actions related with page load and form submit';
 | 
						|
            var pageLoadMessage = 'should load new page using the link from "href" attribute';
 | 
						|
            var pageLoadWithPjaxMessage = pageLoadMessage + ' with pjax';
 | 
						|
 | 
						|
            describe('with invalid elements or configuration', function () {
 | 
						|
                describe('with no form', function () {
 | 
						|
                    withData({
 | 
						|
                        // Links
 | 
						|
                        'link, no href': ['.link-no-href'],
 | 
						|
                        'link, empty href': ['.link-empty-href'],
 | 
						|
                        'link, href contains anchor ("#") only': ['.link-anchor-href'],
 | 
						|
                        'link, no href, data-pjax': ['.link-no-href-pjax'],
 | 
						|
                        'link, empty href, data-pjax': ['.link-empty-href-pjax'],
 | 
						|
                        'link, href contains anchor ("#") only, data-pjax': ['.link-anchor-href-pjax'],
 | 
						|
                        // Not links
 | 
						|
                        'not submit, no form': ['.not-submit-no-form'],
 | 
						|
                        'submit, no form': ['.submit-no-form'],
 | 
						|
                        'submit, data-form, form does not exist': ['.submit-form-not-exist'],
 | 
						|
                        'not submit, no form, data-pjax': ['.not-submit-no-form-pjax'],
 | 
						|
                        'submit, no form, data-pjax': ['.submit-no-form-pjax'],
 | 
						|
                        'submit, data-form, form does not exist, data-pjax': ['.submit-form-not-exist-pjax']
 | 
						|
                    }, function (elementSelector) {
 | 
						|
                        it(noActionsMessage, function () {
 | 
						|
                            var $element = $('.handle-action .no-method .invalid .no-form').find(elementSelector);
 | 
						|
                            assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                            yii.handleAction($element);
 | 
						|
                            verifyNoActions();
 | 
						|
                        });
 | 
						|
                    });
 | 
						|
                });
 | 
						|
 | 
						|
                describe('with form', function () {
 | 
						|
                    withData({
 | 
						|
                        'not submit, data-form': ['.not-submit-outside-form', '#not-submit-separate-form'],
 | 
						|
                        'not submit, inside a form': ['.not-submit-inside-form', '#not-submit-parent-form'],
 | 
						|
                        'not submit, data-form, data-pjax': [
 | 
						|
                            '.not-submit-outside-form-pjax', '#not-submit-separate-form'
 | 
						|
                        ],
 | 
						|
                        'not submit, inside a form, data-pjax': [
 | 
						|
                            '.not-submit-inside-form-pjax', '#not-submit-parent-form-pjax'
 | 
						|
                        ]
 | 
						|
                    }, function (elementSelector, formSelector) {
 | 
						|
                        it(noActionsMessage, function () {
 | 
						|
                            var $element = $('.handle-action .no-method .invalid .form').find(elementSelector);
 | 
						|
                            assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                            var $form = $(formSelector);
 | 
						|
                            assert.lengthOf($form, 1);
 | 
						|
 | 
						|
                            yii.handleAction($element);
 | 
						|
                            verifyNoActions();
 | 
						|
                        });
 | 
						|
                    });
 | 
						|
                });
 | 
						|
            });
 | 
						|
 | 
						|
            describe('with valid elements and configuration', function () {
 | 
						|
                describe('with no form', function () {
 | 
						|
                    withData({
 | 
						|
                        'link': ['.link'],
 | 
						|
                        'link, data-pjax="0"': ['.link-pjax-0']
 | 
						|
                    }, function (elementSelector) {
 | 
						|
                        it(pageLoadMessage, function () {
 | 
						|
                            var $element = $('.handle-action .no-method .valid').find(elementSelector);
 | 
						|
                            assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                            yii.handleAction($element);
 | 
						|
                            verifyPageLoad('/tests/index');
 | 
						|
                        });
 | 
						|
                    });
 | 
						|
 | 
						|
                    describe('with link, data-pjax and no pjax support', function () {
 | 
						|
                        before(function () {
 | 
						|
                            $.support.pjax = false;
 | 
						|
                        });
 | 
						|
 | 
						|
                        after(function () {
 | 
						|
                            $.support.pjax = true;
 | 
						|
                        });
 | 
						|
 | 
						|
                        it(pageLoadMessage, function () {
 | 
						|
                            var $element = $('.handle-action .no-method .valid .link-pjax');
 | 
						|
                            assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                            yii.handleAction($element);
 | 
						|
                            verifyPageLoad('/tests/index');
 | 
						|
                        });
 | 
						|
                    });
 | 
						|
 | 
						|
                    withData({
 | 
						|
                        'link, data-pjax': ['.link-pjax', 'body'],
 | 
						|
                        'link, data-pjax="1"': ['.link-pjax-1', 'body'],
 | 
						|
                        'link, data-pjax="true"': ['.link-pjax-true', 'body'],
 | 
						|
                        'link, data-pjax, outside a container': [
 | 
						|
                            '.link-pjax-outside-container', '#pjax-separate-container'
 | 
						|
                        ],
 | 
						|
                        'link href, data-pjax, inside a container': ['.link-pjax-inside-container', '#pjax-container-2']
 | 
						|
                    }, function (elementSelector, expectedPjaxContainerId) {
 | 
						|
                        it(pageLoadWithPjaxMessage, function () {
 | 
						|
                            var event = $.Event('click');
 | 
						|
                            var $element = $('.handle-action .no-method .valid').find(elementSelector);
 | 
						|
                            assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                            yii.handleAction($element, event);
 | 
						|
                            verifyPageLoadWithPjax($element, event, expectedPjaxContainerId);
 | 
						|
                        });
 | 
						|
                    });
 | 
						|
                });
 | 
						|
 | 
						|
                describe('with form', function () {
 | 
						|
                    withData({
 | 
						|
                        'submit, data-form': ['.submit-outside-form', '#submit-separate-form'],
 | 
						|
                        'submit, inside a form': ['.submit-inside-form', '#submit-parent-form']
 | 
						|
                    }, function (elementSelector, formSelector) {
 | 
						|
                        it('should submit according existing form', function () {
 | 
						|
                            var $element = $('.handle-action .no-method .valid').find(elementSelector);
 | 
						|
                            assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                            var $form = $(formSelector);
 | 
						|
                            var initialFormHtml = $form.get(0).outerHTML;
 | 
						|
                            assert.lengthOf($form, 1);
 | 
						|
 | 
						|
                            yii.handleAction($element);
 | 
						|
 | 
						|
                            verifyFormSubmit($form);
 | 
						|
                            assert.equal($savedSubmittedForm.get(0).outerHTML, initialFormHtml);
 | 
						|
                        });
 | 
						|
                    });
 | 
						|
 | 
						|
                    withData({
 | 
						|
                        'submit, data-form, data-pjax': ['.submit-outside-form-pjax', '#submit-separate-form'],
 | 
						|
                        'submit, inside a form, data-pjax': ['.submit-inside-form-pjax', '#submit-parent-form-pjax']
 | 
						|
                    }, function (elementSelector, formSelector) {
 | 
						|
                        it('should submit according existing form with pjax', function () {
 | 
						|
                            var event = $.Event('click');
 | 
						|
                            var $element = $('.handle-action .no-method .valid').find(elementSelector);
 | 
						|
                            assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                            var $form = $(formSelector);
 | 
						|
                            var initialFormHtml = $form.get(0).outerHTML;
 | 
						|
                            assert.lengthOf($form, 1);
 | 
						|
 | 
						|
                            yii.handleAction($element, event);
 | 
						|
 | 
						|
                            verifyFormSubmitWithPjax($element, event, $form);
 | 
						|
                            assert.equal($savedSubmittedForm.get(0).outerHTML, initialFormHtml);
 | 
						|
                        });
 | 
						|
                    });
 | 
						|
                });
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        describe('with data-method', function () {
 | 
						|
            describe('with no form', function () {
 | 
						|
                withData({
 | 
						|
                    'invalid href': [
 | 
						|
                        '.bad-href',
 | 
						|
                        '<form method="get" action="http://foo.bar/" style="display: none;"></form>'
 | 
						|
                    ],
 | 
						|
                    'invalid data-params': [
 | 
						|
                        '.bad-params',
 | 
						|
                        '<form method="get" action="/tests/index" style="display: none;"></form>'
 | 
						|
                    ],
 | 
						|
                    'data-method="get", data-params, target': [
 | 
						|
                        '.get-params-target',
 | 
						|
                        '<form method="get" action="/tests/index" target="_blank" style="display: none;">' +
 | 
						|
                        '<input name="foo" value="1" type="hidden">' +
 | 
						|
                        '<input name="bar" value="2" type="hidden">' +
 | 
						|
                        '</form>'
 | 
						|
                    ],
 | 
						|
                    'data-method="head", data-params': [
 | 
						|
                        '.head',
 | 
						|
                        '<form method="post" action="/tests/index" style="display: none;">' +
 | 
						|
                        '<input name="_method" value="head" type="hidden">' +
 | 
						|
                        '<input name="_csrf" value="foobar" type="hidden">' +
 | 
						|
                        '<input name="foo" value="1" type="hidden">' +
 | 
						|
                        '<input name="bar" value="2" type="hidden">' +
 | 
						|
                        '</form>'
 | 
						|
                    ],
 | 
						|
                    'data-method="post", data-params': [
 | 
						|
                        '.post',
 | 
						|
                        '<form method="post" action="/tests/index" style="display: none;">' +
 | 
						|
                        '<input name="_csrf" value="foobar" type="hidden">' +
 | 
						|
                        '<input name="foo" value="1" type="hidden">' +
 | 
						|
                        '<input name="bar" value="2" type="hidden">' +
 | 
						|
                        '</form>'
 | 
						|
                    ],
 | 
						|
                    'data-method="post", data-params, upper case': [
 | 
						|
                        '.post-upper-case',
 | 
						|
                        '<form method="POST" action="/tests/index" style="display: none;">' +
 | 
						|
                        '<input name="_csrf" value="foobar" type="hidden">' +
 | 
						|
                        '<input name="foo" value="1" type="hidden">' +
 | 
						|
                        '<input name="bar" value="2" type="hidden">' +
 | 
						|
                        '</form>'
 | 
						|
                    ],
 | 
						|
                    'data-method="put", data-params': [
 | 
						|
                        '.put',
 | 
						|
                        '<form method="post" action="/tests/index" style="display: none;">' +
 | 
						|
                        '<input name="_method" value="put" type="hidden">' +
 | 
						|
                        '<input name="_csrf" value="foobar" type="hidden">' +
 | 
						|
                        '<input name="foo" value="1" type="hidden">' +
 | 
						|
                        '<input name="bar" value="2" type="hidden">' +
 | 
						|
                        '</form>'
 | 
						|
                    ]
 | 
						|
                }, function (elementSelector, expectedFormHtml) {
 | 
						|
                    it('should create temporary form and submit it', function () {
 | 
						|
                        var $element = $('.handle-action .method .no-form').find(elementSelector);
 | 
						|
                        assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                        yii.handleAction($element);
 | 
						|
 | 
						|
                        verifyFormSubmit();
 | 
						|
                        assert.equal($savedSubmittedForm.get(0).outerHTML, expectedFormHtml);
 | 
						|
                    });
 | 
						|
                });
 | 
						|
 | 
						|
                describe('with data-method="get", data-params, data-pjax', function () {
 | 
						|
                    it('should create temporary form and submit it with pjax', function () {
 | 
						|
                        var event = $.Event('click');
 | 
						|
                        var $element = $('.handle-action .method .no-form .get-params-pjax');
 | 
						|
                        assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                        yii.handleAction($element, event);
 | 
						|
 | 
						|
                        verifyFormSubmitWithPjax($element, event);
 | 
						|
 | 
						|
                        var expectedFormHtml = '<form method="get" action="/tests/index" style="display: none;">' +
 | 
						|
                            '<input name="foo" value="1" type="hidden">' +
 | 
						|
                            '<input name="bar" value="2" type="hidden">' +
 | 
						|
                            '</form>';
 | 
						|
                        assert.equal($savedSubmittedForm.get(0).outerHTML, expectedFormHtml);
 | 
						|
                    });
 | 
						|
                });
 | 
						|
            });
 | 
						|
 | 
						|
            describe('with form', function () {
 | 
						|
                withData({
 | 
						|
                    'data-form, new action, new method, data-params': [
 | 
						|
                        '.new-action-new-method',
 | 
						|
                        '#method-form',
 | 
						|
                        '<form id="method-form" method="post" action="/search">' +
 | 
						|
                        '<input name="query" value="a">' +
 | 
						|
                        '<input name="foo" value="1" type="hidden">' +
 | 
						|
                        '<input name="bar" value="2" type="hidden">' +
 | 
						|
                        '</form>'
 | 
						|
                    ],
 | 
						|
                    'data-form, same action, same method, data-params': [
 | 
						|
                        '.same-action-same-method',
 | 
						|
                        '#method-form',
 | 
						|
                        '<form id="method-form" method="get" action="/tests/search">' +
 | 
						|
                        '<input name="query" value="a">' +
 | 
						|
                        '<input name="foo" value="1" type="hidden">' +
 | 
						|
                        '<input name="bar" value="2" type="hidden">' +
 | 
						|
                        '</form>'
 | 
						|
                    ],
 | 
						|
                    'data-form, invalid action, new method, data-params': [
 | 
						|
                        '.bad-action-new-method',
 | 
						|
                        '#method-form',
 | 
						|
                        '<form id="method-form" method="post" action="/tests/search">' +
 | 
						|
                        '<input name="query" value="a">' +
 | 
						|
                        '<input name="foo" value="1" type="hidden">' +
 | 
						|
                        '<input name="bar" value="2" type="hidden">' +
 | 
						|
                        '</form>'
 | 
						|
                    ],
 | 
						|
                    // This is a test for this PR:
 | 
						|
                    // https://github.com/yiisoft/yii2/pull/8014
 | 
						|
                    //
 | 
						|
                    // However the bug currently can not be reproduced in jsdom:
 | 
						|
                    // https://github.com/tmpvar/jsdom/issues/1688
 | 
						|
                    'data-form, same action, same method, hidden "method" and "action" inputs in data-params': [
 | 
						|
                        '.hidden-method-action',
 | 
						|
                        '#form-hidden-method-action',
 | 
						|
                        '<form id="form-hidden-method-action" method="get" action="/tests/search">' +
 | 
						|
                        '<input name="query" value="a">' +
 | 
						|
                        '<input name="method" value="b" type="hidden">' +
 | 
						|
                        '<input name="action" value="c" type="hidden">' +
 | 
						|
                        '<input name="foo" value="1" type="hidden">' +
 | 
						|
                        '<input name="bar" value="2" type="hidden">' +
 | 
						|
                        '</form>'
 | 
						|
                    ]
 | 
						|
                }, function (elementSelector, formSelector, expectedSubmittedFormHtml) {
 | 
						|
                    var message = 'should modify according existing form, submit it and restore to initial condition';
 | 
						|
                    it(message, function () {
 | 
						|
                        var $element = $('.handle-action .method .form').find(elementSelector);
 | 
						|
                        assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                        var $form = $(formSelector);
 | 
						|
                        var initialFormHtml = $form.get(0).outerHTML;
 | 
						|
                        assert.lengthOf($form, 1);
 | 
						|
 | 
						|
                        $form.data('yiiActiveForm', {});
 | 
						|
 | 
						|
                        yii.handleAction($element);
 | 
						|
 | 
						|
                        verifyFormSubmit($form);
 | 
						|
 | 
						|
                        var submittedFormHtml = StringUtils.cleanHTML($savedSubmittedForm.get(0).outerHTML);
 | 
						|
                        assert.equal(submittedFormHtml, expectedSubmittedFormHtml);
 | 
						|
                        assert.equal($form.get(0).outerHTML, initialFormHtml);
 | 
						|
 | 
						|
                        // When activeForm is used for this form, the element triggered the submit should be remembered
 | 
						|
                        // in jQuery data under according key
 | 
						|
                        assert.strictEqual($form.data('yiiActiveForm').submitObject, $element);
 | 
						|
                    });
 | 
						|
                });
 | 
						|
 | 
						|
                describe('with data-form, new action, new method, data-params, data-pjax', function () {
 | 
						|
                    var message = 'should modify according existing form, submit it with pjax and restore to ' +
 | 
						|
                        ' initial condition';
 | 
						|
                    it(message, function () {
 | 
						|
                        var event = $.Event('click');
 | 
						|
                        var $element = $('.handle-action .method .form .new-action-new-method-pjax');
 | 
						|
                        assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                        var $form = $('#method-form');
 | 
						|
                        var initialFormHtml = $form.get(0).outerHTML;
 | 
						|
                        assert.lengthOf($form, 1);
 | 
						|
 | 
						|
                        yii.handleAction($element, event);
 | 
						|
 | 
						|
                        verifyFormSubmitWithPjax($element, event, $form);
 | 
						|
 | 
						|
                        var expectedSubmittedFormHtml = '<form id="method-form" method="post" action="/search">' +
 | 
						|
                            '<input name="query" value="a">' +
 | 
						|
                            '<input name="foo" value="1" type="hidden">' +
 | 
						|
                            '<input name="bar" value="2" type="hidden">' +
 | 
						|
                            '</form>';
 | 
						|
                        var submittedFormHtml = StringUtils.cleanHTML($savedSubmittedForm.get(0).outerHTML);
 | 
						|
                        assert.equal(submittedFormHtml, expectedSubmittedFormHtml);
 | 
						|
                        assert.equal($form.get(0).outerHTML, initialFormHtml);
 | 
						|
                    });
 | 
						|
                });
 | 
						|
            });
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('getQueryParams method', function () {
 | 
						|
        withData({
 | 
						|
            'no query parameters': ['/posts/index', {}],
 | 
						|
            // https://github.com/yiisoft/yii2/issues/13738
 | 
						|
            'question mark, no query parameters': ['/posts/index?', {}],
 | 
						|
            'query parameters': ['/posts/index?foo=1&bar=2', {foo: '1', bar: '2'}],
 | 
						|
            'query parameter with multiple values (not array)': ['/posts/index?foo=1&foo=2', {'foo': ['1', '2']}],
 | 
						|
            'query parameter with multiple values (array)': ['/posts/index?foo[]=1&foo[]=2', {'foo[]': ['1', '2']}],
 | 
						|
            'query parameter with empty value': ['/posts/index?foo=1&foo2', {'foo': '1', 'foo2': ''}],
 | 
						|
            'anchor': ['/posts/index#post', {}],
 | 
						|
            'query parameters, anchor': ['/posts/index?foo=1&bar=2#post', {foo: '1', bar: '2'}],
 | 
						|
            'relative url, query parameters': ['?foo=1&bar=2', {foo: '1', bar: '2'}],
 | 
						|
            'relative url, anchor': ['#post', {}],
 | 
						|
            'relative url, query parameters, anchor': ['?foo=1&bar=2#post', {foo: '1', bar: '2'}],
 | 
						|
            'skipped parameter name': ['?foo=1&=2&baz=3#post', {foo: '1', baz: '3'}],
 | 
						|
            'skipped values': [
 | 
						|
                '?foo=&PostSearch[tags][]=1&PostSearch[tags][]=', {foo: '', 'PostSearch[tags][]': ['1', '']}
 | 
						|
            ],
 | 
						|
            'encoded URI component': ['/posts/index?query=' + encodeURIComponent('count >= 1'), {query: 'count >= 1'}],
 | 
						|
            // https://github.com/yiisoft/yii2/issues/11921
 | 
						|
            'encoded URI component, "+" signs': [
 | 
						|
                '/posts/index?next+celebration+day=Sunday+January+1st&' +
 | 
						|
                'increase+' + encodeURIComponent('++') + '+' + encodeURIComponent('%') +
 | 
						|
                '='
 | 
						|
                + encodeURIComponent('++') + '+20+' + encodeURIComponent('%'),
 | 
						|
                {'next celebration day': 'Sunday January 1st', 'increase ++ %': '++ 20 %'}
 | 
						|
            ],
 | 
						|
            'multiple arrays, anchor': [
 | 
						|
                '/posts/index?CategorySearch[id]=1&CategorySearch[name]=a' +
 | 
						|
                '&PostSearch[name]=b&PostSearch[category_id]=2&PostSearch[tags][]=3&PostSearch[tags][]=4' +
 | 
						|
                '&foo[]=5&foo[]=6&bar=7#post',
 | 
						|
                {
 | 
						|
                    'CategorySearch[id]': '1',
 | 
						|
                    'CategorySearch[name]': 'a',
 | 
						|
                    'PostSearch[name]': 'b',
 | 
						|
                    'PostSearch[category_id]': '2',
 | 
						|
                    'PostSearch[tags][]': ['3', '4'],
 | 
						|
                    'foo[]': ['5', '6'],
 | 
						|
                    bar: '7'
 | 
						|
                }
 | 
						|
            ]
 | 
						|
        }, function (url, expectedParams) {
 | 
						|
            it('should parse all query parameters from string and return them within a object', function () {
 | 
						|
                assert.deepEqual(yii.getQueryParams(url), expectedParams);
 | 
						|
            });
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('initModule method', function () {
 | 
						|
        var calledInitMethods = [];
 | 
						|
        var rootModuleInit = function () {
 | 
						|
            calledInitMethods.push('rootModule');
 | 
						|
        };
 | 
						|
 | 
						|
        afterEach(function () {
 | 
						|
            calledInitMethods = [];
 | 
						|
        });
 | 
						|
 | 
						|
        withData({
 | 
						|
            'isActive is undefined in the root module': [
 | 
						|
                undefined,
 | 
						|
                rootModuleInit,
 | 
						|
                ['rootModule', 'isActiveUndefined', 'isActiveTrue', 'subModule', 'subModule2']
 | 
						|
            ],
 | 
						|
            'isActive is true in the root module': [
 | 
						|
                true,
 | 
						|
                rootModuleInit,
 | 
						|
                ['rootModule', 'isActiveUndefined', 'isActiveTrue', 'subModule', 'subModule2']
 | 
						|
            ],
 | 
						|
            'isActive is false in the root module': [false, rootModuleInit, []],
 | 
						|
            'isActive is undefined in the root module, init is not a method': [
 | 
						|
                undefined,
 | 
						|
                'init',
 | 
						|
                ['isActiveUndefined', 'isActiveTrue', 'subModule', 'subModule2']
 | 
						|
            ]
 | 
						|
        }, function (rootModuleIsActive, rootModuleInit, expectedCalledInitMethods) {
 | 
						|
            var message = 'should call init method in the root module and all submodules depending depending on ' +
 | 
						|
                'activity and if init is a valid method';
 | 
						|
            it(message, function () {
 | 
						|
                // Root module
 | 
						|
 | 
						|
                var module = (function () {
 | 
						|
                    return {
 | 
						|
                        isActive: rootModuleIsActive,
 | 
						|
                        init: rootModuleInit
 | 
						|
                    };
 | 
						|
                })();
 | 
						|
 | 
						|
                // Submodules
 | 
						|
 | 
						|
                module.isActiveUndefined = (function () {
 | 
						|
                    return {
 | 
						|
                        init: function () {
 | 
						|
                            calledInitMethods.push('isActiveUndefined');
 | 
						|
                        }
 | 
						|
                    };
 | 
						|
                })();
 | 
						|
 | 
						|
                module.isActiveTrue = (function () {
 | 
						|
                    return {
 | 
						|
                        isActive: true,
 | 
						|
                        init: function () {
 | 
						|
                            calledInitMethods.push('isActiveTrue');
 | 
						|
                        }
 | 
						|
                    };
 | 
						|
                })();
 | 
						|
 | 
						|
                module.isActiveFalse = (function () {
 | 
						|
                    return {
 | 
						|
                        isActive: false,
 | 
						|
                        init: function () {
 | 
						|
                            calledInitMethods.push('isActiveFalse');
 | 
						|
                        }
 | 
						|
                    };
 | 
						|
                })();
 | 
						|
 | 
						|
                module.initNotFunction = (function () {
 | 
						|
                    return {
 | 
						|
                        init: 'init'
 | 
						|
                    };
 | 
						|
                })();
 | 
						|
 | 
						|
                module.someInteger = 1;
 | 
						|
                module.someString = 'string';
 | 
						|
 | 
						|
                module.subModule = (function () {
 | 
						|
                    return {
 | 
						|
                        init: function () {
 | 
						|
                            calledInitMethods.push('subModule');
 | 
						|
                        }
 | 
						|
                    };
 | 
						|
                })();
 | 
						|
 | 
						|
                module.subModule.subModule2 = (function () {
 | 
						|
                    return {
 | 
						|
                        init: function () {
 | 
						|
                            calledInitMethods.push('subModule2');
 | 
						|
                        }
 | 
						|
                    };
 | 
						|
                })();
 | 
						|
 | 
						|
                yii.initModule(module);
 | 
						|
                assert.deepEqual(calledInitMethods, expectedCalledInitMethods);
 | 
						|
            });
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('CSRF handler', function () {
 | 
						|
        var server;
 | 
						|
        var yiiGetCsrfParamStub;
 | 
						|
        var fakeCsrfParam;
 | 
						|
 | 
						|
        beforeEach(function () {
 | 
						|
            server = sinon.fakeServer.create();
 | 
						|
            window.XMLHttpRequest = global.XMLHttpRequest;
 | 
						|
            yiiGetCsrfParamStub = sinon.stub(yii, 'getCsrfParam', function () {
 | 
						|
                return fakeCsrfParam;
 | 
						|
            })
 | 
						|
        });
 | 
						|
 | 
						|
        afterEach(function () {
 | 
						|
            server.restore();
 | 
						|
            yiiGetCsrfParamStub.restore();
 | 
						|
        });
 | 
						|
 | 
						|
        withData({
 | 
						|
            'crossDomain is false, csrfParam is not set': [false, undefined, undefined],
 | 
						|
            'crossDomain is false, csrfParam is set': [false, 'foobar', 'foobar'],
 | 
						|
            'crossDomain is true, csrfParam is not set': [true, undefined, undefined],
 | 
						|
            'crossDomain is true, csrfParam is set': [true, 'foobar', undefined]
 | 
						|
        }, function (crossDomain, csrfParam, expectedHeaderValue) {
 | 
						|
            var message = 'should add header with CSRF token to AJAX requests only when crossDomain is false and ' +
 | 
						|
                'csrf parameter is set';
 | 
						|
            it(message, function () {
 | 
						|
                fakeCsrfParam = csrfParam;
 | 
						|
                $.ajax({
 | 
						|
                    url: '/tests/index',
 | 
						|
                    crossDomain: crossDomain
 | 
						|
                });
 | 
						|
                server.requests[0].respond(200, {}, '');
 | 
						|
 | 
						|
                assert.lengthOf(server.requests, 1);
 | 
						|
                assert.equal(server.requests[0].requestHeaders['X-CSRF-Token'], expectedHeaderValue);
 | 
						|
            });
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('redirect handler', function () {
 | 
						|
        var windowLocationAssignStub;
 | 
						|
 | 
						|
        beforeEach(function () {
 | 
						|
            windowLocationAssignStub = sinon.stub(window.location, 'assign');
 | 
						|
        });
 | 
						|
 | 
						|
        afterEach(function () {
 | 
						|
            windowLocationAssignStub.restore();
 | 
						|
        });
 | 
						|
 | 
						|
        // https://github.com/yiisoft/yii2/pull/10974
 | 
						|
        describe('with xhr undefined', function () {
 | 
						|
            it('should not perform redirect', function () {
 | 
						|
                var e = $.Event('ajaxComplete');
 | 
						|
                $('body').trigger(e);
 | 
						|
 | 
						|
                assert.isFalse(windowLocationAssignStub.called);
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        describe('with xhr defined', function () {
 | 
						|
            var server;
 | 
						|
 | 
						|
            beforeEach(function () {
 | 
						|
                server = sinon.fakeServer.create();
 | 
						|
                window.XMLHttpRequest = global.XMLHttpRequest;
 | 
						|
            });
 | 
						|
 | 
						|
            afterEach(function () {
 | 
						|
                server.restore();
 | 
						|
            });
 | 
						|
 | 
						|
            describe('with custom header not set', function () {
 | 
						|
                it('should not perform redirect', function () {
 | 
						|
                    $.get('/tests/index');
 | 
						|
                    server.requests[0].respond(200, {}, '');
 | 
						|
 | 
						|
                    assert.lengthOf(server.requests, 1);
 | 
						|
                    assert.isFalse(windowLocationAssignStub.called);
 | 
						|
                });
 | 
						|
            });
 | 
						|
 | 
						|
            describe('with custom header set', function () {
 | 
						|
                it('should perform redirect', function () {
 | 
						|
                    $.get('/tests/index');
 | 
						|
                    server.requests[0].respond(200, {'X-Redirect': 'http://redirect.yii'}, '');
 | 
						|
 | 
						|
                    assert.lengthOf(server.requests, 1);
 | 
						|
                    assert.isTrue(windowLocationAssignStub.calledOnce);
 | 
						|
                    assert.deepEqual(windowLocationAssignStub.getCall(0).args, ['http://redirect.yii']);
 | 
						|
                });
 | 
						|
            });
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('asset filters', function () {
 | 
						|
        var server;
 | 
						|
        var prefilterCallback = function (options) {
 | 
						|
            options.crossDomain = false;
 | 
						|
        };
 | 
						|
        var jsResponse = {
 | 
						|
            status: 200,
 | 
						|
            headers: {'Content-Type': 'application/javascript'},
 | 
						|
            body: 'var foobar = 1;'
 | 
						|
        };
 | 
						|
 | 
						|
        before(function () {
 | 
						|
            $.ajaxPrefilter('script', function (options) {
 | 
						|
                prefilterCallback(options);
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        beforeEach(function () {
 | 
						|
            server = sinon.fakeServer.create();
 | 
						|
            // Allowed: /js/test.js, http://foo.bar/js/test.js
 | 
						|
            server.respondWith(/(http:\/\/foo\.bar)?\/js\/.+\.js/, [
 | 
						|
                jsResponse.status,
 | 
						|
                jsResponse.headers,
 | 
						|
                jsResponse.body
 | 
						|
            ]);
 | 
						|
            window.XMLHttpRequest = global.XMLHttpRequest;
 | 
						|
        });
 | 
						|
 | 
						|
        after(function () {
 | 
						|
            prefilterCallback = function () {
 | 
						|
            };
 | 
						|
        });
 | 
						|
 | 
						|
        afterEach(function () {
 | 
						|
            server.restore();
 | 
						|
        });
 | 
						|
 | 
						|
        function respondToRequestWithSuccess(requestIndex) {
 | 
						|
            server.requests[requestIndex].respond(jsResponse.status, jsResponse.headers, jsResponse.body);
 | 
						|
        }
 | 
						|
 | 
						|
        function respondToRequestWithError(requestIndex) {
 | 
						|
            server.requests[requestIndex].respond(404, {}, '');
 | 
						|
        }
 | 
						|
 | 
						|
        // Note: Please do not test loading of the script with the same name in different tests. After successful
 | 
						|
        // loading it will stay in loadedScripts and the load will be aborted unless this script is reloadable.
 | 
						|
 | 
						|
        describe('with scripts', function () {
 | 
						|
            var XHR_UNSENT;
 | 
						|
            var XHR_OPENED;
 | 
						|
            var XHR_DONE;
 | 
						|
 | 
						|
            before(function () {
 | 
						|
                XHR_UNSENT = window.XMLHttpRequest.UNSENT;
 | 
						|
                XHR_OPENED = window.XMLHttpRequest.OPENED;
 | 
						|
                XHR_DONE = window.XMLHttpRequest.DONE;
 | 
						|
            });
 | 
						|
 | 
						|
            describe('with jsonp dataType', function () {
 | 
						|
                it('should load it as many times as it was requested', function () {
 | 
						|
                    $.ajax({
 | 
						|
                        url: '/js/jsonp.js',
 | 
						|
                        dataType: 'jsonp'
 | 
						|
                    });
 | 
						|
                    server.respond();
 | 
						|
 | 
						|
                    $.ajax({
 | 
						|
                        url: '/js/jsonp.js',
 | 
						|
                        dataType: 'jsonp'
 | 
						|
                    });
 | 
						|
                    server.respond();
 | 
						|
 | 
						|
                    assert.lengthOf(server.requests, 2);
 | 
						|
                    assert.equal(server.requests[0].readyState, XHR_DONE);
 | 
						|
                    assert.equal(server.requests[1].readyState, XHR_DONE);
 | 
						|
                });
 | 
						|
            });
 | 
						|
 | 
						|
            describe('with scripts loaded on the page load', function () {
 | 
						|
                it('should prevent of loading them again for both relative and absolute urls', function () {
 | 
						|
                    $.getScript('/js/existing1.js');
 | 
						|
                    server.respond();
 | 
						|
 | 
						|
                    $.getScript('http://foo.bar/js/existing1.js');
 | 
						|
                    server.respond();
 | 
						|
 | 
						|
                    $.getScript('/js/existing2.js');
 | 
						|
                    server.respond();
 | 
						|
 | 
						|
                    $.getScript('http://foo.bar/js/existing2.js');
 | 
						|
                    server.respond();
 | 
						|
 | 
						|
                    assert.lengthOf(server.requests, 0);
 | 
						|
                });
 | 
						|
            });
 | 
						|
 | 
						|
            describe('with script not loaded before', function () {
 | 
						|
                it('should load it only once for both relative and absolute urls', function () {
 | 
						|
                    $.getScript('/js/new.js');
 | 
						|
                    server.respond();
 | 
						|
 | 
						|
                    $.getScript('/js/new.js');
 | 
						|
                    server.respond();
 | 
						|
 | 
						|
                    $.getScript('http://foo.bar/js/new.js');
 | 
						|
                    server.respond();
 | 
						|
 | 
						|
                    assert.lengthOf(server.requests, 1);
 | 
						|
                    assert.equal(server.requests[0].readyState, XHR_DONE);
 | 
						|
                });
 | 
						|
            });
 | 
						|
 | 
						|
            describe('with reloadableScripts set', function () {
 | 
						|
                before(function () {
 | 
						|
                    yii.reloadableScripts = [
 | 
						|
                        '/js/reloadable.js',
 | 
						|
                        // https://github.com/yiisoft/yii2/issues/11494
 | 
						|
                        '/js/reloadable/script*.js?v=*'
 | 
						|
                    ];
 | 
						|
                });
 | 
						|
 | 
						|
                after(function () {
 | 
						|
                    yii.reloadableScripts = [];
 | 
						|
                });
 | 
						|
 | 
						|
                describe('with match', function () {
 | 
						|
                    withData({
 | 
						|
                        'relative url, exact': ['/js/reloadable.js'],
 | 
						|
                        'relative url, wildcard': ['/js/reloadable/script1.js?v=1'],
 | 
						|
                        'absolute url, exact': ['http://foo.bar/js/reloadable.js'],
 | 
						|
                        'absolute url, wildcard': ['http://foo.bar/js/reloadable/script2.js?v=2']
 | 
						|
                    }, function (url) {
 | 
						|
                        it('should load it as many times as it was requested', function () {
 | 
						|
                            $.getScript(url);
 | 
						|
                            server.respond();
 | 
						|
 | 
						|
                            $.getScript(url);
 | 
						|
                            server.respond();
 | 
						|
 | 
						|
                            assert.lengthOf(server.requests, 2);
 | 
						|
                            assert.equal(server.requests[0].readyState, XHR_DONE);
 | 
						|
                            assert.equal(server.requests[1].readyState, XHR_DONE);
 | 
						|
                        });
 | 
						|
                    });
 | 
						|
                });
 | 
						|
 | 
						|
                describe('with no match', function () {
 | 
						|
                    withData({
 | 
						|
                        'relative url': ['/js/not_reloadable.js'],
 | 
						|
                        'relative url, all wildcards are empty': ['/js/reloadable/script.js?v='],
 | 
						|
                        'absolute url': ['http://foo.bar/js/reloadable/not_reloadable_script.js'],
 | 
						|
                        'absolute url, 1 empty wildcard': ['http://foo.bar/js/reloadable/script1.js?v=']
 | 
						|
                    }, function (url) {
 | 
						|
                        it('should load it only once for both relative and absolute urls', function () {
 | 
						|
                            $.getScript(url);
 | 
						|
                            server.respond();
 | 
						|
 | 
						|
                            $.getScript(url);
 | 
						|
                            server.respond();
 | 
						|
 | 
						|
                            assert.lengthOf(server.requests, 1);
 | 
						|
                            assert.equal(server.requests[0].readyState, XHR_DONE);
 | 
						|
                        });
 | 
						|
                    });
 | 
						|
                });
 | 
						|
 | 
						|
                describe('with failed load after successful load and making it not reloadable', function () {
 | 
						|
                    it('should allow to load it again', function () {
 | 
						|
                        $.getScript('/js/reloadable/script_fail.js?v=1');
 | 
						|
                        respondToRequestWithSuccess(0);
 | 
						|
 | 
						|
                        $.getScript('/js/reloadable/script_fail.js?v=1');
 | 
						|
                        respondToRequestWithError(1);
 | 
						|
                        yii.reloadableScripts = [];
 | 
						|
 | 
						|
                        $.getScript('/js/reloadable/script_fail.js?v=1');
 | 
						|
                        respondToRequestWithError(2);
 | 
						|
 | 
						|
                        assert.lengthOf(server.requests, 3);
 | 
						|
                        assert.equal(server.requests[0].readyState, XHR_DONE);
 | 
						|
                        assert.equal(server.requests[1].readyState, XHR_DONE);
 | 
						|
                        assert.equal(server.requests[2].readyState, XHR_DONE);
 | 
						|
                    });
 | 
						|
                });
 | 
						|
            });
 | 
						|
 | 
						|
            // https://github.com/yiisoft/yii2/issues/10358
 | 
						|
            // https://github.com/yiisoft/yii2/issues/13307
 | 
						|
 | 
						|
            describe('with concurrent requests', function () {
 | 
						|
                // Note: it's not possible to imitate successful loading of all requests, because after the first one
 | 
						|
                // loads, the rest will be aborted by yii.js (readyState will be 0 (UNSENT)).
 | 
						|
                // Sinon requires request to have state 1 (OPENED) for the response to be sent.
 | 
						|
                // Anyway one of the requests will be loaded at least a bit earlier than the others, so we can test
 | 
						|
                // that.
 | 
						|
                describe('with one successfully completed after one failed', function () {
 | 
						|
                    it('should abort remaining requests and disallow to load the script again', function () {
 | 
						|
                        $.getScript('/js/concurrent_success.js');
 | 
						|
                        $.getScript('/js/concurrent_success.js');
 | 
						|
                        $.getScript('/js/concurrent_success.js');
 | 
						|
 | 
						|
                        assert.lengthOf(server.requests, 3);
 | 
						|
 | 
						|
                        assert.equal(server.requests[0].readyState, XHR_OPENED);
 | 
						|
                        assert.equal(server.requests[1].readyState, XHR_OPENED);
 | 
						|
                        assert.equal(server.requests[2].readyState, XHR_OPENED);
 | 
						|
 | 
						|
                        respondToRequestWithError(2);
 | 
						|
                        respondToRequestWithSuccess(1);
 | 
						|
 | 
						|
                        assert.equal(server.requests[0].readyState, XHR_UNSENT);
 | 
						|
                        assert.isTrue(server.requests[0].aborted);
 | 
						|
                        assert.equal(server.requests[1].readyState, XHR_DONE);
 | 
						|
                        assert.isUndefined(server.requests[1].aborted);
 | 
						|
                        assert.equal(server.requests[2].readyState, XHR_DONE);
 | 
						|
                        assert.isUndefined(server.requests[2].aborted);
 | 
						|
 | 
						|
                        $.getScript('/js/concurrent_success.js');
 | 
						|
                        server.respond();
 | 
						|
 | 
						|
                        assert.lengthOf(server.requests, 3);
 | 
						|
                    });
 | 
						|
                });
 | 
						|
 | 
						|
                describe('with all requests failed', function () {
 | 
						|
                    it('should allow to load the script again', function () {
 | 
						|
                        $.getScript('/js/concurrent_fail.js');
 | 
						|
                        $.getScript('/js/concurrent_fail.js');
 | 
						|
                        $.getScript('/js/concurrent_fail.js');
 | 
						|
 | 
						|
                        respondToRequestWithError(0);
 | 
						|
                        respondToRequestWithError(1);
 | 
						|
                        respondToRequestWithError(2);
 | 
						|
 | 
						|
                        $.getScript('/js/concurrent_fail.js');
 | 
						|
 | 
						|
                        respondToRequestWithSuccess(3);
 | 
						|
 | 
						|
                        assert.lengthOf(server.requests, 4);
 | 
						|
                        assert.equal(server.requests[0].readyState, XHR_DONE);
 | 
						|
                        assert.equal(server.requests[1].readyState, XHR_DONE);
 | 
						|
                        assert.equal(server.requests[2].readyState, XHR_DONE);
 | 
						|
                        assert.equal(server.requests[3].readyState, XHR_DONE);
 | 
						|
                    });
 | 
						|
                });
 | 
						|
 | 
						|
                describe('with requests to different urls successfully completed', function () {
 | 
						|
                    it('should not cause any conflicts and disallow to load these scripts again', function () {
 | 
						|
                        $.getScript('/js/concurrent_url1.js');
 | 
						|
                        $.getScript('/js/concurrent_url2.js');
 | 
						|
 | 
						|
                        $.getScript('/js/concurrent_url1.js');
 | 
						|
                        $.getScript('/js/concurrent_url2.js');
 | 
						|
 | 
						|
                        respondToRequestWithSuccess(0);
 | 
						|
                        respondToRequestWithSuccess(3);
 | 
						|
 | 
						|
                        $.getScript('/js/concurrent_url1.js');
 | 
						|
                        $.getScript('/js/concurrent_url2.js');
 | 
						|
 | 
						|
                        assert.lengthOf(server.requests, 4);
 | 
						|
                        assert.equal(server.requests[0].readyState, XHR_DONE);
 | 
						|
                        assert.isUndefined(server.requests[0].aborted);
 | 
						|
                        assert.equal(server.requests[1].readyState, XHR_UNSENT);
 | 
						|
                        assert.isTrue(server.requests[1].aborted);
 | 
						|
                        assert.equal(server.requests[2].readyState, XHR_UNSENT);
 | 
						|
                        assert.isTrue(server.requests[2].aborted);
 | 
						|
                        assert.equal(server.requests[3].readyState, XHR_DONE);
 | 
						|
                        assert.isUndefined(server.requests[3].aborted);
 | 
						|
                    });
 | 
						|
                });
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        describe('with stylesheets', function () {
 | 
						|
            // Note: All added stylesheets for the tests must have ".added-stylesheet" class for the proper cleanup
 | 
						|
 | 
						|
            afterEach(function () {
 | 
						|
                $('.added-stylesheet').remove();
 | 
						|
            });
 | 
						|
 | 
						|
            describe('with not reloadable assets', function () {
 | 
						|
                it('should not allow to add duplicate stylesheets for both relative and absolute urls', function () {
 | 
						|
                    var $styleSheets = $('.asset-filters .stylesheets');
 | 
						|
                    assert.lengthOf($styleSheets, 1);
 | 
						|
 | 
						|
                    $.get('/tests/index', function () {
 | 
						|
                        $styleSheets.append(
 | 
						|
                            '<link class="added-stylesheet" href="/css/stylesheet1.css" rel="stylesheet">'
 | 
						|
                        );
 | 
						|
                        $styleSheets.append(
 | 
						|
                            '<link class="added-stylesheet" href="/css/stylesheet2.css" rel="stylesheet">'
 | 
						|
                        );
 | 
						|
                    });
 | 
						|
 | 
						|
                    server.requests[0].respond(200, {}, '');
 | 
						|
 | 
						|
                    assert.lengthOf($('link[rel="stylesheet"]'), 2);
 | 
						|
                    assert.lengthOf($('#stylesheet1'), 1);
 | 
						|
                    assert.lengthOf($('#stylesheet2'), 1);
 | 
						|
                });
 | 
						|
            });
 | 
						|
 | 
						|
            describe('with reloadable assets', function () {
 | 
						|
                before(function () {
 | 
						|
                    yii.reloadableScripts = [
 | 
						|
                        '/css/reloadable.css',
 | 
						|
                        // https://github.com/yiisoft/yii2/issues/11494
 | 
						|
                        '/css/reloadable/stylesheet*.css'
 | 
						|
                    ];
 | 
						|
                });
 | 
						|
 | 
						|
                after(function () {
 | 
						|
                    yii.reloadableScripts = [];
 | 
						|
                });
 | 
						|
 | 
						|
                it('should allow to add duplicate stylesheets for both relative and absolute urls', function () {
 | 
						|
                    var $styleSheets = $('.asset-filters .stylesheets');
 | 
						|
                    assert.lengthOf($styleSheets, 1);
 | 
						|
 | 
						|
                    $.get('/tests/index', function () {
 | 
						|
                        $styleSheets.append(
 | 
						|
                            '<link class="added-stylesheet" href="/css/reloadable.css" rel="stylesheet">'
 | 
						|
                        );
 | 
						|
                        $styleSheets.append(
 | 
						|
                            '<link class="added-stylesheet" href="http://foo.bar/css/reloadable.css" rel="stylesheet">'
 | 
						|
                        );
 | 
						|
                        $styleSheets.append(
 | 
						|
                            '<link class="added-stylesheet" href="/css/reloadable/stylesheet1.css" rel="stylesheet">'
 | 
						|
                        );
 | 
						|
                        $styleSheets.append(
 | 
						|
                            '<link class="added-stylesheet" href="http://foo.bar/css/reloadable/stylesheet1.css" ' +
 | 
						|
                            'rel="stylesheet">'
 | 
						|
                        );
 | 
						|
                    });
 | 
						|
 | 
						|
                    server.requests[0].respond(200, {}, '');
 | 
						|
 | 
						|
                    assert.lengthOf($('link[rel=stylesheet]'), 6);
 | 
						|
                });
 | 
						|
            });
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('data methods', function () {
 | 
						|
        var windowConfirmStub;
 | 
						|
        var yiiConfirmSpy;
 | 
						|
        var yiiHandleActionStub;
 | 
						|
        var extraEventHandlerSpy;
 | 
						|
 | 
						|
        beforeEach(function () {
 | 
						|
            windowConfirmStub = sinon.stub(window, 'confirm', function () {
 | 
						|
                return true;
 | 
						|
            });
 | 
						|
            yiiConfirmSpy = sinon.spy(yii, 'confirm');
 | 
						|
            yiiHandleActionStub = sinon.stub(yii, 'handleAction');
 | 
						|
            extraEventHandlerSpy = sinon.spy();
 | 
						|
            $(window.document).on('click change', '.data-methods-element', extraEventHandlerSpy);
 | 
						|
        });
 | 
						|
 | 
						|
        afterEach(function () {
 | 
						|
            windowConfirmStub.restore();
 | 
						|
            yiiConfirmSpy.restore();
 | 
						|
            yiiHandleActionStub.restore();
 | 
						|
            extraEventHandlerSpy.reset();
 | 
						|
            $(window.document).off('click change', '.data-methods-element');
 | 
						|
        });
 | 
						|
 | 
						|
        describe('with data not set', function () {
 | 
						|
            it('should continue handling interaction with element', function () {
 | 
						|
                var event = $.Event('click');
 | 
						|
                var $element = $('#data-methods-no-data');
 | 
						|
                assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                $element.trigger(event);
 | 
						|
 | 
						|
                assert.isFalse(yiiConfirmSpy.called);
 | 
						|
                assert.isFalse(yiiHandleActionStub.called);
 | 
						|
                assert.isTrue(extraEventHandlerSpy.calledOnce);
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        describe('disabled confirm dialog', function () {
 | 
						|
            it('confirm data param = false', function () {
 | 
						|
                var element = $('#data-methods-no-data');
 | 
						|
                element.attr('data-confirm', false);
 | 
						|
                element.trigger($.Event('click'));
 | 
						|
 | 
						|
                assert.isFalse(yiiConfirmSpy.called);
 | 
						|
                assert.isTrue(yiiHandleActionStub.called);
 | 
						|
            });
 | 
						|
            it('confirm data param = empty', function () {
 | 
						|
                var element = $('#data-methods-no-data');
 | 
						|
                element.attr('data-confirm', '');
 | 
						|
                element.trigger($.Event('click'));
 | 
						|
 | 
						|
                assert.isFalse(yiiConfirmSpy.called);
 | 
						|
                assert.isTrue(yiiHandleActionStub.called);
 | 
						|
            });
 | 
						|
            it('confirm data param = undefined', function () {
 | 
						|
                var element = $('#data-methods-no-data');
 | 
						|
                element.attr('data-confirm', undefined);
 | 
						|
                element.trigger($.Event('click'));
 | 
						|
 | 
						|
                assert.isFalse(yiiConfirmSpy.called);
 | 
						|
                assert.isTrue(yiiHandleActionStub.called);
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        describe('with clickableSelector with data-confirm', function () {
 | 
						|
            it('should call confirm and handleAction methods', function () {
 | 
						|
                var event = $.Event('click');
 | 
						|
                var elementId = 'data-methods-click-confirm';
 | 
						|
                var $element = $('#' + elementId);
 | 
						|
                assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                $element.trigger(event);
 | 
						|
 | 
						|
                assert.isTrue(yiiConfirmSpy.calledOnce);
 | 
						|
                assert.equal(yiiConfirmSpy.getCall(0).args[0], 'Are you sure?');
 | 
						|
                assert.isFunction(yiiConfirmSpy.getCall(0).args[1]);
 | 
						|
                // https://github.com/yiisoft/yii2/issues/10097
 | 
						|
                assert.instanceOf(yiiConfirmSpy.getCall(0).thisValue, window.HTMLAnchorElement);
 | 
						|
                assert.equal(yiiConfirmSpy.getCall(0).thisValue.id, elementId);
 | 
						|
 | 
						|
                assert.isTrue(yiiHandleActionStub.calledOnce);
 | 
						|
                assert.equal(yiiHandleActionStub.getCall(0).args[0].attr('id'), elementId);
 | 
						|
                assert.strictEqual(yiiHandleActionStub.getCall(0).args[1], event);
 | 
						|
 | 
						|
                assert.isFalse(extraEventHandlerSpy.called);
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        describe('with changeableSelector without data-confirm', function () {
 | 
						|
            var elementId = 'data-methods-change';
 | 
						|
            var $element;
 | 
						|
 | 
						|
            before(function () {
 | 
						|
                $element = $('#' + elementId);
 | 
						|
            });
 | 
						|
 | 
						|
            after(function () {
 | 
						|
                $element.val('');
 | 
						|
            });
 | 
						|
 | 
						|
            it('should call handleAction method only', function () {
 | 
						|
                var event = $.Event('change');
 | 
						|
                assert.lengthOf($element, 1);
 | 
						|
 | 
						|
                $element.val(1);
 | 
						|
                $element.trigger(event);
 | 
						|
 | 
						|
                assert.isFalse(yiiConfirmSpy.called);
 | 
						|
 | 
						|
                assert.isTrue(yiiHandleActionStub.calledOnce);
 | 
						|
                assert.equal(yiiHandleActionStub.getCall(0).args[0].attr('id'), elementId);
 | 
						|
                assert.strictEqual(yiiHandleActionStub.getCall(0).args[1], event);
 | 
						|
 | 
						|
                assert.isFalse(extraEventHandlerSpy.called);
 | 
						|
            });
 | 
						|
        });
 | 
						|
    });
 | 
						|
});
 |