from tests.support.asserts import assert_success


def element_click(session, element):
    return session.transport.send(
        "POST", "/session/{session_id}/element/{element_id}/click".format(
            session_id=session.session_id,
            element_id=element.id))


def test_click_event_bubbles_to_parents(session, inline):
    session.url = inline("""
        <style>
        body * {
          margin: 10px;
          padding: 10px;
          border: 1px solid blue;
        }
        </style>

        <div id=three>THREE
          <div id=two>TWO
            <div id=one>ONE</div>
          </div>
        </div>

        <script>
        window.clicks = [];

        var elements = document.querySelectorAll("div");
        for (var level = 0; level < elements.length; level++) {
          elements[level].addEventListener("click", function(clickEvent) {
            window.clicks.push(clickEvent.currentTarget);
          });
        }
        </script>
        """)
    three, two, one = session.find.css("div")
    one.click()

    clicks = session.execute_script("return window.clicks")
    assert one in clicks
    assert two in clicks
    assert three in clicks


def test_spin_event_loop(session, inline):
    """
    Wait until the user agent event loop has spun enough times to
    process the DOM events generated by clicking.
    """
    session.url = inline("""
        <style>
        body * {
          margin: 10px;
          padding: 10px;
          border: 1px solid blue;
        }
        </style>

        <div id=three>THREE
          <div id=two>TWO
            <div id=one>ONE</div>
          </div>
        </div>

        <script>
        window.delayedClicks = [];

        var elements = document.querySelectorAll("div");
        for (var level = 0; level < elements.length; level++) {
          elements[level].addEventListener("click", function(clickEvent) {
            var target = clickEvent.currentTarget;
            setTimeout(function() { window.delayedClicks.push(target); }, 0);
          });
        }
        </script>
        """)
    three, two, one = session.find.css("div")
    one.click()

    delayed_clicks = session.execute_script("return window.delayedClicks")
    assert one in delayed_clicks
    assert two in delayed_clicks
    assert three in delayed_clicks


def test_element_disappears_during_click(session, inline):
    """
    When an element in the event bubbling order disappears (its CSS
    display style is set to "none") during a click, Gecko and Blink
    exhibit different behaviour.  Whilst Chrome fires a "click"
    DOM event on <body>, Firefox does not.

    A WebDriver implementation may choose to wait for this event to let
    the event loops spin enough times to let click events propagate,
    so this is a corner case test that Firefox does not hang indefinitely.
    """
    session.url = inline("""
        <style>
        #over,
        #under {
          position: absolute;
          top: 8px;
          left: 8px;
          width: 100px;
          height: 100px;
        }

        #over {
          background: blue;
          opacity: .5;
        }
        #under {
          background: yellow;
        }

        #log {
          margin-top: 120px;
        }
        </style>

        <body id="body">
          <div id=under></div>
          <div id=over></div>

           <div id=log></div>
        </body>

        <script>
        let under = document.querySelector("#under");
        let over = document.querySelector("#over");
        let body = document.querySelector("body");
        let log = document.querySelector("#log");

        function logEvent({type, target, currentTarget}) {
          log.innerHTML += "<p></p>";
          log.lastElementChild.textContent =
              `${type} in ${target.id} (handled by ${currentTarget.id})`;
        }

        for (let ev of ["click", "mousedown", "mouseup"]) {
          under.addEventListener(ev, logEvent);
          over.addEventListener(ev, logEvent);
          body.addEventListener(ev, logEvent);
        }

        over.addEventListener("mousedown", function(mousedownEvent) {
          over.style.display = "none";
        });
        </script>
        """)
    over = session.find.css("#over", all=False)

    # should not time out
    response = element_click(session, over)
    assert_success(response)
