require 'watirspec_helper'

describe 'Browser' do
  before do
    url = WatirSpec.url_for('window_switching.html')
    browser.goto url
    browser.a(id: 'open').click
    Watir::Wait.until { browser.windows.size == 2 }
  end

  after do
    browser.original_window.use
    browser.windows.reject(&:current?).each(&:close)
  end

  describe '#windows' do
    it 'returns an array of window handles' do
      wins = browser.windows
      expect(wins).to_not be_empty
      wins.each { |win| expect(win).to be_kind_of(Watir::Window) }
    end

    it 'only returns windows matching the given selector' do
      browser.wait_until { |b| b.window(title: 'closeable window').exists? }
      expect(browser.windows(title: 'closeable window').size).to eq 1
    end

    it 'raises ArgumentError if the selector is invalid' do
      expect { browser.windows(name: 'foo') }.to raise_error(ArgumentError)
    end

    it 'returns an empty array if no window matches the selector' do
      expect(browser.windows(title: 'noop')).to eq []
    end
  end

  describe '#window' do
    it 'finds window by :url' do
      w = browser.window(url: /closeable\.html/).use
      expect(w).to be_kind_of(Watir::Window)
    end

    it 'finds window by :title' do
      w = browser.window(title: 'closeable window').use
      expect(w).to be_kind_of(Watir::Window)
    end

    it 'finds window by :index' do
      w = browser.window(index: 1).use
      expect(w).to be_kind_of(Watir::Window)
    end

    it 'should not find incorrect handle' do
      expect(browser.window(handle: 'bar')).to_not be_present
    end

    it 'returns the current window if no argument is given' do
      expect(browser.window.url).to match(/window_switching\.html/)
    end

    it 'stores the reference to a window when no argument is given' do
      original_window = browser.window
      browser.window(index: 1).use
      expect(original_window.url).to match(/window_switching\.html/)
    end

    bug 'https://bugzilla.mozilla.org/show_bug.cgi?id=1223277', :firefox do
      not_compliant_on :headless do
        it 'it executes the given block in the window' do
          browser.window(title: 'closeable window') {
            link = browser.a(id: 'close')
            expect(link).to exist
            link.click
          }.wait_while(&:present?)

          expect(browser.windows.size).to eq 1
        end
      end
    end

    it 'raises ArgumentError if the selector is invalid' do
      expect { browser.window(name: 'foo') }.to raise_error(ArgumentError)
    end

    it 'raises a NoMatchingWindowFoundException error if no window matches the selector' do
      expect { browser.window(title: 'noop').use }.to raise_no_matching_window_exception
    end

    it "raises a NoMatchingWindowFoundException error if there's no window at the given index" do
      expect { browser.window(index: 100).use }.to raise_no_matching_window_exception
    end

    it 'raises NoMatchingWindowFoundException error when attempting to use a window with an incorrect handle' do
      expect { browser.window(handle: 'bar').use }.to raise_no_matching_window_exception
    end
  end
end

describe 'Window' do
  context 'multiple windows' do
    before do
      browser.goto WatirSpec.url_for('window_switching.html')
      browser.a(id: 'open').click
      Watir::Wait.until { browser.windows.size == 2 }
    end

    after do
      browser.original_window.use
      browser.windows.reject(&:current?).each(&:close)
    end

    not_compliant_on :safari, %i[firefox linux] do
      describe '#close' do
        it 'closes a window' do
          browser.a(id: 'open').click
          Watir::Wait.until { browser.windows.size == 3 }

          browser.window(title: 'closeable window').close
          expect(browser.windows.size).to eq 2
        end
      end

      it 'closes the current window' do
        browser.a(id: 'open').click
        Watir::Wait.until { browser.windows.size == 3 }

        window = browser.window(title: 'closeable window').use
        window.close
        expect(browser.windows.size).to eq 2
      end
    end

    describe '#use' do
      it 'switches to the window' do
        browser.window(title: 'closeable window').use
        expect(browser.title).to eq 'closeable window'
      end
    end

    describe '#current?' do
      it 'returns true if it is the current window' do
        expect(browser.window(title: browser.title)).to be_current
      end

      it 'returns false if it is not the current window' do
        expect(browser.window(title: 'closeable window')).to_not be_current
      end
    end

    describe '#title' do
      it 'returns the title of the window' do
        titles = browser.windows.map(&:title)
        expect(titles.size).to eq 2

        expect(titles.sort).to eq ['window switching', 'closeable window'].sort
      end

      it 'does not change the current window' do
        expect(browser.title).to eq 'window switching'
        expect(browser.windows.find { |w| w.title == 'closeable window' }).to_not be_nil
        expect(browser.title).to eq 'window switching'
      end
    end

    describe '#url' do
      it 'returns the url of the window' do
        expect(browser.windows.select { |w| w.url =~ /window_switching\.html/ }.size).to eq 1
        expect(browser.windows.select { |w| w.url =~ /closeable\.html$/ }.size).to eq 1
      end

      it 'does not change the current window' do
        expect(browser.url).to match(/window_switching\.html/)
        expect(browser.windows.find { |w| w.url =~ /closeable\.html/ }).to_not be_nil
        expect(browser.url).to match(/window_switching/)
      end
    end

    describe '#eql?' do
      it 'knows when two windows are equal' do
        expect(browser.window).to eq browser.window(index: 0)
      end

      it 'knows when two windows are not equal' do
        win1 = browser.window(index: 0)
        win2 = browser.window(index: 1)

        expect(win1).to_not eq win2
      end
    end

    not_compliant_on :relaxed_locate do
      describe '#wait_until &:present?' do
        it 'times out waiting for a non-present window' do
          expect {
            browser.window(title: 'noop').wait_until(timeout: 0.5, &:present?)
          }.to raise_error(Watir::Wait::TimeoutError)
        end
      end
    end
  end

  context 'with a closed window' do
    before do
      browser.goto WatirSpec.url_for('window_switching.html')
      browser.a(id: 'open').click
      Watir::Wait.until { browser.windows.size == 2 }
    end

    after do
      browser.original_window.use
      browser.windows.reject(&:current?).each(&:close)
    end

    bug 'https://bugzilla.mozilla.org/show_bug.cgi?id=1223277', :firefox do
      not_compliant_on :headless do
        describe '#exists?' do
          it 'returns false if previously referenced window is closed' do
            window = browser.window(title: 'closeable window')
            window.use
            browser.a(id: 'close').click
            Watir::Wait.until { browser.windows.size == 1 }
            expect(window).to_not be_present
          end

          it 'returns false if closed window is referenced' do
            browser.window(title: 'closeable window').use
            browser.a(id: 'close').click
            Watir::Wait.until { browser.windows.size == 1 }
            expect(browser.window).to_not exist
          end

          it 'returns false if window closes during iteration' do
            browser.window(title: 'closeable window').use
            original_handle = browser.original_window.instance_variable_get('@handle')
            handles = browser.windows.map { |w| w.instance_variable_get('@handle') }

            browser.a(id: 'close').click
            Watir::Wait.until { browser.windows.size == 1 }
            allow(browser.wd).to receive(:window_handles).and_return(handles, [original_handle])
            expect(browser.window(title: 'closeable window')).to_not exist
          end
        end
      end
    end

    describe '#current?' do
      it 'returns false if the referenced window is closed' do
        original_window = browser.window
        browser.window(title: 'closeable window').use
        original_window.close
        expect(original_window).to_not be_current
      end
    end

    not_compliant_on :safari, :headless do
      describe '#eql?' do
        it 'should return false when checking equivalence to a closed window' do
          original_window = browser.window
          other_window = browser.window(index: 1)
          other_window.use
          original_window.close
          expect(other_window == original_window).to be false
        end
      end
    end

    not_compliant_on :headless do
      describe '#use' do
        it 'raises NoMatchingWindowFoundException error when attempting to use a referenced window that is closed' do
          original_window = browser.window
          browser.window(index: 1).use
          original_window.close
          expect { original_window.use }.to raise_no_matching_window_exception
        end

        bug 'https://bugzilla.mozilla.org/show_bug.cgi?id=1223277', :firefox do
          it 'raises NoMatchingWindowFoundException error when attempting to use the current window if it is closed' do
            browser.window(title: 'closeable window').use
            browser.a(id: 'close').click
            Watir::Wait.until { browser.windows.size == 1 }
            expect { browser.window.use }.to raise_no_matching_window_exception
          end
        end
      end
    end

    it 'raises an exception when using an element on a closed window' do
      window = browser.window(title: 'closeable window')
      window.use
      browser.a(id: 'close').click
      msg = 'browser window was closed'
      expect { browser.a.text }.to raise_exception(Watir::Exception::NoMatchingWindowFoundException, msg)
    end

    it 'raises an exception when locating a closed window' do
      browser.window(title: 'closeable window').use
      handles = browser.windows.map(&:handle)
      browser.a(id: 'close').click
      allow(browser.wd).to receive(:window_handles).and_return(handles, [browser.original_window.handle])
      expect { browser.window(title: 'closeable window').use }.to raise_no_matching_window_exception
    end

    it 'raises an exception when locating a window closed during lookup' do
      browser.window(title: 'closeable window').use
      browser.a(id: 'close-delay').click

      begin
        module Watir
          class Browser
            alias title_old title

            def title
              sleep 0.5
              title_old
            end
          end
        end

        expect { browser.window(title: 'closeable window').use }.to raise_no_matching_window_exception
      ensure
        module Watir
          class Browser
            alias title title_old
          end
        end
      end
    end
  end

  bug 'https://bugzilla.mozilla.org/show_bug.cgi?id=1223277', :firefox do
    not_compliant_on :headless do
      context 'with current window closed' do
        before do
          browser.goto WatirSpec.url_for('window_switching.html')
          browser.a(id: 'open').click
          Watir::Wait.until { browser.windows.size == 2 }
          browser.window(title: 'closeable window').use
          browser.a(id: 'close').click
          Watir::Wait.until { browser.windows.size == 1 }
        end

        after do
          browser.window(index: 0).use
          browser.windows[1..-1].each(&:close)
        end

        describe '#present?' do
          it 'should find window by index' do
            expect(browser.window(index: 0)).to be_present
          end

          it 'should find window by url' do
            expect(browser.window(url: /window_switching\.html/)).to be_present
          end

          it 'should find window by title' do
            expect(browser.window(title: 'window switching')).to be_present
          end
        end

        describe '#use' do
          context 'switching windows without blocks' do
            it 'by index' do
              browser.window(index: 0).use
              expect(browser.title).to be == 'window switching'
            end

            it 'by url' do
              browser.window(url: /window_switching\.html/).use
              expect(browser.title).to be == 'window switching'
            end

            it 'by title' do
              browser.window(title: 'window switching').use
              expect(browser.url).to match(/window_switching\.html/)
            end
          end

          context 'Switching windows with blocks' do
            it 'by index' do
              browser.window(index: 0).use { expect(browser.title).to be == 'window switching' }
            end

            it 'by url' do
              browser.window(url: /window_switching\.html/).use { expect(browser.title).to be == 'window switching' }
            end

            it 'by title' do
              browser.window(title: 'window switching').use { expect(browser.url).to match(/window_switching\.html/) }
            end
          end
        end
      end
    end
  end

  context 'manipulating size and position' do
    before do
      browser.goto WatirSpec.url_for('window_switching.html')
    end

    not_compliant_on :headless do
      it 'should get the size of the current window' do
        size = browser.window.size

        expect(size.width).to eq browser.execute_script('return window.outerWidth;')
        expect(size.height).to eq browser.execute_script('return window.outerHeight;')
      end
    end

    it 'should get the position of the current window' do
      pos = browser.window.position

      expect(pos.x).to eq browser.execute_script('return window.screenX;')
      expect(pos.y).to eq browser.execute_script('return window.screenY;')
    end

    not_compliant_on :headless do
      it 'should resize the window' do
        initial_size = browser.window.size
        browser.window.resize_to(
          initial_size.width - 20,
          initial_size.height - 20
        )

        new_size = browser.window.size

        expect(new_size.width).to eq initial_size.width - 20
        expect(new_size.height).to eq initial_size.height - 20
      end
    end

    not_compliant_on :headless, %i[remote firefox] do
      it 'should move the window' do
        initial_pos = browser.window.position

        browser.window.move_to(
          initial_pos.x + 2,
          initial_pos.y + 2
        )

        new_pos = browser.window.position
        expect(new_pos.x).to eq initial_pos.x + 2
        expect(new_pos.y).to eq initial_pos.y + 2
      end
    end

    not_compliant_on :headless do
      compliant_on :window_manager do
        it 'should maximize the window' do
          initial_size = browser.window.size
          browser.window.resize_to(
            initial_size.width,
            initial_size.height - 20
          )

          browser.window.maximize
          browser.wait_until { browser.window.size != initial_size }

          new_size = browser.window.size
          expect(new_size.width).to be >= initial_size.width
          expect(new_size.height).to be > (initial_size.height - 20)
        end
      end
    end
  end
end