#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++

require 'spec_helper'

describe PaginationHelper, type: :helper do
  let(:paginator) do
    # creating a mock pagination object
    # this one is then identical (from the interface) to a active record
    paginator = WillPaginate::Collection.create(current_page, per_page) do |pager|
      result = Array.new(pager.per_page) { |i| i }

      pager.replace(result)

      unless pager.total_entries
        pager.total_entries = total_entries
      end
    end

    paginator
  end

  describe '#pagination_links_full' do
    let(:per_page) { 10 }
    let(:total_entries) { 55 }
    let(:offset) { 1 }
    let(:current_page) { 1 }
    let(:pagination) { helper.pagination_links_full(paginator) }

    before do
      # setup the helpers environment as if the helper is rendered after having called
      # /work_packages
      url_options = helper.url_options

      allow(helper)
        .to receive(:params)
        .and_return(ActionController::Parameters.new(controller: 'work_packages', action: 'index'))
      allow(helper)
        .to receive(:url_options)
        .and_return(url_options.merge(controller: 'work_packages', action: 'index'))
    end

    it "should be inside a 'pagination' div" do
      expect(pagination).to have_selector('div.pagination')
    end

    it 'should have a next_page reference' do
      expect(pagination).to have_selector('.pagination--item.-next')
    end

    it 'should not have a previous_page reference' do
      expect(pagination).not_to have_selector('.pagination--item.-prev')
    end

    it 'should have links to every page except the current one' do
      (1..(total_entries / per_page)).each do |i|
        next if i == current_page

        expect(pagination).to have_selector("a[href='#{work_packages_path(page: i)}']",
                                            text: Regexp.new("^#{i}$"))
      end
    end

    it 'should not have a link to the current page' do
      expect(pagination).not_to have_selector('a', text: Regexp.new("^#{current_page}$"))
    end

    it 'should have an element for the curren page' do
      expect(pagination).to have_selector('.pagination--item.-current',
                                          text: Regexp.new("^#{current_page}$"))
    end

    it 'should show the range of the entries displayed' do
      range = "(#{(current_page * per_page) - per_page + 1} - " +
              "#{current_page * per_page}/#{total_entries})"
      expect(pagination).to have_selector('.pagination--range', text: range)
    end

    it 'should have different urls if the params are specified as options' do
      params = { controller: 'work_packages', action: 'index' }

      pagination = helper.pagination_links_full(paginator, params: params)

      (1..(total_entries / per_page)).each do |i|
        next if i == current_page

        href = work_packages_path({ page: i }.merge(params))

        expect(pagination).to have_selector("a[href='#{href}']", text: Regexp.new("^#{i}$"))
      end
    end

    it 'should show the available pre page options' do
      allow(Setting)
        .to receive(:per_page_options)
        .and_return("#{per_page},#{per_page * 10}")

      expect(pagination).to have_selector('.pagination--options')

      expect(pagination).to have_selector('.pagination--options .-current', text: per_page)

      path = work_packages_path(page: current_page, per_page: Setting.per_page_options_array.last)
      expect(pagination).to have_selector(".pagination--options a[href='#{path}']")
    end

    describe 'WHEN the first page is the current' do
      let(:current_page) { 1 }

      it 'should deactivate the previous page link' do
        expect(pagination).not_to have_selector('.pagination--item.-prev')
      end

      it 'should have a link to the next page' do
        path = work_packages_path(page: current_page + 1)
        expect(pagination).to have_selector(".pagination--item.-next a[href='#{path}']")
      end
    end

    describe 'WHEN the last page is the current' do
      let(:current_page) { total_entries / per_page + 1 }

      it 'should deactivate the next page link' do
        expect(pagination).not_to have_selector('.pagination--item.-next')
      end

      it 'should have a link to the previous page' do
        path = work_packages_path(page: current_page - 1)
        expect(pagination).to have_selector(".pagination--item.-prev a[href='#{path}']")
      end
    end

    describe 'WHEN the paginated object is empty' do
      let(:total_entries) { 0 }

      it 'should show no pages' do
        expect(pagination).not_to have_selector('.pagination--items .pagination--item')
      end

      it 'should show no pagination' do
        expect(pagination).not_to have_selector('.pagination')
      end
    end
  end

  describe '#page_param' do
    it 'should return page if provided and sensible' do
      page = 2

      expect(page_param(page: page)).to eq(page)
    end

    it 'should return default page 1 if page provided but useless' do
      page = 0

      expect(page_param(page: page)).to eq(1)
    end

    context 'with multiples per_page',
            with_settings: { per_page_options: '5,10,15' } do
      it 'should calculate page from offset and limit if page is not provided' do
        # need to change settings as only multiples of per_page
        # are allowed for limit
        offset = 55
        limit = 10

        expect(page_param(offset: offset, limit: limit)).to eq(6)
      end
    end

    it 'should ignore offset and limit if page is provided' do
      offset = 55
      limit = 10
      page = 7

      expect(page_param(offset: offset, limit: limit, page: page)).to eq(page)
    end

    context 'faulty settings',
            with_settings: { per_page_options: '-1,2,3' } do
      it 'should not break if limit is bogus (also faulty settings)' do
        offset = 55
        limit = 'lorem'

        expect(page_param(offset: offset, limit: limit)).to eq(28)
      end
    end

    it 'should return 1 if nothing is provided' do
      expect(page_param({})).to eq(1)
    end
  end

  describe '#per_page_param',
           with_settings: { per_page_options: '1,2,3' } do
    it 'should return per_page if provided and one of the values stored in the settings' do
      per_page = 2

      expect(per_page_param(per_page: per_page)).to eq(per_page)
    end

    it 'should return per_page if provided and store it in the session' do
      session[:per_page] = 3
      per_page = 2

      expect(per_page_param(per_page: per_page)).to eq(per_page)
      expect(session[:per_page]).to eq(2)
    end

    it 'should take the smallest value stored in the settings ' +
       'if provided per_page param is not one of the configured' do
      per_page = 4

      expect(per_page_param(per_page: per_page)).to eq(1)
    end

    it 'prefers the value stored in the session if it is valid according to the settings' do
      session[:per_page] = 2

      expect(per_page_param(per_page: 3)).to eq(session[:per_page])
    end

    it 'ignores the value stored in the session if it is not valid according to the settings' do
      session[:per_page] = 4

      expect(per_page_param(per_page: 3)).to eq(3)
    end

    it 'uses limit synonymously to per_page' do
      limit = 2

      expect(per_page_param(limit: limit)).to eq(limit)
    end

    it 'prefers per_page over limit' do
      limit = 2
      per_page = 3

      expect(per_page_param(limit: limit, per_page: per_page)).to eq(per_page)
    end

    it 'stores the value in the session' do
      limit = 2

      per_page_param(limit: limit)

      expect(session[:per_page]).to eq(limit)
    end
  end
end
