require 'rails_helper'

feature 'Merge request approvals', :js do
  let(:user) { create(:user) }
  let(:project) { create(:project, :public, :repository, approvals_before_merge: 1) }

  context 'when editing an MR with a different author' do
    let(:author) { create(:user) }
    let(:merge_request) { create(:merge_request, author: author, source_project: project) }

    before do
      project.add_developer(user)
      project.add_developer(author)

      sign_in(user)
      visit edit_project_merge_request_path(project, merge_request)

      find('#s2id_merge_request_approver_ids .select2-input').click
    end

    it 'does not allow setting the author as an approver' do
      expect(find('.select2-results')).not_to have_content(author.name)
    end

    it 'allows setting the current user as an approver' do
      expect(find('.select2-results')).to have_content(user.name)
    end
  end

  context 'when creating an MR from a fork' do
    let(:other_user) { create(:user) }
    let(:non_member) { create(:user) }
    let(:forked_project) { create(:project, :public, :repository, creator: user) }

    before do
      create(:forked_project_link, forked_to_project: forked_project, forked_from_project: project)

      forked_project.add_developer(user)
      project.add_developer(user)
      project.add_developer(other_user)

      sign_in(user)
      visit project_new_merge_request_path(forked_project, merge_request: { target_branch: 'master', source_branch: 'feature' })

      find('#s2id_merge_request_approver_ids .select2-input').click
    end

    it 'allows setting other users as approvers' do
      expect(find('.select2-results')).to have_content(other_user.name)
    end

    it 'does not allow setting the current user as an approver' do
      expect(find('.select2-results')).not_to have_content(user.name)
    end

    it 'filters non members from approvers list' do
      expect(find('.select2-results')).not_to have_content(non_member.name)
    end
  end

  context "Group approvers" do
    context 'when creating an MR' do
      let(:other_user) { create(:user) }

      before do
        project.add_developer(user)
        project.add_developer(other_user)

        sign_in(user)
      end

      it 'allows setting groups as approvers' do
        group = create :group
        group.add_developer(other_user)

        visit project_new_merge_request_path(project, merge_request: { target_branch: 'master', source_branch: 'feature' })
        find('#s2id_merge_request_approver_group_ids .select2-input').click

        wait_for_requests

        expect(find('.select2-results')).to have_content(group.name)

        find('.select2-results').click
        click_on("Submit merge request")

        find('.approvals-components')
        expect(page).to have_content("Requires 1 more approval")
        expect(page).to have_selector(".approvals-required-text a[title='#{other_user.name}']")
      end

      it 'allows delete approvers group when it is set in project' do
        approver = create :user
        group = create :group
        group.add_developer(other_user)
        create :approver_group, group: group, target: project
        create :approver, user: approver, target: project

        visit project_new_merge_request_path(project, merge_request: { target_branch: 'master', source_branch: 'feature' })

        within('.approver-list li.approver-group') do
          click_on "Remove"
        end

        expect(page).to have_css('.approver-list li', count: 1)

        click_on("Submit merge request")

        wait_for_requests

        expect(page).not_to have_selector(".approvals-required-text a[title='#{other_user.name}']")
        expect(page).to have_selector(".approvals-required-text a[title='#{approver.name}']")
        expect(page).to have_content("Requires 1 more approval")
      end
    end

    context 'when editing an MR with a different author' do
      let(:other_user) { create(:user) }
      let(:merge_request) { create(:merge_request, source_project: project) }

      before do
        project.add_developer(user)

        sign_in(user)
      end

      it 'allows setting groups as approvers' do
        group = create :group
        group.add_developer(other_user)
        group.add_developer(user)

        visit edit_project_merge_request_path(project, merge_request)
        find('#s2id_merge_request_approver_group_ids .select2-input').click

        wait_for_requests

        expect(find('.select2-results')).to have_content(group.name)

        find('.select2-results').click
        click_on("Save changes")

        wait_for_requests
        find('.approvals-components')
        expect(page).to have_content("Requires 1 more approval")
      end

      it 'allows delete approvers group when it`s set in project' do
        approver = create :user
        group = create :group
        group.add_developer(other_user)
        create :approver_group, group: group, target: project
        create :approver, user: approver, target: project

        visit edit_project_merge_request_path(project, merge_request)

        within('.approver-list li.approver-group') do
          click_on "Remove"
        end

        expect(page).to have_css('.approver-list li', count: 1)

        click_on("Save changes")

        find('.approvals-components')
        expect(page).to have_content("Requires 1 more approval")
        expect(page).to have_selector(".approvals-required-text a[title='#{approver.name}']")
      end

      it 'allows changing approvals number' do
        create_list :approver, 3, target: project

        visit project_merge_request_path(project, merge_request)

        # project setting in the beginning on the show MR page
        find('.approvals-components')
        expect(page).to have_content("Requires 1 more approval")

        find('.merge-request').click_on 'Edit'

        # project setting in the beginning on the edit MR page
        expect(find('#merge_request_approvals_before_merge').value).to eq('1')

        fill_in 'merge_request_approvals_before_merge', with: '3'

        click_on('Save changes')

        # new MR setting on the show MR page
        find('.approvals-components')
        expect(page).to have_content("Requires 3 more approvals")

        find('.merge-request').click_on 'Edit'

        # new MR setting on the edit MR page
        expect(find('#merge_request_approvals_before_merge').value).to eq('3')
      end
    end
  end

  context 'Approving by approvers from groups' do
    let(:other_user) { create(:user) }
    let(:merge_request) { create(:merge_request, source_project: project) }
    let(:group) { create :group }

    before do
      project.add_developer(user)
      group.add_developer(other_user)
      group.add_developer(user)

      sign_in(user)
    end

    context 'when group is assigned to a project', :js do
      before do
        create :approver_group, group: group, target: project
        visit project_merge_request_path(project, merge_request)
      end

      it 'I am able to approve' do
        approve_merge_request
        expect(page).to have_content('Approved by')
        expect(page).to have_css('.approver-avatar')
      end

      it 'I am able to unapprove' do
        approve_merge_request
        unapprove_merge_request
        expect(page).to have_no_css('.approver-avatar')
      end
    end

    context 'when group is assigned to a merge request', :js do
      before do
        create :approver_group, group: group, target: merge_request
        visit project_merge_request_path(project, merge_request)
      end

      it 'I am able to approve' do
        approve_merge_request
        wait_for_requests
        expect(page).to have_content('Approved by')
        expect(page).to have_css('.approver-avatar')
      end

      it 'I am able to unapprove' do
        approve_merge_request
        unapprove_merge_request
        expect(page).to have_no_css('.approver-avatar')
      end
    end

    context 'when CI is running but no approval given', :js do
      before do
        create :approver_group, group: group, target: merge_request
        pipeline = create(:ci_empty_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch)
        merge_request.update(head_pipeline: pipeline)
        visit project_merge_request_path(project, merge_request)
      end

      it 'I am unable to set Merge when pipeline succeeds' do
        # before approval status is loaded
        expect(page).to have_button('Merge when pipeline succeeds', disabled: true)

        wait_for_requests

        # after approval status is loaded
        expect(page).to have_button('Merge when pipeline succeeds', disabled: true)
      end
    end
  end

  context 'when merge when discussions resolved is active', :js do
    let(:project) do
      create(:project, :repository,
        approvals_before_merge: 1,
        only_allow_merge_if_all_discussions_are_resolved: true)
    end

    before do
      project.add_developer(user)
      sign_in(user)

      visit project_new_merge_request_path(project, merge_request: { target_branch: 'master', source_branch: 'feature' })

      click_button 'Submit merge request'
    end

    it 'does not show checking ability text' do
      expect(find('.mr-widget-approvals-container')).not_to have_text('Checking ability to merge automatically')
      expect(find('.mr-widget-approvals-container')).to have_selector('.approvals-body')
    end
  end
end

def approve_merge_request
  page.within '.mr-state-widget' do
    find('.approve-btn').click
  end
  wait_for_requests
end

def unapprove_merge_request
  page.within '.mr-state-widget' do
    find('.unapprove-btn-wrap').click
  end
  wait_for_requests
end