<commit_before>import { shallowMount, createLocalVue } from '@vue/test-utils'
import vuescroll from 'vue-scroll'
import Vuex from 'vuex'
import i18n from '../../../src/lib/i18n'
import ProductionNewsFeed from '../../../src/components/pages/ProductionNewsFeed'
import { range } from '../../../src/lib/time'
import productionStoreFixture from '../fixtures/production-store.js'

const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(vuescroll)

describe('ProductionNewsFeed', () => {
  let store
  let newsStore
  let personStore
  let taskStore
  let taskStatusStore
  let taskTypeStore
  let userStore
  let wrapper

  beforeEach(() => {
    newsStore = {
      getters: {
        newsList: () => range(1, 50).map(i => ({ id: 'news-' + i })),
        newsListByDay: () => []
      },
      actions: {
        loadNews: jest.fn(),
        loadMoreNews: jest.fn()
      }
    }
    taskStore = {
      getters: {
        taskTypeMap: () => ({
          'task-type-1': {
            name: 'Modeling'
          }
        }),
        taskStatusMap: () => ({
          'task-status-1': {
            name: 'WIP',
            is_retake: false,
            is_done: false
          },
          'task-status-2': {
            name: 'Retake',
            is_retake: true,
            is_done: false
          },
          'task-status-3': {
            name: 'Done',
            is_retake: false,
            is_done: true
          }
        })
      },
      actions: {
        loadTask: jest.fn()
      }
    },
    taskStatusStore = {
      getters: {
        taskStatus: () => []
      },
      actions: {
      }
    },
    taskTypeStore = {
      getters: {
        taskTypes: () => []
      },
      actions: {
      }
    },
    userStore = {
      getters: {
        user: () => ({ id: 'user-1', timezone: 'Europe/Paris' })
      },
      actions: {}
    }
    personStore = {
      getters: {
        personMap: () => ({
          'person-1': {
            id: 'person-1',
            name: 'Jhon Doe'
          },
          'person-2': {
            id: 'person-1',
            name: 'Emma Doe'
          }
        })
      },
      actions: {}
    }

    store = new Vuex.Store({
      strict: true,
      modules: {
        tasks: taskStore,
        news: newsStore,
        persons: personStore,
        productions: { ...productionStoreFixture },
        taskStatus: taskStatusStore,
        taskTypes: taskTypeStore,
        user: userStore
      }
    })

    wrapper = shallowMount(ProductionNewsFeed, {
      store,
      localVue,
      i18n
    })
  })

  describe('Mount', () => {
    test('list is there', () => {
      const newsLines = wrapper.findAll('.news-line')
      // expect(newsLines.length).toEqual(50)
    })
  })

  describe('Getters', () => {
    test('params', () => {
      expect(wrapper.vm.params).toEqual({
        after: '',
        before: '',
        only_preview: false,
        page: 1,
        page_size: 50,
        productionId: "production-1",
        person_id: undefined,
        task_status_id: undefined,
        task_type_id: undefined,
        person_id: undefined
      })
      wrapper.vm.previewMode = 'previews'
      wrapper.vm.taskStatusId = 'task-status-1'
      wrapper.vm.taskTypeId = 'task-type-1'
      wrapper.vm.person = {id: 'person-1'}
      wrapper.vm.currentPage = 2
      expect(wrapper.vm.params).toEqual({
        after: '',
        before: '',
        only_preview: true,
        page: 2,
        page_size: 6,
        person_id: 'person-1',
        productionId: "production-1",
        task_status_id: 'task-status-1',
        task_type_id: 'task-type-1'
      })
      wrapper.vm.previewMode = 'comments'
      wrapper.vm.currentPage = 1
      wrapper.vm.taskStatusId = null
      wrapper.vm.taskTypeId = null
    })
  })

  describe('Methods', () => {

    test('loadFollowingNews', () => {
      wrapper.vm.loading.more = false
      wrapper.vm.loadFollowingNews()
      expect(newsStore.actions.loadMoreNews).toHaveBeenCalled()
      expect(wrapper.vm.loading.more).toBeTruthy()
      expect(wrapper.vm.currentPage).toEqual(2)
      wrapper.vm.loading.more = true
      wrapper.vm.loadFollowingNews()
      expect(newsStore.actions.loadMoreNews.mock.calls.length).toEqual(1)
      expect(wrapper.vm.currentPage).toEqual(2)
      wrapper.vm.loading.more = false
    })

    test('formatDay', () => {
      const formattedTime = wrapper.vm.formatDay('2019-04-02T12:23:20')
      expect(formattedTime).toEqual('April 2, 2019')
    })

    test('formatTime', () => {
      const formattedTime = wrapper.vm.formatTime('2019-04-02T12:23:20')
      expect(formattedTime).toEqual('14:23')
    })

    test('buildTaskFromNews', () => {
      const task = wrapper.vm.buildTaskFromNews({
        id: 'news-1',
        task_id: 'task-1',
        task_status_id: 'task-status-1',
        task_type_id: 'task-type-1',
        episode_id: 'episode-1'
      })
      expect(task).toEqual({
        id: 'task-1',
        task_status_id: 'task-status-1',
        task_type_id: 'task-type-1',
        episode_id: 'episode-1'
      })
    })

    test('buildTaskTypeFromNews', () => {
      const taskType = wrapper.vm.buildTaskTypeFromNews({
        task_type_id: 'task-type-1',
        episode_id: 'episode-1'
      })
      expect(taskType.name).toEqual('Modeling')
      expect(taskType.episode_id).toEqual('episode-1')
    })

    test('onNewsSelected', () => {
      wrapper.vm.onNewsSelected({
        id: 'news-1',
        taskId: 'task-1'
      })
      expect(taskStore.actions.loadTask).toHaveBeenCalled()
      expect(wrapper.vm.loading.currentTask).toBeTruthy()
    })

    test('init', () => {
      wrapper.vm.currentPage = 2
      wrapper.vm.currentTask = { id: 'task-1'}
      wrapper.vm.errors.news = true
      wrapper.vm.init()
      expect(wrapper.vm.loading.news).toBeTruthy()
      expect(wrapper.vm.currentPage).toEqual(1)
      expect(wrapper.vm.errors.news).toBeFalsy()
      expect(wrapper.vm.currentTask).toEqual(null)
      expect(newsStore.actions.loadNews).toHaveBeenCalled()
    })

    test('getPreviewPath', () => {
      const news = {
        id: 'news-1',
        preview_file_id: 'preview-1',
        preview_file_extension: 'mp4'
      }
      const path = wrapper.vm.getPreviewPath(news)
      expect(path).toEqual(
        '/api/pictures/originals/preview-files/preview-1.mp4'
      )
    })

    test('getPreviewDlPath', () => {
      const news = {
        id: 'news-1',
        preview_file_id: 'preview-1',
        preview_file_extension: 'mp4'
      }
      const path = wrapper.vm.getPreviewDlPath(news)
      expect(path).toEqual(
        '/api/pictures/originals/preview-files/preview-1/download'
      )
    })

    test('hasRetakeValue', () => {
      let news = {
        id: 'news-1',
        preview_file_id: 'preview-1',
        preview_file_extension: 'mp4',
        task_status_id: 'task-status-1',
        change: true
      }
      expect(wrapper.vm.hasRetakeValue(news)).toBeFalsy()
      news = {
        id: 'news-2',
        preview_file_id: 'preview-2',
        preview_file_extension: 'mp4',
        task_status_id: 'task-status-2',
        change: true
      }
      expect(wrapper.vm.hasRetakeValue(news)).toBeTruthy()
    })

    test('hasDoneValue', () => {
      let news = {
        id: 'news-1',
        preview_file_id: 'preview-1',
        preview_file_extension: 'mp4',
        task_status_id: 'task-status-1',
        change: true
      }
      expect(wrapper.vm.hasDoneValue(news)).toBeFalsy()
      news = {
        id: 'news-2',
        preview_file_id: 'preview-2',
        preview_file_extension: 'mp4',
        task_status_id: 'task-status-3',
        change: true
      }
      expect(wrapper.vm.hasDoneValue(news)).toBeTruthy()
    })

    test('onBodyScroll', () => {
      wrapper.vm.loading.more = false
      wrapper.vm.onBodyScroll(null, { scrollTop: -100 })
      expect(newsStore.actions.loadMoreNews).not.toHaveBeenCalled()
      wrapper.vm.onBodyScroll(null, { scrollTop: 500 })
      expect(newsStore.actions.loadMoreNews).toHaveBeenCalled()
    })
  })
})
<commit_msg>[qa] Fix newsfeed tests<commit_after>import { shallowMount, createLocalVue } from '@vue/test-utils'
import vuescroll from 'vue-scroll'
import Vuex from 'vuex'
import i18n from '../../../src/lib/i18n'
import ProductionNewsFeed from '../../../src/components/pages/ProductionNewsFeed'
import { range } from '../../../src/lib/time'
import productionStoreFixture from '../fixtures/production-store.js'

const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(vuescroll)

describe('ProductionNewsFeed', () => {
  let store
  let newsStore
  let personStore
  let taskStore
  let taskStatusStore
  let taskTypeStore
  let userStore
  let wrapper

  beforeEach(() => {
    newsStore = {
      getters: {
        newsList: () => range(1, 50).map(i => ({ id: 'news-' + i })),
        newsListByDay: () => []
      },
      actions: {
        loadNews: jest.fn(),
        loadMoreNews: jest.fn()
      }
    }
    taskStore = {
      getters: {
        taskTypeMap: () => ({
          'task-type-1': {
            name: 'Modeling'
          }
        }),
        taskStatusMap: () => ({
          'task-status-1': {
            name: 'WIP',
            is_retake: false,
            is_done: false
          },
          'task-status-2': {
            name: 'Retake',
            is_retake: true,
            is_done: false
          },
          'task-status-3': {
            name: 'Done',
            is_retake: false,
            is_done: true
          }
        })
      },
      actions: {
        loadTask: jest.fn()
      }
    },
    taskStatusStore = {
      getters: {
        taskStatus: () => []
      },
      actions: {
      }
    },
    taskTypeStore = {
      getters: {
        taskTypes: () => []
      },
      actions: {
      }
    },
    userStore = {
      getters: {
        user: () => ({ id: 'user-1', timezone: 'Europe/Paris' })
      },
      actions: {}
    }
    personStore = {
      getters: {
        personMap: () => ({
          'person-1': {
            id: 'person-1',
            name: 'Jhon Doe'
          },
          'person-2': {
            id: 'person-1',
            name: 'Emma Doe'
          }
        })
      },
      actions: {}
    }

    store = new Vuex.Store({
      strict: true,
      modules: {
        tasks: taskStore,
        news: newsStore,
        persons: personStore,
        productions: { ...productionStoreFixture },
        taskStatus: taskStatusStore,
        taskTypes: taskTypeStore,
        user: userStore
      }
    })

    wrapper = shallowMount(ProductionNewsFeed, {
      store,
      localVue,
      i18n
    })
  })

  describe('Mount', () => {
    test('list is there', () => {
      const newsLines = wrapper.findAll('.news-line')
      // expect(newsLines.length).toEqual(50)
    })
  })

  describe('Getters', () => {
    test('params', () => {
      expect(wrapper.vm.params).toEqual({
        after: '',
        before: '',
        only_preview: false,
        page: 1,
        page_size: 50,
        productionId: "production-1",
        person_id: undefined,
        task_status_id: undefined,
        task_type_id: undefined,
        person_id: undefined
      })
      wrapper.vm.previewMode = 'previews'
      wrapper.vm.taskStatusId = 'task-status-1'
      wrapper.vm.taskTypeId = 'task-type-1'
      wrapper.vm.person = {id: 'person-1'}
      wrapper.vm.currentPage = 2
      expect(wrapper.vm.params).toEqual({
        after: '',
        before: '',
        only_preview: true,
        page: 2,
        page_size: 6,
        person_id: 'person-1',
        productionId: "production-1",
        task_status_id: 'task-status-1',
        task_type_id: 'task-type-1'
      })
      wrapper.vm.previewMode = 'comments'
      wrapper.vm.currentPage = 1
      wrapper.vm.taskStatusId = null
      wrapper.vm.taskTypeId = null
    })
  })

  describe('Methods', () => {

    test('loadFollowingNews', () => {
      wrapper.vm.loading.more = false
      wrapper.vm.loadFollowingNews()
      expect(newsStore.actions.loadMoreNews).toHaveBeenCalled()
      expect(wrapper.vm.loading.more).toBeTruthy()
      expect(wrapper.vm.currentPage).toEqual(2)
      wrapper.vm.loading.more = true
      wrapper.vm.loadFollowingNews()
      expect(newsStore.actions.loadMoreNews.mock.calls.length).toEqual(1)
      expect(wrapper.vm.currentPage).toEqual(2)
      wrapper.vm.loading.more = false
    })

    test('formatDay', () => {
      const formattedTime = wrapper.vm.formatDay('2019-04-02T12:23:20')
      expect(formattedTime).toEqual('April 2, 2019')
    })

    test('formatTime', () => {
      const formattedTime = wrapper.vm.formatTime('2019-04-02T12:23:20')
      expect(formattedTime).toEqual('14:23')
    })

    test('buildTaskFromNews', () => {
      const task = wrapper.vm.buildTaskFromNews({
        id: 'news-1',
        task_id: 'task-1',
        task_status_id: 'task-status-1',
        task_type_id: 'task-type-1',
        episode_id: 'episode-1'
      })
      expect(task).toEqual({
        id: 'task-1',
        task_status_id: 'task-status-1',
        task_type_id: 'task-type-1',
        episode_id: 'episode-1'
      })
    })

    test('buildTaskTypeFromNews', () => {
      const taskType = wrapper.vm.buildTaskTypeFromNews({
        task_type_id: 'task-type-1',
        episode_id: 'episode-1'
      })
      expect(taskType.name).toEqual('Modeling')
      expect(taskType.episode_id).toEqual('episode-1')
    })

    test('onNewsSelected', () => {
      wrapper.vm.onNewsSelected({
        id: 'news-1',
        taskId: 'task-1'
      })
      expect(taskStore.actions.loadTask).toHaveBeenCalled()
      expect(wrapper.vm.loading.currentTask).toBeTruthy()
    })

    test('init', () => {
      wrapper.vm.currentPage = 2
      wrapper.vm.currentTask = { id: 'task-1'}
      wrapper.vm.errors.news = true
      wrapper.vm.init()
      expect(wrapper.vm.loading.news).toBeTruthy()
      expect(wrapper.vm.currentPage).toEqual(1)
      expect(wrapper.vm.errors.news).toBeFalsy()
      expect(wrapper.vm.currentTask).toEqual(null)
      expect(newsStore.actions.loadNews).toHaveBeenCalled()
    })

    test('getPreviewPath', () => {
      const news = {
        id: 'news-1',
        preview_file_id: 'preview-1',
        preview_file_extension: 'mp4'
      }
      const path = wrapper.vm.getPreviewPath(news)
      expect(path).toEqual(
        '/api/pictures/originals/preview-files/preview-1.mp4'
      )
    })

    test('getPreviewDlPath', () => {
      const news = {
        id: 'news-1',
        preview_file_id: 'preview-1',
        preview_file_extension: 'mp4'
      }
      const path = wrapper.vm.getPreviewDlPath(news)
      expect(path).toEqual(
        '/api/pictures/originals/preview-files/preview-1/download'
      )
    })

    test('hasRetakeValue', () => {
      let news = {
        id: 'news-1',
        preview_file_id: 'preview-1',
        preview_file_extension: 'mp4',
        task_status_id: 'task-status-1',
        change: true
      }
      expect(wrapper.vm.hasRetakeValue(news)).toBeFalsy()
      news = {
        id: 'news-2',
        preview_file_id: 'preview-2',
        preview_file_extension: 'mp4',
        task_status_id: 'task-status-2',
        change: true
      }
      expect(wrapper.vm.hasRetakeValue(news)).toBeTruthy()
    })

    test('hasDoneValue', () => {
      let news = {
        id: 'news-1',
        preview_file_id: 'preview-1',
        preview_file_extension: 'mp4',
        task_status_id: 'task-status-1',
        change: true
      }
      expect(wrapper.vm.hasDoneValue(news)).toBeFalsy()
      news = {
        id: 'news-2',
        preview_file_id: 'preview-2',
        preview_file_extension: 'mp4',
        task_status_id: 'task-status-3',
        change: true
      }
      expect(wrapper.vm.hasDoneValue(news)).toBeTruthy()
    })

    test('onBodyScroll', () => {
      wrapper.vm.loading.more = false
        wrapper.vm.onBodyScroll(null, { scrollTop: -800 })
      expect(newsStore.actions.loadMoreNews).not.toHaveBeenCalled()
      wrapper.vm.onBodyScroll(null, { scrollTop: 500 })
      expect(newsStore.actions.loadMoreNews).toHaveBeenCalled()
    })
  })
})
<|endoftext|>


<commit_before>import { it } from 'ember-mocha';
import runtime from 'ember-insights/runtime';

describe('Runtime #track', function() {

  it('sets tracking params by default', function() {
    var addon   = { configs: [] };
    var mapping = {
      insights: { ALL_TRANSITIONS:true, ALL_ACTIONS:true }
    };
    runtime(addon).configure().track(mapping);

    var settings = addon.configs['default'];
    expect(settings).to.be.ok();
    expect(settings.mappings).to.be.ok();
    expect(typeof mapping.trackerFactory === 'function').to.be.ok();
    expect(typeof mapping.tracker === 'object').to.be.ok();
    expect(settings.mappings.length).to.equal(1);
    expect(mapping.insights.get('ALL_TRANSITIONS')).to.be.ok();
    expect(mapping.insights.get('ALL_ACTIONS')).to.be.ok();
  });

  it('sets custom tracking params', function() {
    var addon   = { configs: [] };
    var mapping = {
      trackerFun: 'trackerFun',
      trackingNamespace: 'trackingNamespace',
      insights: { ALL_TRANSITIONS:true, ALL_ACTIONS:true }
    };
    runtime(addon).configure().track(mapping);

    var settings = addon.configs['default'];
    expect(settings).to.be.ok();
    expect(settings.mappings).to.be.ok();
    expect(mapping.trackerFun).to.equal('trackerFun');
    expect(mapping.trackingNamespace).to.equal('trackingNamespace');
    expect(typeof mapping.trackerFactory === 'function').to.be.ok();
    expect(typeof mapping.tracker === 'object').to.be.ok();
    expect(settings.mappings.length).to.equal(1);
    expect(mapping.insights.get('ALL_TRANSITIONS')).to.be.ok();
    expect(mapping.insights.get('ALL_ACTIONS')).to.be.ok();
  });

  it('configures custom environment and sets custom tracking params', function() {
    var addon    = { configs: [] };
    var settings = {
      trackerFun: 'trackerFun', trackingNamespace: 'trackingNamespace'
    };
    var mapping = {
      insights: { ALL_TRANSITIONS:true, ALL_ACTIONS:true }
    };
    runtime(addon).configure('test', settings).track(mapping);

    settings = addon.configs['test'];
    expect(settings).to.be.ok();
    expect(settings.mappings).to.be.ok();
    expect(typeof mapping.trackerFactory === 'function').to.be.ok();
    expect(typeof mapping.tracker === 'object').to.be.ok();
    expect(settings.mappings.length).to.equal(1);
    expect(mapping.insights.get('ALL_TRANSITIONS')).to.be.ok();
    expect(mapping.insights.get('ALL_ACTIONS')).to.be.ok();
  });

});
<commit_msg>Cleanup tests, #107<commit_after>import { it } from 'ember-mocha';
import runtime from 'ember-insights/runtime';

describe('Runtime #track', function() {

  it('sets tracking params by default', function() {
    var addon   = { configs: [] };
    var mapping = {
      insights: { ALL_TRANSITIONS:true, ALL_ACTIONS:true }
    };

    runtime(addon).configure().track(mapping);

    var settings = addon.configs['default'];
    expect(settings).to.be.ok();
    expect(settings.mappings).to.be.ok();
    expect(typeof mapping.trackerFactory === 'function').to.be.ok();
    expect(typeof mapping.tracker === 'object').to.be.ok();
    expect(settings.mappings.length).to.equal(1);
    expect(mapping.insights.get('ALL_TRANSITIONS')).to.be.ok();
    expect(mapping.insights.get('ALL_ACTIONS')).to.be.ok();
  });

  it('configures custom environment and sets custom tracking params', function() {
    var addon    = { configs: [] };
    var settings = { };
    var mapping  = {
      insights: { ALL_TRANSITIONS:true, ALL_ACTIONS:true }
    };

    runtime(addon).configure('test', settings).track(mapping);

    settings = addon.configs['test'];
    expect(settings).to.be.ok();
    expect(settings.mappings).to.be.ok();
    expect(typeof mapping.trackerFactory === 'function').to.be.ok();
    expect(typeof mapping.tracker === 'object').to.be.ok();
    expect(settings.mappings.length).to.equal(1);
    expect(mapping.insights.get('ALL_TRANSITIONS')).to.be.ok();
    expect(mapping.insights.get('ALL_ACTIONS')).to.be.ok();
  });

});
<|endoftext|>


<commit_before>(function(TextViewer, $, undefined) {

    /*****************************************************
     * Viewer
     */
    function Viewer(options) {
        this.options = options;
        this.api_url = this.options.api_url || (window.location.pathname + 'api/');
        this.panes = {'center': null};

        this.createPanesFromQueryString();

        var self = this;
        $(window).on('popstate', function() {
            self.onQueryStringUpdated();
        });
    }

    Viewer.prototype.onQueryStringUpdated = function() {
        this.createPanesFromQueryString();
    }

    Viewer.prototype.onPaneAddressChanged = function(pane) {
        for (var k in this.panes) {
            if (this.panes.hasOwnProperty(k)) {
                if (this.panes[k] !== pane) {
                    this.panes[k].syncWith(pane.address);
                }
            }
        }
    }

    Viewer.prototype.updateQueryString = function() {
        if (history.pushState) {
            var newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?';
            for (var k in this.panes) {
                if (this.panes.hasOwnProperty(k)) {
                    newurl += k + '=' + this.panes[k].address;
                    newurl += '&';
                }
            }
            window.history.pushState({path:newurl},'',newurl);
        }
    }

    Viewer.prototype.createPanesFromQueryString = function() {
        var self = this;

        // create panes from the query string
        var qs = get_query_string();

        $.each(qs, function(pane_slug, value) {
            if (pane_slug && typeof self.panes !== 'undefined') {
                if (self.panes[pane_slug]) {
                    self.panes[pane_slug].requestAddress(value);
                } else {
                    var pane = self.panes[pane_slug] = new Pane(self, pane_slug, {'query_string': value})
                    if (self.options.on_create_pane) {
                        self.options.on_create_pane(pane);
                    }
                }
            }
        });
    };

    Viewer.prototype.getSyncedWithAddress = function(pane) {
        ret = '';

        for (var k in this.panes) {
            if (this.panes.hasOwnProperty(k)) {
                if (this.panes[k] !== pane) {
                    // TODO: synced with a specific pane rather than first other
                    // one we find.
                    ret = this.panes[k].address;
                    break;
                }
            }
        }

        return ret;
    }

    /*****************************************************
     * Pane
     */
    function Pane(panes, slug, options) {
        this.view = {
            document: {
                slug: '',
                label: '',
            },
            pane_slug: slug,

            view: 'critical',
            views: ['critical'],

            display_settings: [],

            location_type: 'location',
            location_types: ['1', '2'],

            location: 'location',
            locations: ['1', '2'],

            conventions: '',

            chunk: 'chunk',
            errors: [],
        };

        this.panes = panes;
        this.options = options;
        this.address = null;
        this.address_requested = '';
        this.addresses = {};

        this.init();
    }

    Pane.prototype.init = function() {
        // create pane from query string
        // self.options.query_string = "Fr_20125/semi-diplomatic/"
        this.requestAddress(this.options.query_string);
    };

    Pane.prototype.isSynced = function() {
        return (~this.address_requested.indexOf('synced'));
    }

    Pane.prototype.syncWith = function(address) {
        if (this.isSynced()) {
            // todo: sync with that specific address
            this.changeAddressPart('location_type', 'synced');
        }
    }

    Pane.prototype.getAddressParts = function(address) {
        address = address || this.address || '';
        address = address.replace(/\/?(.*)\/?/, '$1');
        var parts = address.split('/');
        ret = {
            'document': parts.shift() || 'default',
            'view': parts.shift() || 'default',
            'location_type': parts.shift() || 'default',
            'location': parts.shift() || 'default',
        }
        return ret;
    }

    Pane.prototype.getAddressFromParts = function(parts) {
        var ret = parts.document + '/' + parts.view + '/' + parts.location_type + '/' + parts.location;
        return ret;
    }

    Pane.prototype.changeAddressPart = function(part_name, value) {
        var parts = this.getAddressParts(this.address_requested);
        parts[part_name] = value;
        return this.requestAddress(this.getAddressFromParts(parts));
    }

    Pane.prototype.requestAddress = function(address) {
        // api call
        if (address === this.address) return;
        var self = this;

        parts = this.getAddressParts(address);

        // 2 get initial chunk
        var url = this.panes.api_url + this.getAddressFromParts(parts);
        var on_success = function(data, jqXHR, textStatus) {
            self.address_requested = address;
            self.onRequestSuccessful(data, jqXHR, textStatus);
        };
        var on_complete = function(jqXHR, textStatus) {
            self.onRequestComplete(textStatus, jqXHR);
        };

        this.view.errors = [];
        var data = null;

        if (parts.location_type == 'synced') {
            data = {
               'sw': this.panes.getSyncedWithAddress(this)
            }
        }

        call_api(url, on_success, on_complete, data);
    }

    Pane.prototype.onRequestSuccessful = function(response, textStatus, jqXHR) {
        if (!response.errors) {
            this.view.chunk = response.chunk;

            this.onReceivedAddress(response.address);
        } else {
            this.view.errors = response.errors;
        }
    }

    Pane.prototype.getPartMeta = function(dict) {
        var ret = {
            'slug': dict.slug,
            'label': dict.label,
        };
        if (dict.label_long) ret.label_long = dict.label_long;
        return ret;
    };

    // set all the views, location types and locations for this document
    // make a request if necessary and set up temporary values
    Pane.prototype.requestAddresses = function() {
        var self = this;
        var parts = this.getAddressParts();

        if (!self.addresses || (parts.document != self.addresses.slug)) {
            // temp values
            self.addresses = {
                'slug': parts.document,
                'label': parts.document,
                'views': [{
                    slug: parts.view,
                    label: parts.view,
                    location_types: [{
                        slug: parts.location_type,
                        label: parts.location_type,
                        locations: [{
                            slug: parts.location,
                            label: parts.location,
                            label_long: parts.location,
                        }]
                    }]
                }]
            };

            if (1) {
                // let's make a new request about this document
                var url_document = this.panes.api_url + parts.document;
                call_api(url_document, function(response, jqXHR, textStatus) {
                    // save doc metadata
                    self.addresses = response;
                    self.renderAddresses();
                }, null, null, false);
            }
        }

        self.renderAddresses();
    }

    // Update the address of the loaded chunk in self.view
    // Based on self.address and self.addresses
    Pane.prototype.renderAddresses = function() {
        var self = this;

        // update self.view
        self.view.document = self.getPartMeta(self.addresses);

        var parts = this.getAddressParts();

        // update the view list
        self.view.views = [];
        self.addresses.views.map(function(aview) {
            self.view.views.push(self.getPartMeta(aview));
        });

        // http://localhost:8000/textviewer/api/Fr20125/
        // Update the lists in self.view
        // by doing simple lookups in the self.views from the address parts.
        this.addresses.views.map(function(aview) {
            // update the view list
            if (aview.slug == parts.view) {
                self.view.view = self.getPartMeta(aview);

                // currently selected view
                // location_types
                self.view.location_types = [];
                self.view.locations = [];

                self.view.conventions = aview.conventions || '';

                // TODO: clone?
                self.view.display_settings = aview.display_settings || [];

                var user_location_type = self.isSynced() ? 'synced': parts.location_type;

                aview.location_types.map(function(location_type) {
                    self.view.location_types.push(self.getPartMeta(location_type));

                    if (location_type.slug == user_location_type) {

                        self.view.location_type = self.getPartMeta(location_type);

                        location_type.locations.map(function(location) {
                            self.view.locations.push(self.getPartMeta(location));
                            if (location.slug == parts.location) {
                                self.view.location = self.getPartMeta(location);
                            }
                        });
                    }
                });
            }
        });
    }

    // update the chunk address in self.view
    Pane.prototype.onReceivedAddress = function(address) {
        // update the loaded address
        // this is the real address of our this.view.chunk
        this.address = address;

        // update address in URL
        this.panes.updateQueryString();

        // request and render the addresses
        this.requestAddresses();

        // broadcast our address to other panes so they can sync with us
        this.panes.onPaneAddressChanged(this);
    }

    Pane.prototype.onRequestComplete = function(textStatus, jqXHR) {
        // TODO
    }

    /*****************************************************
    *     Helpers
    */
    // TODO: move this to another JS

    function get_query_string() {
        var vars = {}, hash;
        var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
        for(var i = 0; i < hashes.length; i++)
        {
            hash = hashes[i].split('=');
            vars[hash[0]] = hash[1];
        }
        return vars;
    }

    function call_api(url, onSuccess, onComplete, requestData, synced) {
        // See http://stackoverflow.com/questions/9956255.
        // This tricks prevents caching of the fragment by the browser.
        // Without this if you move away from the page and then click back
        // it will show only the last Ajax response instead of the full HTML page.
        url = url ? url : '';
        var url_ajax = url + ((url.indexOf('?') === -1) ? '?' : '&') + 'jx=1';

        var getData = {
            url: url_ajax,
            data: requestData,
            async: (synced ? false : true),
            complete: onComplete,
            success: onSuccess
        };
        if (requestData && requestData.method) {
            getData.type = requestData.method;
            delete requestData.method;
        }
        var ret = $.ajax(getData);

        return ret;
    };

    // ===============================================================
    // User Interface
    // ===============================================================

    function on_create_pane(pane) {
        // A new pane was created.
        // We generate the html with Vue.js
        // by cloning the existing template into the div for this pane.
        var $template = $('#text-pane-template').clone();
        $template.removeAttr('id');
        var containerid = '#text-pane-'+pane.view.pane_slug
        var $container = $(containerid);
        $container.html($template);

        pane.view.display_settings_active = {};

        new Vue({
            el: containerid,
            data: pane.view,
            watch: {
                'view.slug': function(val) {
                    pane.changeAddressPart('view', val);
                },
                'location.slug': function(val) {
                    pane.changeAddressPart('location', val);
                },
                'chunk': function(val) {
                    this.$nextTick(function() {
                        // convert the hrefs to the bibliography page
                        $("a[href]").each(function() {
                            var link = $(this).attr('href');
                            // TODO: we shouldn't hard-code this link
                            link = '/k/bibliography/#' + link;
                            $(this).attr('href', link);
                        });
                        // we remove all reveals initialised by foundation
                        // to avoid endless accumulation and duplicates
                        $("[data-reveal!=''][data-reveal]").remove();
                        // we initi foundation on all the new reveals
                        $(this.$el).find('.reveal').each(function() {
                            new Foundation.Reveal($(this));
                        })
                    });
                }
            },
            methods: {
                onClickView: function(view) {
                    pane.changeAddressPart('view', view);
                },
                onClickLocationType: function(location_type) {
                    pane.changeAddressPart('location_type', location_type);
                },
                // TODO: that logic should move to Panel
                // TODO: the list of available display settings should be
                // determined by this class instead of the web api.
                isDisplaySettingActive: function(setting) {
                    return !!this.display_settings_active[setting.slug];
                },
                onClickDisplaySetting: function(setting) {
                    this.$set(this.display_settings_active, setting.slug, !!!(this.display_settings_active[setting.slug]));
                },
                getClassesFromDisplaySettings: function() {
                    var self = this;
                    var ret = '';
                    if (this.display_settings) {
                        this.display_settings.map(function(setting) {
                            if (self.isDisplaySettingActive(setting)) {
                                if (ret) ret += ' ';
                                ret += setting.classes;
                            }
                        });
                    }

                    return ret;
                }
            },
        });
    }

    // customisation

    function on_chunk_loaded(vue) {

    }

    // ===============================================================
    // Initialisation
    // TODO: move this out

    $(function() {
        Vue.directive('f-dropdown', {
            bind: function(el) {
                Vue.nextTick(function () {
                    $(el).addClass('dropdown menu');
                    new Foundation.DropdownMenu($(el));
                })
            },
            unbind: function(el) {
                $(el).foundation.destroy();
            },
        });

        var options = {
            'on_create_pane': on_create_pane,
            'on_chunk_loaded': on_chunk_loaded,
        };
        var viewer = new Viewer(options);
        // viewer.load('Fr_20125/critical/section/588/');

        $('section.main').on('click', 'div[data-corresp]', function() {
            $('section.main div[data-corresp]').removeClass('highlight');
            $(this).addClass('highlight');
        });
    });

}( window.TextViewer = window.TextViewer || {}, jQuery ));
<commit_msg>Bugfix: display of a new chunk would always remove all reveals, even those from other panes. Now only remove reveals from own panes.<commit_after>(function(TextViewer, $, undefined) {

    /*****************************************************
     * Viewer
     */
    function Viewer(options) {
        this.options = options;
        this.api_url = this.options.api_url || (window.location.pathname + 'api/');
        this.panes = {'center': null};

        this.createPanesFromQueryString();

        var self = this;
        $(window).on('popstate', function() {
            self.onQueryStringUpdated();
        });
    }

    Viewer.prototype.onQueryStringUpdated = function() {
        this.createPanesFromQueryString();
    }

    Viewer.prototype.onPaneAddressChanged = function(pane) {
        for (var k in this.panes) {
            if (this.panes.hasOwnProperty(k)) {
                if (this.panes[k] !== pane) {
                    this.panes[k].syncWith(pane.address);
                }
            }
        }
    }

    Viewer.prototype.updateQueryString = function() {
        if (history.pushState) {
            var newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?';
            for (var k in this.panes) {
                if (this.panes.hasOwnProperty(k)) {
                    newurl += k + '=' + this.panes[k].address;
                    newurl += '&';
                }
            }
            window.history.pushState({path:newurl},'',newurl);
        }
    }

    Viewer.prototype.createPanesFromQueryString = function() {
        var self = this;

        // create panes from the query string
        var qs = get_query_string();

        $.each(qs, function(pane_slug, value) {
            if (pane_slug && typeof self.panes !== 'undefined') {
                if (self.panes[pane_slug]) {
                    self.panes[pane_slug].requestAddress(value);
                } else {
                    var pane = self.panes[pane_slug] = new Pane(self, pane_slug, {'query_string': value})
                    if (self.options.on_create_pane) {
                        self.options.on_create_pane(pane);
                    }
                }
            }
        });
    };

    Viewer.prototype.getSyncedWithAddress = function(pane) {
        ret = '';

        for (var k in this.panes) {
            if (this.panes.hasOwnProperty(k)) {
                if (this.panes[k] !== pane) {
                    // TODO: synced with a specific pane rather than first other
                    // one we find.
                    ret = this.panes[k].address;
                    break;
                }
            }
        }

        return ret;
    }

    /*****************************************************
     * Pane
     */
    function Pane(panes, slug, options) {
        this.view = {
            document: {
                slug: '',
                label: '',
            },
            pane_slug: slug,

            view: 'critical',
            views: ['critical'],

            display_settings: [],

            location_type: 'location',
            location_types: ['1', '2'],

            location: 'location',
            locations: ['1', '2'],

            conventions: '',

            chunk: 'chunk',
            errors: [],
        };

        this.panes = panes;
        this.options = options;
        this.address = null;
        this.address_requested = '';
        this.addresses = {};

        this.init();
    }

    Pane.prototype.init = function() {
        // create pane from query string
        // self.options.query_string = "Fr_20125/semi-diplomatic/"
        this.requestAddress(this.options.query_string);
    };

    Pane.prototype.isSynced = function() {
        return (~this.address_requested.indexOf('synced'));
    }

    Pane.prototype.syncWith = function(address) {
        if (this.isSynced()) {
            // todo: sync with that specific address
            this.changeAddressPart('location_type', 'synced');
        }
    }

    Pane.prototype.getAddressParts = function(address) {
        address = address || this.address || '';
        address = address.replace(/\/?(.*)\/?/, '$1');
        var parts = address.split('/');
        ret = {
            'document': parts.shift() || 'default',
            'view': parts.shift() || 'default',
            'location_type': parts.shift() || 'default',
            'location': parts.shift() || 'default',
        }
        return ret;
    }

    Pane.prototype.getAddressFromParts = function(parts) {
        var ret = parts.document + '/' + parts.view + '/' + parts.location_type + '/' + parts.location;
        return ret;
    }

    Pane.prototype.changeAddressPart = function(part_name, value) {
        var parts = this.getAddressParts(this.address_requested);
        parts[part_name] = value;
        return this.requestAddress(this.getAddressFromParts(parts));
    }

    Pane.prototype.requestAddress = function(address) {
        // api call
        if (address === this.address) return;
        var self = this;

        parts = this.getAddressParts(address);

        // 2 get initial chunk
        var url = this.panes.api_url + this.getAddressFromParts(parts);
        var on_success = function(data, jqXHR, textStatus) {
            self.address_requested = address;
            self.onRequestSuccessful(data, jqXHR, textStatus);
        };
        var on_complete = function(jqXHR, textStatus) {
            self.onRequestComplete(textStatus, jqXHR);
        };

        this.view.errors = [];
        var data = null;

        if (parts.location_type == 'synced') {
            data = {
               'sw': this.panes.getSyncedWithAddress(this)
            }
        }

        call_api(url, on_success, on_complete, data);
    }

    Pane.prototype.onRequestSuccessful = function(response, textStatus, jqXHR) {
        if (!response.errors) {
            this.view.chunk = response.chunk;

            this.onReceivedAddress(response.address);
        } else {
            this.view.errors = response.errors;
        }
    }

    Pane.prototype.getPartMeta = function(dict) {
        var ret = {
            'slug': dict.slug,
            'label': dict.label,
        };
        if (dict.label_long) ret.label_long = dict.label_long;
        return ret;
    };

    // set all the views, location types and locations for this document
    // make a request if necessary and set up temporary values
    Pane.prototype.requestAddresses = function() {
        var self = this;
        var parts = this.getAddressParts();

        if (!self.addresses || (parts.document != self.addresses.slug)) {
            // temp values
            self.addresses = {
                'slug': parts.document,
                'label': parts.document,
                'views': [{
                    slug: parts.view,
                    label: parts.view,
                    location_types: [{
                        slug: parts.location_type,
                        label: parts.location_type,
                        locations: [{
                            slug: parts.location,
                            label: parts.location,
                            label_long: parts.location,
                        }]
                    }]
                }]
            };

            if (1) {
                // let's make a new request about this document
                var url_document = this.panes.api_url + parts.document;
                call_api(url_document, function(response, jqXHR, textStatus) {
                    // save doc metadata
                    self.addresses = response;
                    self.renderAddresses();
                }, null, null, false);
            }
        }

        self.renderAddresses();
    }

    // Update the address of the loaded chunk in self.view
    // Based on self.address and self.addresses
    Pane.prototype.renderAddresses = function() {
        var self = this;

        // update self.view
        self.view.document = self.getPartMeta(self.addresses);

        var parts = this.getAddressParts();

        // update the view list
        self.view.views = [];
        self.addresses.views.map(function(aview) {
            self.view.views.push(self.getPartMeta(aview));
        });

        // http://localhost:8000/textviewer/api/Fr20125/
        // Update the lists in self.view
        // by doing simple lookups in the self.views from the address parts.
        this.addresses.views.map(function(aview) {
            // update the view list
            if (aview.slug == parts.view) {
                self.view.view = self.getPartMeta(aview);

                // currently selected view
                // location_types
                self.view.location_types = [];
                self.view.locations = [];

                self.view.conventions = aview.conventions || '';

                // TODO: clone?
                self.view.display_settings = aview.display_settings || [];

                var user_location_type = self.isSynced() ? 'synced': parts.location_type;

                aview.location_types.map(function(location_type) {
                    self.view.location_types.push(self.getPartMeta(location_type));

                    if (location_type.slug == user_location_type) {

                        self.view.location_type = self.getPartMeta(location_type);

                        location_type.locations.map(function(location) {
                            self.view.locations.push(self.getPartMeta(location));
                            if (location.slug == parts.location) {
                                self.view.location = self.getPartMeta(location);
                            }
                        });
                    }
                });
            }
        });
    }

    // update the chunk address in self.view
    Pane.prototype.onReceivedAddress = function(address) {
        // update the loaded address
        // this is the real address of our this.view.chunk
        this.address = address;

        // update address in URL
        this.panes.updateQueryString();

        // request and render the addresses
        this.requestAddresses();

        // broadcast our address to other panes so they can sync with us
        this.panes.onPaneAddressChanged(this);
    }

    Pane.prototype.onRequestComplete = function(textStatus, jqXHR) {
        // TODO
    }

    /*****************************************************
    *     Helpers
    */
    // TODO: move this to another JS

    function get_query_string() {
        var vars = {}, hash;
        var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
        for(var i = 0; i < hashes.length; i++)
        {
            hash = hashes[i].split('=');
            vars[hash[0]] = hash[1];
        }
        return vars;
    }

    function call_api(url, onSuccess, onComplete, requestData, synced) {
        // See http://stackoverflow.com/questions/9956255.
        // This tricks prevents caching of the fragment by the browser.
        // Without this if you move away from the page and then click back
        // it will show only the last Ajax response instead of the full HTML page.
        url = url ? url : '';
        var url_ajax = url + ((url.indexOf('?') === -1) ? '?' : '&') + 'jx=1';

        var getData = {
            url: url_ajax,
            data: requestData,
            async: (synced ? false : true),
            complete: onComplete,
            success: onSuccess
        };
        if (requestData && requestData.method) {
            getData.type = requestData.method;
            delete requestData.method;
        }
        var ret = $.ajax(getData);

        return ret;
    };

    // ===============================================================
    // User Interface
    // ===============================================================

    function on_create_pane(pane) {
        // A new pane was created.
        // We generate the html with Vue.js
        // by cloning the existing template into the div for this pane.
        var $template = $('#text-pane-template').clone();
        $template.removeAttr('id');
        var containerid = '#text-pane-'+pane.view.pane_slug
        var $container = $(containerid);
        $container.html($template);

        pane.view.display_settings_active = {};

        new Vue({
            el: containerid,
            data: pane.view,
            watch: {
                'view.slug': function(val) {
                    pane.changeAddressPart('view', val);
                },
                'location.slug': function(val) {
                    pane.changeAddressPart('location', val);
                },
                'chunk': function(val) {
                    this.$nextTick(function() {
                        // convert the hrefs to the bibliography page
                        $("a[href]").each(function() {
                            var link = $(this).attr('href');
                            // TODO: we shouldn't hard-code this link
                            link = '/k/bibliography/#' + link;
                            $(this).attr('href', link);
                        });
                        // we remove all reveals initialised by foundation
                        // to avoid endless accumulation and duplicates
                        $('.reveal[data-panel="'+containerid+'"]').remove();
                        // we init Foundation on all the new reveals
                        $(this.$el).find('.reveal').each(function() {
                            var $reveal = $(this);
                            $reveal.attr('data-panel', containerid);
                            new Foundation.Reveal($reveal);
                        })
                    });
                }
            },
            methods: {
                onClickView: function(view) {
                    pane.changeAddressPart('view', view);
                },
                onClickLocationType: function(location_type) {
                    pane.changeAddressPart('location_type', location_type);
                },
                // TODO: that logic should move to Panel
                // TODO: the list of available display settings should be
                // determined by this class instead of the web api.
                isDisplaySettingActive: function(setting) {
                    return !!this.display_settings_active[setting.slug];
                },
                onClickDisplaySetting: function(setting) {
                    this.$set(this.display_settings_active, setting.slug, !!!(this.display_settings_active[setting.slug]));
                },
                getClassesFromDisplaySettings: function() {
                    var self = this;
                    var ret = '';
                    if (this.display_settings) {
                        this.display_settings.map(function(setting) {
                            if (self.isDisplaySettingActive(setting)) {
                                if (ret) ret += ' ';
                                ret += setting.classes;
                            }
                        });
                    }

                    return ret;
                }
            },
        });
    }

    // customisation

    function on_chunk_loaded(vue) {

    }

    // ===============================================================
    // Initialisation
    // TODO: move this out

    $(function() {
        Vue.directive('f-dropdown', {
            bind: function(el) {
                Vue.nextTick(function () {
                    $(el).addClass('dropdown menu');
                    new Foundation.DropdownMenu($(el));
                })
            },
            unbind: function(el) {
                $(el).foundation.destroy();
            },
        });

        var options = {
            'on_create_pane': on_create_pane,
            'on_chunk_loaded': on_chunk_loaded,
        };
        var viewer = new Viewer(options);
        // viewer.load('Fr_20125/critical/section/588/');

        $('section.main').on('click', 'div[data-corresp]', function() {
            $('section.main div[data-corresp]').removeClass('highlight');
            $(this).addClass('highlight');
        });
    });

}( window.TextViewer = window.TextViewer || {}, jQuery ));
<|endoftext|>


<commit_before>'use strict';

module.exports = function (emitter, context, types) {
  types.forEach(function through (type) {
    emitter.on(type, raise);
  });

  function raise () {
    var ctx = context;
    console.log(arguments);
    while (ctx) {
      ctx.emit.apply(emitter, arguments);
      ctx = ctx.parent;
    }
  }
};
<commit_msg>fixed for good<commit_after>'use strict';

module.exports = function (emitter, context, types) {
  types.forEach(function through (type) {
    emitter.on(type, raise);
  });

  function raise () {
    var args = Array.prototype.slice.call(arguments);
    var all = [type].concat(args);
    var ctx = context;
    while (ctx) {
      ctx.emit.apply(emitter, all);
      ctx = ctx.parent;
    }
  }
};
<|endoftext|>


<commit_before>/* eslint-env jest */
/* eslint-disable react/no-multi-comp */
import chai, { expect } from 'chai';
import dirtyChai from 'dirty-chai';
import { mount } from 'enzyme';
import uniqueId from 'lodash.uniqueid';
import React from 'react';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';

chai.use(dirtyChai);
chai.use(sinonChai);

function getUniqueState() {
	return { [uniqueId('stateVarName')]: uniqueId('stateVarValue') };
}

function getUniqueProps() {
	return { [uniqueId('propName')]: uniqueId('propValue') };
}

// Skip these tests if the current version of React does not define PureComponent.
const descriptor = React.PureComponent ? describe : describe.skip;

descriptor('PureComponent extension', () => {
	const { default: PureComponent } = require('./pureComponent'); // eslint-disable-line global-require

	class TestComponent extends PureComponent {
		constructor(props) {
			super(props);
			this.state = getUniqueState();
		}

		render() {
			return null;
		}
	}

	const callbackWill = sinon.spy(TestComponent.prototype, 'componentWillMountOrReceiveProps');
	const callbackDid = sinon.spy(TestComponent.prototype, 'componentDidMountOrUpdate');
	const callbackRender = sinon.spy(TestComponent.prototype, 'render');

	afterEach(() => {
		callbackWill.reset();
		callbackDid.reset();
		callbackRender.reset();
	});

	describe('componentWillMountOrReceiveProps()', () => {
		it('runs once on mount', () => {
			mount(<TestComponent />);
			expect(callbackWill).to.have.been.calledOnce();
		});

		it('runs on mount with first parameter of component props', () => {
			const component = mount(<TestComponent {...getUniqueProps()} />);
			expect(callbackWill.firstCall).to.have.been.calledWith(component.props());
		});

		it('runs on mount with "this" context of component', () => {
			const component = mount(<TestComponent />);
			expect(callbackWill.firstCall).to.have.been.calledOn(component.getNode());
		});

		it('runs on mount before render()', () => {
			mount(<TestComponent />);
			expect(callbackWill).to.have.been.calledBefore(callbackRender);
		});

		it('runs on props update', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackWill).to.have.been.calledTwice();
		});

		it('runs on props update when no props change', () => {
			const component = mount(<TestComponent {...getUniqueProps()} />);
			component.setProps(component.props());
			expect(callbackWill).to.have.been.calledTwice();
		});

		it('runs on props update with first parameter of component props', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackWill.secondCall).to.have.been.calledWith(component.props());
		});

		it('runs on props update with "this" context of component', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackWill.secondCall).to.have.been.calledOn(component.getNode());
		});

		it('runs on props update before render()', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackWill.secondCall).to.have.been.calledBefore(callbackRender.secondCall);
		});

		it('does not run on state update', () => {
			const component = mount(<TestComponent />);
			component.setState(getUniqueState());
			expect(callbackWill).to.have.been.calledOnce();
		});
	});

	describe('componentDidMountOrUpdate()', () => {
		it('runs once when mounted', () => {
			mount(<TestComponent />);
			expect(callbackDid).to.have.been.calledOnce();
		});

		it('runs on mount with first parameter of component props', () => {
			const component = mount(<TestComponent {...getUniqueProps()} />);
			expect(callbackDid.firstCall).to.have.been.calledWith(component.props());
		});

		it('runs on mount with second parameter of component state', () => {
			const component = mount(<TestComponent />);
			expect(callbackDid.firstCall).to.have.been.calledWith(sinon.match.any, component.state());
		});

		it('runs on mount with "this" context of component', () => {
			const component = mount(<TestComponent />);
			expect(callbackDid.firstCall).to.have.been.calledOn(component.getNode());
		});

		it('runs after render()', () => {
			mount(<TestComponent />);
			expect(callbackDid).to.have.been.calledAfter(callbackRender);
		});

		it('runs on props update', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackDid).to.have.been.calledTwice();
		});

		it('runs on props update with first parameter of previous component props', () => {
			const component = mount(<TestComponent {...getUniqueProps()} />);
			const initialProps = component.props();
			component.setProps(getUniqueProps());
			expect(callbackDid.secondCall).to.have.been.calledWith(initialProps);
		});

		it('runs on props update with second parameter of previous component state', () => {
			const component = mount(<TestComponent />);
			const initialState = component.state();
			component.setProps(getUniqueProps());
			expect(callbackDid.secondCall).to.have.been.calledWith(sinon.match.any, initialState);
		});

		it('runs on props update with "this" context of component', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackDid.secondCall).to.have.been.calledOn(component.getNode());
		});

		it('runs on state update', () => {
			const component = mount(<TestComponent />);
			component.setState(getUniqueState());
			expect(callbackDid).to.have.been.calledTwice();
		});

		it('runs on state update with first parameter of previous component props', () => {
			const component = mount(<TestComponent {...getUniqueProps()} />);
			const initialProps = component.props();
			component.setState(getUniqueState());
			expect(callbackDid.secondCall).to.have.been.calledWith(initialProps);
		});

		it('runs on state update with second parameter of previous component state', () => {
			const component = mount(<TestComponent {...getUniqueProps()} />);
			const initialState = component.state();
			component.setState(getUniqueState());
			expect(callbackDid.secondCall).to.have.been.calledWith(sinon.match.any, initialState);
		});

		it('runs on state update with "this" context of component', () => {
			const component = mount(<TestComponent />);
			component.setState(getUniqueState());
			expect(callbackDid.secondCall).to.have.been.calledOn(component.getNode());
		});

		it('runs on props update before render()', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackDid.secondCall).to.have.been.calledAfter(callbackRender.secondCall);
		});
	});
});

descriptor('PureComponent extension with overrides calling super()', () => {
	const { default: PureComponent } = require('./pureComponent'); // eslint-disable-line global-require

	const userComponentWillMountBefore = sinon.spy();
	const userComponentWillMountAfter = sinon.spy();
	const userComponentDidMountBefore = sinon.spy();
	const userComponentDidMountAfter = sinon.spy();
	const userComponentWillReceivePropsBefore = sinon.spy();
	const userComponentWillReceivePropsAfter = sinon.spy();
	const userComponentDidUpdateBefore = sinon.spy();
	const userComponentDidUpdateAfter = sinon.spy();

	class TestComponentWithSuper extends PureComponent {
		componentWillMount() {
			userComponentWillMountBefore();
			super.componentWillMount();
			userComponentWillMountAfter();
		}

		componentDidMount() {
			userComponentDidMountBefore();
			super.componentDidMount();
			userComponentDidMountAfter();
		}

		componentWillReceiveProps() {
			userComponentWillReceivePropsBefore();
			super.componentWillReceiveProps();
			userComponentWillReceivePropsAfter();
		}

		componentDidUpdate() {
			userComponentDidUpdateBefore();
			super.componentDidUpdate();
			userComponentDidUpdateAfter();
		}

		render() {
			return null;
		}
	}

	const callbackWill = sinon.spy(TestComponentWithSuper.prototype, 'componentWillMountOrReceiveProps');
	const callbackDid = sinon.spy(TestComponentWithSuper.prototype, 'componentDidMountOrUpdate');

	afterEach(() => {
		const allSpies = [
			userComponentWillMountBefore, userComponentWillMountAfter,
			userComponentDidMountBefore, userComponentDidMountAfter,
			userComponentWillReceivePropsBefore, userComponentWillReceivePropsAfter,
			userComponentDidUpdateBefore, userComponentDidUpdateAfter,
			callbackWill, callbackDid,
		];
		allSpies.forEach(spy => spy.reset());
	});

	describe('componentWillMountOrReceiveProps()', () => {
		it('runs once on mount', () => {
			mount(<TestComponentWithSuper />);
			expect(callbackWill).to.have.been.calledOnce();
		});

		it('runs user code in override on mount', () => {
			mount(<TestComponentWithSuper />);
			sinon.assert.callOrder(
				userComponentWillMountBefore, callbackWill, userComponentWillMountAfter,
			);
		});

		it('runs on props update', () => {
			const component = mount(<TestComponentWithSuper />);
			component.setProps(getUniqueProps());
			expect(callbackWill).to.have.been.calledTwice();
		});

		it('runs user code in override on props update', () => {
			const component = mount(<TestComponentWithSuper />);
			component.setProps(getUniqueProps());
			sinon.assert.callOrder(
				userComponentWillReceivePropsBefore, callbackWill, userComponentWillReceivePropsAfter,
			);
		});

		it('does not run on state update', () => {
			const component = mount(<TestComponentWithSuper />);
			component.setState(getUniqueState());
			expect(callbackWill).to.have.been.calledOnce();
		});
	});

	describe('componentDidMountOrUpdate()', () => {
		it('runs once when mounted', () => {
			mount(<TestComponentWithSuper />);
			expect(callbackDid).to.have.been.calledOnce();
		});

		it('runs user code in override on mount', () => {
			mount(<TestComponentWithSuper />);
			sinon.assert.callOrder(
				userComponentDidMountBefore, callbackDid, userComponentDidMountAfter,
			);
		});

		it('runs on props update', () => {
			const component = mount(<TestComponentWithSuper />);
			component.setProps(getUniqueProps());
			expect(callbackDid).to.have.been.calledTwice();
		});

		it('runs user code in override on props update', () => {
			const component = mount(<TestComponentWithSuper {...getUniqueProps()} />);
			component.setProps(getUniqueProps());
			sinon.assert.callOrder(
				userComponentDidUpdateBefore, callbackDid, userComponentDidUpdateAfter,
			);
		});

		it('runs on state update', () => {
			const component = mount(<TestComponentWithSuper />);
			component.setState(getUniqueState());
			expect(callbackDid).to.have.been.calledTwice();
		});

		it('runs user code in override on state update', () => {
			const component = mount(<TestComponentWithSuper />);
			component.setState(getUniqueState());
			sinon.assert.callOrder(
				userComponentDidUpdateBefore, callbackDid, userComponentDidUpdateAfter,
			);
		});
	});
});

descriptor('Component extension with overrides not calling super()', () => {
	const { default: PureComponent } = require('./pureComponent'); // eslint-disable-line global-require

	class TestComponentWithoutSuper extends PureComponent {
		componentWillMount() {}
		componentDidMount() {}
		componentWillReceiveProps() {}
		componentDidUpdate() {}

		render() {
			return null;
		}
	}

	const callbackWill = sinon.spy(TestComponentWithoutSuper.prototype, 'componentWillMountOrReceiveProps');
	const callbackDid = sinon.spy(TestComponentWithoutSuper.prototype, 'componentDidMountOrUpdate');

	afterEach(() => {
		callbackWill.reset();
		callbackDid.reset();
	});

	describe('componentWillMountOrReceiveProps()', () => {
		it('does not run on mount', () => {
			mount(<TestComponentWithoutSuper />);
			expect(callbackWill).not.to.have.been.called();
		});

		it('does not run on props update', () => {
			const component = mount(<TestComponentWithoutSuper />);
			component.setProps(getUniqueProps());
			expect(callbackWill).not.to.have.been.called();
		});

		it('does not run on state update', () => {
			const component = mount(<TestComponentWithoutSuper />);
			component.setState(getUniqueState());
			expect(callbackWill).not.to.have.been.called();
		});
	});

	describe('componentDidMountOrUpdate()', () => {
		it('does not run when mounted', () => {
			mount(<TestComponentWithoutSuper />);
			expect(callbackDid).not.to.have.been.called();
		});

		it('does not run on props update', () => {
			const component = mount(<TestComponentWithoutSuper />);
			component.setProps(getUniqueProps());
			expect(callbackDid).not.to.have.been.called();
		});

		it('does not run on state update', () => {
			const component = mount(<TestComponentWithoutSuper />);
			component.setState(getUniqueState());
			expect(callbackDid).not.to.have.been.called();
		});
	});
});
<commit_msg>Use package entry point for require()<commit_after>/* eslint-env jest */
/* eslint-disable react/no-multi-comp */
import chai, { expect } from 'chai';
import dirtyChai from 'dirty-chai';
import { mount } from 'enzyme';
import uniqueId from 'lodash.uniqueid';
import React from 'react';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';

chai.use(dirtyChai);
chai.use(sinonChai);

function getUniqueState() {
	return { [uniqueId('stateVarName')]: uniqueId('stateVarValue') };
}

function getUniqueProps() {
	return { [uniqueId('propName')]: uniqueId('propValue') };
}

// Skip these tests if the current version of React does not define PureComponent.
const descriptor = React.PureComponent ? describe : describe.skip;

descriptor('PureComponent extension', () => {
	const { PureComponent } = require('./'); // eslint-disable-line global-require

	class TestComponent extends PureComponent {
		constructor(props) {
			super(props);
			this.state = getUniqueState();
		}

		render() {
			return null;
		}
	}

	const callbackWill = sinon.spy(TestComponent.prototype, 'componentWillMountOrReceiveProps');
	const callbackDid = sinon.spy(TestComponent.prototype, 'componentDidMountOrUpdate');
	const callbackRender = sinon.spy(TestComponent.prototype, 'render');

	afterEach(() => {
		callbackWill.reset();
		callbackDid.reset();
		callbackRender.reset();
	});

	describe('componentWillMountOrReceiveProps()', () => {
		it('runs once on mount', () => {
			mount(<TestComponent />);
			expect(callbackWill).to.have.been.calledOnce();
		});

		it('runs on mount with first parameter of component props', () => {
			const component = mount(<TestComponent {...getUniqueProps()} />);
			expect(callbackWill.firstCall).to.have.been.calledWith(component.props());
		});

		it('runs on mount with "this" context of component', () => {
			const component = mount(<TestComponent />);
			expect(callbackWill.firstCall).to.have.been.calledOn(component.getNode());
		});

		it('runs on mount before render()', () => {
			mount(<TestComponent />);
			expect(callbackWill).to.have.been.calledBefore(callbackRender);
		});

		it('runs on props update', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackWill).to.have.been.calledTwice();
		});

		it('runs on props update when no props change', () => {
			const component = mount(<TestComponent {...getUniqueProps()} />);
			component.setProps(component.props());
			expect(callbackWill).to.have.been.calledTwice();
		});

		it('runs on props update with first parameter of component props', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackWill.secondCall).to.have.been.calledWith(component.props());
		});

		it('runs on props update with "this" context of component', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackWill.secondCall).to.have.been.calledOn(component.getNode());
		});

		it('runs on props update before render()', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackWill.secondCall).to.have.been.calledBefore(callbackRender.secondCall);
		});

		it('does not run on state update', () => {
			const component = mount(<TestComponent />);
			component.setState(getUniqueState());
			expect(callbackWill).to.have.been.calledOnce();
		});
	});

	describe('componentDidMountOrUpdate()', () => {
		it('runs once when mounted', () => {
			mount(<TestComponent />);
			expect(callbackDid).to.have.been.calledOnce();
		});

		it('runs on mount with first parameter of component props', () => {
			const component = mount(<TestComponent {...getUniqueProps()} />);
			expect(callbackDid.firstCall).to.have.been.calledWith(component.props());
		});

		it('runs on mount with second parameter of component state', () => {
			const component = mount(<TestComponent />);
			expect(callbackDid.firstCall).to.have.been.calledWith(sinon.match.any, component.state());
		});

		it('runs on mount with "this" context of component', () => {
			const component = mount(<TestComponent />);
			expect(callbackDid.firstCall).to.have.been.calledOn(component.getNode());
		});

		it('runs after render()', () => {
			mount(<TestComponent />);
			expect(callbackDid).to.have.been.calledAfter(callbackRender);
		});

		it('runs on props update', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackDid).to.have.been.calledTwice();
		});

		it('runs on props update with first parameter of previous component props', () => {
			const component = mount(<TestComponent {...getUniqueProps()} />);
			const initialProps = component.props();
			component.setProps(getUniqueProps());
			expect(callbackDid.secondCall).to.have.been.calledWith(initialProps);
		});

		it('runs on props update with second parameter of previous component state', () => {
			const component = mount(<TestComponent />);
			const initialState = component.state();
			component.setProps(getUniqueProps());
			expect(callbackDid.secondCall).to.have.been.calledWith(sinon.match.any, initialState);
		});

		it('runs on props update with "this" context of component', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackDid.secondCall).to.have.been.calledOn(component.getNode());
		});

		it('runs on state update', () => {
			const component = mount(<TestComponent />);
			component.setState(getUniqueState());
			expect(callbackDid).to.have.been.calledTwice();
		});

		it('runs on state update with first parameter of previous component props', () => {
			const component = mount(<TestComponent {...getUniqueProps()} />);
			const initialProps = component.props();
			component.setState(getUniqueState());
			expect(callbackDid.secondCall).to.have.been.calledWith(initialProps);
		});

		it('runs on state update with second parameter of previous component state', () => {
			const component = mount(<TestComponent {...getUniqueProps()} />);
			const initialState = component.state();
			component.setState(getUniqueState());
			expect(callbackDid.secondCall).to.have.been.calledWith(sinon.match.any, initialState);
		});

		it('runs on state update with "this" context of component', () => {
			const component = mount(<TestComponent />);
			component.setState(getUniqueState());
			expect(callbackDid.secondCall).to.have.been.calledOn(component.getNode());
		});

		it('runs on props update before render()', () => {
			const component = mount(<TestComponent />);
			component.setProps(getUniqueProps());
			expect(callbackDid.secondCall).to.have.been.calledAfter(callbackRender.secondCall);
		});
	});
});

descriptor('PureComponent extension with overrides calling super()', () => {
	const { PureComponent } = require('./'); // eslint-disable-line global-require

	const userComponentWillMountBefore = sinon.spy();
	const userComponentWillMountAfter = sinon.spy();
	const userComponentDidMountBefore = sinon.spy();
	const userComponentDidMountAfter = sinon.spy();
	const userComponentWillReceivePropsBefore = sinon.spy();
	const userComponentWillReceivePropsAfter = sinon.spy();
	const userComponentDidUpdateBefore = sinon.spy();
	const userComponentDidUpdateAfter = sinon.spy();

	class TestComponentWithSuper extends PureComponent {
		componentWillMount() {
			userComponentWillMountBefore();
			super.componentWillMount();
			userComponentWillMountAfter();
		}

		componentDidMount() {
			userComponentDidMountBefore();
			super.componentDidMount();
			userComponentDidMountAfter();
		}

		componentWillReceiveProps() {
			userComponentWillReceivePropsBefore();
			super.componentWillReceiveProps();
			userComponentWillReceivePropsAfter();
		}

		componentDidUpdate() {
			userComponentDidUpdateBefore();
			super.componentDidUpdate();
			userComponentDidUpdateAfter();
		}

		render() {
			return null;
		}
	}

	const callbackWill = sinon.spy(TestComponentWithSuper.prototype, 'componentWillMountOrReceiveProps');
	const callbackDid = sinon.spy(TestComponentWithSuper.prototype, 'componentDidMountOrUpdate');

	afterEach(() => {
		const allSpies = [
			userComponentWillMountBefore, userComponentWillMountAfter,
			userComponentDidMountBefore, userComponentDidMountAfter,
			userComponentWillReceivePropsBefore, userComponentWillReceivePropsAfter,
			userComponentDidUpdateBefore, userComponentDidUpdateAfter,
			callbackWill, callbackDid,
		];
		allSpies.forEach(spy => spy.reset());
	});

	describe('componentWillMountOrReceiveProps()', () => {
		it('runs once on mount', () => {
			mount(<TestComponentWithSuper />);
			expect(callbackWill).to.have.been.calledOnce();
		});

		it('runs user code in override on mount', () => {
			mount(<TestComponentWithSuper />);
			sinon.assert.callOrder(
				userComponentWillMountBefore, callbackWill, userComponentWillMountAfter,
			);
		});

		it('runs on props update', () => {
			const component = mount(<TestComponentWithSuper />);
			component.setProps(getUniqueProps());
			expect(callbackWill).to.have.been.calledTwice();
		});

		it('runs user code in override on props update', () => {
			const component = mount(<TestComponentWithSuper />);
			component.setProps(getUniqueProps());
			sinon.assert.callOrder(
				userComponentWillReceivePropsBefore, callbackWill, userComponentWillReceivePropsAfter,
			);
		});

		it('does not run on state update', () => {
			const component = mount(<TestComponentWithSuper />);
			component.setState(getUniqueState());
			expect(callbackWill).to.have.been.calledOnce();
		});
	});

	describe('componentDidMountOrUpdate()', () => {
		it('runs once when mounted', () => {
			mount(<TestComponentWithSuper />);
			expect(callbackDid).to.have.been.calledOnce();
		});

		it('runs user code in override on mount', () => {
			mount(<TestComponentWithSuper />);
			sinon.assert.callOrder(
				userComponentDidMountBefore, callbackDid, userComponentDidMountAfter,
			);
		});

		it('runs on props update', () => {
			const component = mount(<TestComponentWithSuper />);
			component.setProps(getUniqueProps());
			expect(callbackDid).to.have.been.calledTwice();
		});

		it('runs user code in override on props update', () => {
			const component = mount(<TestComponentWithSuper {...getUniqueProps()} />);
			component.setProps(getUniqueProps());
			sinon.assert.callOrder(
				userComponentDidUpdateBefore, callbackDid, userComponentDidUpdateAfter,
			);
		});

		it('runs on state update', () => {
			const component = mount(<TestComponentWithSuper />);
			component.setState(getUniqueState());
			expect(callbackDid).to.have.been.calledTwice();
		});

		it('runs user code in override on state update', () => {
			const component = mount(<TestComponentWithSuper />);
			component.setState(getUniqueState());
			sinon.assert.callOrder(
				userComponentDidUpdateBefore, callbackDid, userComponentDidUpdateAfter,
			);
		});
	});
});

descriptor('Component extension with overrides not calling super()', () => {
	const { PureComponent } = require('./'); // eslint-disable-line global-require

	class TestComponentWithoutSuper extends PureComponent {
		componentWillMount() {}
		componentDidMount() {}
		componentWillReceiveProps() {}
		componentDidUpdate() {}

		render() {
			return null;
		}
	}

	const callbackWill = sinon.spy(TestComponentWithoutSuper.prototype, 'componentWillMountOrReceiveProps');
	const callbackDid = sinon.spy(TestComponentWithoutSuper.prototype, 'componentDidMountOrUpdate');

	afterEach(() => {
		callbackWill.reset();
		callbackDid.reset();
	});

	describe('componentWillMountOrReceiveProps()', () => {
		it('does not run on mount', () => {
			mount(<TestComponentWithoutSuper />);
			expect(callbackWill).not.to.have.been.called();
		});

		it('does not run on props update', () => {
			const component = mount(<TestComponentWithoutSuper />);
			component.setProps(getUniqueProps());
			expect(callbackWill).not.to.have.been.called();
		});

		it('does not run on state update', () => {
			const component = mount(<TestComponentWithoutSuper />);
			component.setState(getUniqueState());
			expect(callbackWill).not.to.have.been.called();
		});
	});

	describe('componentDidMountOrUpdate()', () => {
		it('does not run when mounted', () => {
			mount(<TestComponentWithoutSuper />);
			expect(callbackDid).not.to.have.been.called();
		});

		it('does not run on props update', () => {
			const component = mount(<TestComponentWithoutSuper />);
			component.setProps(getUniqueProps());
			expect(callbackDid).not.to.have.been.called();
		});

		it('does not run on state update', () => {
			const component = mount(<TestComponentWithoutSuper />);
			component.setState(getUniqueState());
			expect(callbackDid).not.to.have.been.called();
		});
	});
});
<|endoftext|>