require "watirspec_helper"

not_compliant_on :safari do
  describe Watir::Wait do
    describe "#until" do
      it "waits until the block returns true" do
        Watir::Wait.until(timeout: 0.5) { @result = true }
        expect(@result).to be true
      end

      it "executes block if timeout is zero" do
        Watir::Wait.until(timeout: 0) { @result = true }
        expect(@result).to be true
      end

      it "times out" do
        expect { Watir::Wait.until(timeout: 0.5) { false } }.to raise_error(Watir::Wait::TimeoutError)
      end

      it "times out with a custom message" do
        expect {
          Watir::Wait.until(timeout: 0.5, message: "oops") { false }
        }.to raise_error(Watir::Wait::TimeoutError, "timed out after 0.5 seconds, oops")
      end

      it "uses timer for waiting" do
        timer = Watir::Wait.timer
        expect(timer).to receive(:wait).with(0.5).and_call_original
        Watir::Wait.until(timeout: 0.5) { true }
      end

      it "ordered pairs are deprecated" do
        message = /Instead of passing arguments into Wait#until method, use keywords/
        expect { Watir::Wait.until(0) { true } }.to output(message).to_stderr
      end
    end

    describe "#while" do
      it "waits while the block returns true" do
        expect(Watir::Wait.while(timeout: 0.5) { false }).to be_nil
      end

      it "executes block if timeout is zero" do
        expect(Watir::Wait.while(timeout: 0) { false }).to be_nil
      end

      it "times out" do
        expect { Watir::Wait.while(timeout: 0.5) { true } }.to raise_error(Watir::Wait::TimeoutError)
      end

      it "times out with a custom message" do
        expect {
          Watir::Wait.while(timeout: 0.5, message: "oops") { true }
        }.to raise_error(Watir::Wait::TimeoutError, "timed out after 0.5 seconds, oops")
      end

      it "uses timer for waiting" do
        timer = Watir::Wait.timer
        expect(timer).to receive(:wait).with(0.5).and_call_original
        Watir::Wait.while(timeout: 0.5) { false }
      end

      it "ordered pairs are deprecated" do
        message = /Instead of passing arguments into Wait#while method, use keywords/
        expect { Watir::Wait.while(0) { false } }.to output(message).to_stderr
      end
    end

    describe "#timer" do
      it "returns default timer" do
        expect(Watir::Wait.timer).to be_a(Watir::Wait::Timer)
      end
    end

    describe "#timer=" do
      after { Watir::Wait.timer = nil }

      it "changes default timer" do
        timer = Class.new
        Watir::Wait.timer = timer
        expect(Watir::Wait.timer).to eq(timer)
      end
    end
  end

  describe Watir::Element do
    before do
      browser.goto WatirSpec.url_for("wait.html")
    end

    # TODO: This is deprecated; remove in future version
    not_compliant_on :relaxed_locate do
      describe "#when_present" do
        it "invokes subsequent method calls when the element becomes present" do
          browser.a(id: 'show_bar').click

          bar = browser.div(id: 'bar')
          bar.when_present(2).click
          expect(bar.text).to eq "changed"
        end

        it "times out when given a block" do
          expect { browser.div(id: 'bar').when_present(1) {} }.to raise_error(Watir::Wait::TimeoutError)
        end

        it "times out when not given a block" do
          message = /^timed out after 1 seconds, waiting for (\{:id=>"bar", :tag_name=>"div"\}|\{:tag_name=>"div", :id=>"bar"\}) to become present$/
          expect { browser.div(id: 'bar').when_present(1).click }.to raise_error(Watir::Wait::TimeoutError, message)
        end

        it "responds to Element methods" do
          decorator = browser.div.when_present

          expect(decorator).to respond_to(:exist?)
          expect(decorator).to respond_to(:present?)
          expect(decorator).to respond_to(:click)
        end

        it "delegates present? to element" do
          Object.class_eval do
            def present?
              false
            end
          end
          element = browser.a(id: "show_bar").when_present(1)
          expect(element).to be_present
        end

        it "processes before calling present?" do
          browser.a(id: 'show_bar').click
          expect(browser.div(id: 'bar').when_present.present?).to be true
        end
      end
    end

    not_compliant_on :relaxed_locate do
      describe "#wait_until &:enabled?" do
        it "invokes subsequent method calls when the element becomes enabled" do
          browser.a(id: 'enable_btn').click

          btn = browser.button(id: 'btn')
          btn.wait_until(timeout: 2, &:enabled?).click
          Watir::Wait.while { btn.enabled? }
          expect(btn.disabled?).to be true
        end

        it "times out" do
          message = /^timed out after 1 seconds, waiting for true condition on (\{:id=>"btn", :tag_name=>"button"\}|\{:tag_name=>"button", :id=>"btn"\})$/
          expect { browser.button(id: 'btn').wait_until(timeout: 1, &:enabled?).click }.to raise_error(Watir::Wait::TimeoutError, message)
        end

        it "responds to Element methods" do
          element = browser.button.wait_until { true }

          expect(element).to respond_to(:exist?)
          expect(element).to respond_to(:present?)
          expect(element).to respond_to(:click)
        end

        it "can be chained with #wait_until &:present?" do
          browser.a(id: 'show_and_enable_btn').click
          browser.button(id: 'btn2').wait_until(&:present?).wait_until(&:enabled?).click

          expect(browser.button(id: 'btn2')).to exist
          expect(browser.button(id: 'btn2')).to be_enabled
        end
      end
    end

    describe "#wait_until_present" do
      it "it waits until the element appears" do
        browser.a(id: 'show_bar').click
        expect { browser.div(id: 'bar').wait_until_present(timeout: 5) }.to_not raise_exception
      end

      it "times out if the element doesn't appear" do
        message = /^timed out after 1 seconds, waiting for true condition on (\{:id=>"bar", :tag_name=>"div"\}|\{:tag_name=>"div", :id=>"bar"\})$/
        expect { browser.div(id: 'bar').wait_until_present(timeout: 1) }.to raise_error(Watir::Wait::TimeoutError, message)
      end

      it "ordered pairs are deprecated" do
        browser.a(id: 'show_bar').click
        message = /Instead of passing arguments into #wait_until_present method, use keywords/
        expect { browser.div(id: 'bar').wait_until_present(5) }.to output(message).to_stderr
      end
    end

    describe "#wait_while_present" do
      it "waits until the element disappears" do
        browser.a(id: 'hide_foo').click
        expect { browser.div(id: 'foo').wait_while_present(timeout: 1) }.to_not raise_exception
      end

      it "times out if the element doesn't disappear" do
        message = /^timed out after 1 seconds, waiting for false condition on (\{:id=>"foo", :tag_name=>"div"\}|\{:tag_name=>"div", :id=>"foo"\})$/
        expect { browser.div(id: 'foo').wait_while_present(timeout: 1) }.to raise_error(Watir::Wait::TimeoutError, message)
      end

      it "ordered pairs are deprecated" do
        browser.a(id: 'hide_foo').click
        message = /Instead of passing arguments into #wait_while_present method, use keywords/
        expect { browser.div(id: 'foo').wait_while_present(5) }.to output(message).to_stderr
      end
    end

    describe "#wait_until" do
      it "returns element for additional actions" do
        element = browser.div(id: 'foo')
        expect(element.wait_until(&:exist?)).to eq element
      end

      it "accepts self in block" do
        element = browser.div(id: 'bar')
        browser.a(id: 'show_bar').click
        expect { element.wait_until { |el| el.text == 'bar' } }.to_not raise_exception
      end

      it "accepts any values in block" do
        element = browser.div(id: 'bar')
        expect { element.wait_until { true } }.to_not raise_exception
      end

      it "accepts just a timeout parameter" do
        element = browser.div(id: 'bar')
        expect { element.wait_until(timeout: 0) { true } }.to_not raise_exception
      end

      it "accepts just a message parameter" do
        element = browser.div(id: 'bar')
        expect { element.wait_until(message: 'no') { true } }.to_not raise_exception
      end
    end

    describe "#wait_while" do
      it "returns element for additional actions" do
        element = browser.div(id: 'foo')
        browser.a(id: 'hide_foo').click
        expect(element.wait_while(&:present?)).to eq element
      end

      it "accepts self in block" do
        element = browser.div(id: 'foo')
        browser.a(id: 'hide_foo').click
        expect { element.wait_while { |el| el.text == 'foo' } }.to_not raise_exception
      end

      it "accepts any values in block" do
        element = browser.div(id: 'foo')
        expect { element.wait_while { false } }.to_not raise_exception
      end

      it "accepts just a timeout parameter" do
        element = browser.div(id: 'foo')
        expect { element.wait_while(timeout: 0) { false } }.to_not raise_exception
      end

      it "accepts just a message parameter" do
        element = browser.div(id: 'foo')
        expect { element.wait_while(message: 'no') { false } }.to_not raise_exception
      end
    end
  end

  describe Watir do
    describe "#default_timeout" do
      before do
        Watir.default_timeout = 1

        browser.goto WatirSpec.url_for("wait.html")
      end

      after do
        # Reset the default timeout
        Watir.default_timeout = 30
      end

      context "when no timeout is specified" do
        it "is used by Wait#until" do
          expect {
            Watir::Wait.until { false }
          }.to raise_error(Watir::Wait::TimeoutError)
        end

        it "is used by Wait#while" do
          expect {
            Watir::Wait.while { true }
          }.to raise_error(Watir::Wait::TimeoutError)
        end

        it "is used by Element#wait_until_present" do
          expect {
            browser.div(id: 'bar').wait_until_present
          }.to raise_error(Watir::Wait::TimeoutError)
        end

        it "is used by Element#wait_while_present" do
          expect {
            browser.div(id: 'foo').wait_while_present
          }.to raise_error(Watir::Wait::TimeoutError)
        end
      end
    end
  end
end