<commit_before>import chai from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import {Route, createRouter, destroyRouter, middleware} from '../src/index';
import Mn from 'backbone.marionette';
import Backbone from 'backbone';

let expect = chai.expect;
chai.use(sinonChai);

let router, routes;
let RootRoute, ParentRoute, ChildRoute, GrandChildRoute, LeafRoute;

let ParentView = Mn.View.extend({
  template: function () {
    return '<div class="child-view"></div>'
  },
  regions: {
    outlet: '.child-view'
  }
});

let GrandChildView = Mn.View.extend({
  tagName: 'h2',
  template: function () {
    return 'GrandChild'
  }
});

let LeafView = Mn.View.extend({
  template: function () {
    return 'Leaf'
  }
});

describe('Render', () => {

  let $;

  before(function () {
    this.jsdom = require('jsdom-global')()
    Backbone.$ = $ = require('jquery')(window)
  });

  after(function () {
    this.jsdom()
  })

  beforeEach(() => {
    router = createRouter({location: 'memory'});
    router.use(middleware);
    ParentRoute = Route.extend({
      viewClass: ParentView
    });
    RootRoute = Route.extend({}), ChildRoute = Route.extend({}),
      GrandChildRoute = Route.extend({}), LeafRoute = Route.extend({});
    routes = function (route) {
      route('parent', {routeClass: ParentRoute}, function () {
        route('child', {routeClass: ChildRoute}, function () {
          route('grandchild', {viewClass: GrandChildView}, function () {
            route('leaf', {routeClass: LeafRoute, viewClass: LeafView})
          })
        })
      });
      route('root', {routeClass: RootRoute, routeOptions: {viewClass: ParentView}});
      route('root2', {viewClass: ParentView})
    };
    router.map(routes);
    router.listen();

    document.body.innerHTML = '<div id="main"></div>';
    let RootRegion = Mn.Region.extend({
      el: '#main'
    });
    router.rootRegion = new RootRegion()
  });

  afterEach(() => {
    destroyRouter(router);
  });

  describe('viewClass', function () {

    it('can be defined in the Route class', function(done){
      router.transitionTo('parent').then(function () {
        expect($('#main').html()).to.be.equal('<div><div class="child-view"></div></div>');
        done()
      }).catch(done)
    });

    it('can be passed through routeOptions.viewClass', function(done){
      router.transitionTo('root').then(function () {
        expect($('#main').html()).to.be.equal('<div><div class="child-view"></div></div>');
        done()
      }).catch(done)
    });

    it('can be passed through viewClass, without a routeClass', function(done){
      router.transitionTo('root2').then(function () {
        expect($('#main').html()).to.be.equal('<div><div class="child-view"></div></div>');
        done()
      }).catch(done)
    });

    it('will propagate events defined in viewEvents to Route ', function (done) {
      let spy1 = sinon.spy()
      let spy2 = sinon.spy()
      RootRoute.prototype.viewEvents = {
        'my:event': function () {
          spy1()
        },
        'other:event': function () {
          spy1()
        }
      }
      router.transitionTo('root').then(function () {
        router.rootRegion.currentView.trigger('my:event')
        expect(spy1).to.be.calledOnce
        expect(spy2).to.not.be.called
        done()
      }).catch(done)

    });

    describe('of a root route', function () {

      it('should be rendered in rootRegion', function (done) {
        router.transitionTo('parent').then(function () {
          expect($('#main').html()).to.be.equal('<div><div class="child-view"></div></div>');
          done()
        }).catch(done)
      });

      it('should abort transition if no rootRegion is defined', function (done) {
        router.rootRegion = null;
        router.transitionTo('parent').then(function () {
          done('transition resolved')
        }).catch(function (error) {
          expect(error).to.be.an('error');
          expect(error.message).to.be.equal('No outlet region');
          done()
        })
      })
    });

    describe('of a child route', function () {

      it('should be rendered in the outlet region of the nearest route with a view', function (done) {
        router.transitionTo('grandchild').then(function () {
          expect($('#main').html()).to.be.equal('<div><div class="child-view"><h2>GrandChild</h2></div></div>');
          done()
        }).catch(done)
      });

      it('should abort transition if no outlet region is defined in the nearest route with a view', function (done) {
        router.transitionTo('leaf').then(function () {
          done('transition resolved')
        }).catch(function (error) {
          expect(error).to.be.an('error');
          expect(error.message).to.be.equal('No outlet region in view');
          done()
        })
      })

    })

    describe('of a target route', function () {

      it('should be rendered even if already activated', function () {
        let spy = sinon.spy(ParentView.prototype, 'render')
        return router.transitionTo('grandchild').then(function () {
          return router.transitionTo('parent')
        }).then(function () {
          expect(spy).to.be.calledTwice;
          expect($('#main').html()).to.be.equal('<div><div class="child-view"></div></div>');
        })
      });
    })

  })

  describe('renderView', function () {
    it('should be called with region and transition objects', function () {
      let spy = sinon.spy(ParentRoute.prototype, 'renderView')
      let transition = router.transitionTo('parent')
      return transition.then(function () {
        expect(spy).to.be.calledOnce.and.calledWith(router.rootRegion, transition)
      })
    })
  })

  describe('updateView', function () {

    it('should be called only if route has a rendered view', function () {
      let spy = sinon.spy(ParentRoute.prototype, 'updateView')
      let transition
      return router.transitionTo('parent').then(function () {
        expect(spy).not.to.be.called
        //force a new render
        transition = router.transitionTo('parent', {}, {id: 1})
        return transition
      }).then(function () {
        expect(spy).to.be.calledOnce.and.calledWith(transition)
      })
    })

    it('should prevent view re render if returns truthy', function () {
      let routeInstance, savedView
      sinon.stub(ParentRoute.prototype, 'initialize', function () {
        routeInstance = this
      })

      ParentRoute.prototype.updateView = function () {
        return true
      }
<commit_msg>Add tests for renderView timing<commit_after>import chai from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import {Route, createRouter, destroyRouter, middleware} from '../src/index';
import _ from 'underscore';
import Radio from 'backbone.radio';
import Mn from 'backbone.marionette';
import Backbone from 'backbone';

let expect = chai.expect;
chai.use(sinonChai);

let router, routes;
let RootRoute, ParentRoute, ChildRoute, GrandChildRoute, LeafRoute;

let ParentView = Mn.View.extend({
  template: function () {
    return '<div class="child-view"></div>'
  },
  regions: {
    outlet: '.child-view'
  }
});

let GrandChildView = Mn.View.extend({
  tagName: 'h2',
  template: function () {
    return 'GrandChild'
  }
});

let LeafView = Mn.View.extend({
  template: function () {
    return 'Leaf'
  }
});

describe('Render', () => {

  let $;

  before(function () {
    this.jsdom = require('jsdom-global')()
    Backbone.$ = $ = require('jquery')(window)
  });

  after(function () {
    this.jsdom()
  })

  beforeEach(() => {
    router = createRouter({location: 'memory'});
    router.use(middleware);
    ParentRoute = Route.extend({
      viewClass: ParentView
    });
    RootRoute = Route.extend({}), ChildRoute = Route.extend({}),
      GrandChildRoute = Route.extend({}), LeafRoute = Route.extend({});
    routes = function (route) {
      route('parent', {routeClass: ParentRoute}, function () {
        route('child', {routeClass: ChildRoute}, function () {
          route('grandchild', {viewClass: GrandChildView}, function () {
            route('leaf', {routeClass: LeafRoute, viewClass: LeafView})
          })
        })
      });
      route('root', {routeClass: RootRoute, routeOptions: {viewClass: ParentView}});
      route('root2', {viewClass: ParentView})
    };
    router.map(routes);
    router.listen();

    document.body.innerHTML = '<div id="main"></div>';
    let RootRegion = Mn.Region.extend({
      el: '#main'
    });
    router.rootRegion = new RootRegion()
  });

  afterEach(() => {
    destroyRouter(router);
  });

  describe('viewClass', function () {

    it('can be defined in the Route class', function(done){
      router.transitionTo('parent').then(function () {
        expect($('#main').html()).to.be.equal('<div><div class="child-view"></div></div>');
        done()
      }).catch(done)
    });

    it('can be passed through routeOptions.viewClass', function(done){
      router.transitionTo('root').then(function () {
        expect($('#main').html()).to.be.equal('<div><div class="child-view"></div></div>');
        done()
      }).catch(done)
    });

    it('can be passed through viewClass, without a routeClass', function(done){
      router.transitionTo('root2').then(function () {
        expect($('#main').html()).to.be.equal('<div><div class="child-view"></div></div>');
        done()
      }).catch(done)
    });

    it('will propagate events defined in viewEvents to Route ', function (done) {
      let spy1 = sinon.spy()
      let spy2 = sinon.spy()
      RootRoute.prototype.viewEvents = {
        'my:event': function () {
          spy1()
        },
        'other:event': function () {
          spy1()
        }
      }
      router.transitionTo('root').then(function () {
        router.rootRegion.currentView.trigger('my:event')
        expect(spy1).to.be.calledOnce
        expect(spy2).to.not.be.called
        done()
      }).catch(done)

    });

    describe('of a root route', function () {

      it('should be rendered in rootRegion', function (done) {
        router.transitionTo('parent').then(function () {
          expect($('#main').html()).to.be.equal('<div><div class="child-view"></div></div>');
          done()
        }).catch(done)
      });

      it('should abort transition if no rootRegion is defined', function (done) {
        router.rootRegion = null;
        router.transitionTo('parent').then(function () {
          done('transition resolved')
        }).catch(function (error) {
          expect(error).to.be.an('error');
          expect(error.message).to.be.equal('No outlet region');
          done()
        })
      })
    });

    describe('of a child route', function () {

      it('should be rendered in the outlet region of the nearest route with a view', function (done) {
        router.transitionTo('grandchild').then(function () {
          expect($('#main').html()).to.be.equal('<div><div class="child-view"><h2>GrandChild</h2></div></div>');
          done()
        }).catch(done)
      });

      it('should abort transition if no outlet region is defined in the nearest route with a view', function (done) {
        router.transitionTo('leaf').then(function () {
          done('transition resolved')
        }).catch(function (error) {
          expect(error).to.be.an('error');
          expect(error.message).to.be.equal('No outlet region in view');
          done()
        })
      })

    })

    describe('of a target route', function () {

      it('should be rendered even if already activated', function () {
        let spy = sinon.spy(ParentView.prototype, 'render')
        return router.transitionTo('grandchild').then(function () {
          return router.transitionTo('parent')
        }).then(function () {
          expect(spy).to.be.calledTwice;
          expect($('#main').html()).to.be.equal('<div><div class="child-view"></div></div>');
        })
      });
    })

  })

  describe('renderView', function () {
    it('should be called with region and transition objects', function () {
      let spy = sinon.spy(ParentRoute.prototype, 'renderView')
      let transition = router.transitionTo('parent')
      return transition.then(function () {
        expect(spy).to.be.calledOnce.and.calledWith(router.rootRegion, transition)
      })
    })

    it('should be called after activate is resolved', function () {
      let spy = sinon.spy(ParentRoute.prototype, 'renderView')
      let activateSpy = sinon.spy()
      ParentRoute.prototype.activate = function () {
        return new Promise(function (resolve) {
          setTimeout(function () {
            resolve()
          }, 100)
        })
      }
      Radio.channel('router').on('activate', activateSpy)

      return router.transitionTo('parent').then(function () {
        expect(spy).to.be.calledAfter(activateSpy)
      })
    })

    it('should be not be called when transition is cancelled', function (done) {
      let spy = sinon.spy(ParentRoute.prototype, 'renderView')
      ParentRoute.prototype.activate = function (transition) {
        transition.cancel()
      }

      router.transitionTo('parent').catch(function () {
        _.defer(function () {
          expect(spy).to.not.be.called
          done()
        })
      })
    })
  })

  describe('updateView', function () {

    it('should be called only if route has a rendered view', function () {
      let spy = sinon.spy(ParentRoute.prototype, 'updateView')
      let transition
      return router.transitionTo('parent').then(function () {
        expect(spy).not.to.be.called
        //force a new render
        transition = router.transitionTo('parent', {}, {id: 1})
        return transition
      }).then(function () {
        expect(spy).to.be.calledOnce.and.calledWith(transition)
      })
    })

    it('should prevent view re render if returns truthy', function () {
      let routeInstance, savedView
      sinon.stub(ParentRoute.prototype, 'initialize', function () {
        routeInstance = this
      })

      ParentRoute.prototype.updateView = function () {
        return true
      }

{'fraction_non_alphanumeric': 0.16653751320887636, 'fraction_numerical': 0.0019020781965480803, 'mean_word_length': 2.726962457337884, 'pattern_counts': {'":': 0, '<': 63, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5850693', 'n_tokens_mistral': 4131, 'n_tokens_neox': 4043, 'n_words': 869}