/* global $ */ const mw = require( '../utils/mw' ), mustache = require( '../utils/mustache' ), jQuery = require( '../utils/jQuery' ), dom = require( '../utils/dom' ), mfExtend = require( '../../../src/mobile.startup/mfExtend' ), util = require( '../../../src/mobile.startup/util' ), oo = require( '../utils/oo' ), sinon = require( 'sinon' ); let View; /** @type {sinon.SinonSandbox} */ let sandbox; QUnit.module( 'MobileFrontend mobile.startup/View', { beforeEach: function () { sandbox = sinon.sandbox.create(); dom.setUp( sandbox, global ); jQuery.setUp( sandbox, global ); oo.setUp( sandbox, global ); mw.setUp( sandbox, global ); mustache.setUp( sandbox, global ); View = require( '../../../src/mobile.startup/View' ); }, afterEach: function () { jQuery.tearDown(); sandbox.restore(); } } ); QUnit.test( 'View', function ( assert ) { const el = document.createElement( 'div' ), view = new View( { el } ); assert.strictEqual( view.$el[ 0 ].outerHTML, '
', 'Views use el and treat as CSS selector. view border box by default' ); assert.strictEqual( view.$el[ 0 ].tagName.toUpperCase(), 'DIV', 'assign proper jQuery object to $el' ); } ); QUnit.test( 'View, jQuery proxy functions', function ( assert ) { const view = new View( { el: 'body' } ); [ 'append', 'prepend', 'appendTo', 'prependTo', 'after', 'before', 'insertAfter', 'insertBefore', 'remove', 'detach' ].forEach( function ( prop ) { const stub = sandbox.stub( view.$el, prop ); view[ prop ]( 'test', 1 ); assert.true( stub.calledWith( 'test', 1 ) ); stub.restore(); } ); } ); QUnit.test( 'View#preRender', function ( assert ) { function ChildView() { View.apply( this, arguments ); } mfExtend( ChildView, View, { template: util.template( '

{{text}}

' ), preRender: function () { this.options.text = 'hello'; } } ); const view = new ChildView(); assert.strictEqual( view.$el.html(), '

hello

', 'manipulate template data' ); } ); QUnit.test( 'View#postRender', function ( assert ) { const spy = sandbox.spy(); function ChildView() { View.apply( this, arguments ); } mfExtend( ChildView, View, { postRender: function () { spy(); } } ); // eslint-disable-next-line no-new new ChildView(); assert.strictEqual( spy.callCount, 1, 'invoke postRender' ); } ); QUnit.test( 'View#delegateEvents', function ( assert ) { function EventsView( props ) { View.call( this, util.extend( { events: { 'click p span': function ( ev ) { ev.preventDefault(); assert.true( true, 'Span was clicked and handled' ); }, 'click p': 'onParagraphClick', click: 'onClick' } }, props ) ); } mfExtend( EventsView, View, { template: util.template( '

test

' ), onParagraphClick: function ( ev ) { ev.preventDefault(); assert.true( true, 'Paragraph was clicked and handled' ); }, onClick: function ( ev ) { ev.preventDefault(); assert.true( true, 'View was clicked and handled' ); } } ); const view = new EventsView(); view.appendTo( 'body' ); // Check if events are set and handlers called view.$el.find( 'span' ).trigger( 'click' ); view.$el.find( 'p' ).trigger( 'click' ); view.$el.trigger( 'click' ); // Check if events can be unset and handlers are not called view.undelegateEvents(); view.$el.find( 'span' ).trigger( 'click' ); view.$el.find( 'p' ).trigger( 'click' ); view.$el.trigger( 'click' ); } ); QUnit.test( 'View#render (with isTemplateMode)', function ( assert ) { const $parent = $( '
' ); function TemplateModeView() { View.apply( this, arguments ); } mfExtend( TemplateModeView, View, { template: util.template( '

{{text}}

' ), isTemplateMode: true } ); function ContainerView() { View.call( this, { className: 'bar' } ); } mfExtend( ContainerView, View, { template: util.template( '

test

' ) } ); const view = new TemplateModeView(); const textFirstRun = view.$el.text(); const view2 = new ContainerView(); view.render(); view2.render(); // attach to the DOM view.$el.appendTo( $parent ); // and then do a second render... view.render( { text: 'hello' } ); assert.true( view.$el.hasClass( 'foo' ) ); assert.true( view2.$el.hasClass( 'bar' ) ); assert.strictEqual( textFirstRun, '', 'first run, no text defined' ); assert.strictEqual( view.$el.text(), 'hello', 'second run, text has been defined' ); assert.strictEqual( view.$el.parent( $parent ).length, 1, 'A re-rendered view thats attached to the DOM remains attached to the DOM' ); } ); QUnit.test( 'View#render events (with isTemplateMode)', function ( assert ) { function TemplateModeView( props ) { View.call( this, util.extend( { events: { 'click span': 'onClick' } }, props ) ); } mfExtend( TemplateModeView, View, { onClick: function () { this.$el.empty().text( 'hello world' ); }, template: util.template( '

test

' ), isTemplateMode: true } ); const view = new TemplateModeView(); // trigger event view.$el.find( 'span' ).trigger( 'click' ); assert.strictEqual( view.$el.text(), 'hello world', 'event was called' ); assert.strictEqual( view.$el.find( 'span' ).length, 0, 'span disappeared' ); // do same again but call render twice const view2 = new TemplateModeView(); // force a re-render view2.render(); // trigger event to show events didn't get lost view2.$el.find( 'span' ).trigger( 'click' ); assert.strictEqual( view2.$el.text(), 'hello world', 'event was called' ); assert.strictEqual( view2.$el.find( 'span' ).length, 0, 'span disappeared' ); } ); QUnit.test( 'View with className option', function ( assert ) { [ [ new View(), 'view-border-box', 'className not defined on a normal View without options and isBorderBox is default' ], [ new View( {} ), 'view-border-box', 'className not defined on a normal View with empty options and isBorderBox is default' ], [ new View( { className: 'banana' } ), 'banana view-border-box', 'option is passed to View (along with default isBorderBox property)' ], [ new View( { isBorderBox: false } ), undefined, 'Passing isBorderBox option removes default view-border-box class' ], [ new View( { isBorderBox: true } ), 'view-border-box', 'Passing isBorderBox option as true retains default view-border-box class' ] ].forEach( function ( test ) { assert.strictEqual( test[0].$el.attr( 'class' ), test[1], test[2] ); } ); } ); QUnit.test( 'View.make()', function ( assert ) { const view = View.make( { className: 'foo' }, [ util.parseHTML( '
' ).text( 'hello' ) ] ); assert.strictEqual( view.$el.find( '> div' ).text().trim(), 'hello', 'view created with element' ); } );