Skip to content

Conversation

b4handjr
Copy link
Contributor

This is a matured version of PR #69.

Solves #1, #2, #3, #5, #6

@coveralls
Copy link

Coverage Status

Coverage remained the same at 100.0% when pulling 6ba3614 on jrbenny35:add-tab-and-tabbar into 2c6417c on mozilla:master.

1 similar comment
@coveralls
Copy link

coveralls commented Sep 28, 2017

Coverage Status

Coverage remained the same at 100.0% when pulling 6ba3614 on jrbenny35:add-tab-and-tabbar into 2c6417c on mozilla:master.

from foxpuppet.region import Region


class Tab_bar(Region):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use CamelCase naming convention (TabBar) as prescribed in pep8

tabs = [self.Tab(self, el) for el in
self.selenium.find_elements(*self._tabs_locator)]
# Assign handles
for tab, handle in zip(tabs, self.selenium.window_handles):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we be sure that the tab elements and window handles will be returned in the same order? What if there are two windows, each with multiple tabs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into this. I was under the impression that it will be in order of tab opened since it is a list.

Now thinking about the multiple window situation...technically, geckodriver would see it as the same session since you can have multiple windows per profile already. I don't know if there is a way to differentiate tabs from windows as far as window_handles goes. I think they are all in one.

Its a little tricky because do we assume that a user testing this will only open one window? If there is no way to tell a window and a tab apart, unless we create a foxpuppet tracking object that has a dict object and can tell the user which is which depending on when they opened it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we assume that a user testing this will only open one window?

I wouldn't make this assumption. We're providing them with an API for opening new windows and now tabs.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no order of window handles at all. Also note that window handles can change right now given if the tab is moved to a different content process (like remoteness change).

Chrome windows of Firefox are handled via chrome_window_handles but not sure if we pass those through geckodriver yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going along with whimboo, it seems as if it doesn't matter if you open a window or a tab, selenium sees that as a window_handle.

We could either keep track of each event with a dict but would that be necessary? It seems as if we need an easy way to differentiate between a chrome window handle and a "Normal" window_handle.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are going to open a new tab, you are interested in that tab aka window_handle and not the window aka chrome_window_handle. Also it doesn't matter for the test in which window this tab is located in.

with self.selenium.context(self.selenium.CONTEXT_CHROME):
self.selenium.find_element(*self._new_tab_button_locator).click()
self.wait.until(
lambda _: len(self.selenium.window_handles) != current_tabs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should wait for there to be one additional tab open. This would also return true if a window/tab closed, or several new windows/tabs opened.

'anonid', 'close-button')
button.click()
self.wait.until(
lambda _: len(self.selenium.window_handles) != current_tabs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, we should narrow this condition to mean this tab is closed. We could wait for the current handle to be removed, rather than wait for the count of handles to change. Perhaps @whimboo has other suggestions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have nothing additional to add here.

"""
self.selenium.switch_to.window(self.handle)
return self
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why return the tab?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you select or 'Focus' that tab, you should be able to get info about it. i.e.: name, close button, etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You already have a reference to the tab though, in order to call this method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reference before does not switch selenium over to the tab.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should only affect content though? Is the tab region itself not possible to interact with without switching the window?

Copy link
Contributor Author

@b4handjr b4handjr Oct 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct. Should we focus the tab when we open it?

I could follow firefox puppeteer and check if it is selected and if so switch to it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the default behaviour of Firefox is to select the new tab, however I believe it's possible to change this via a preference.

Copy link
Contributor Author

@b4handjr b4handjr Oct 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes so firefox will focus it, but selenium won't it seems.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Selenium requires an explicit update of the current window handle. As long as you don't do that the browsing context will remain in the opener tab.

self.selenium.find_element(*self._new_tab_button_locator).click()
self.wait.until(
lambda _: len(self.selenium.window_handles) != current_tabs)
return self.tabs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why return the list of tabs? It would make more sense to return the new tab so we could do something like:

new_tab = tab_bar.open_new_tab()
new_tab.close()

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would all make more sense if code or the technique which we have already written for Firefox Puppet would be taken. It said this already a couple of times, and here is one more example. It would help us to not have such a lot of review cycles.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently in Firefox Puppeteer:

Wait(self.marionette).until(
            lambda mn: len(mn.window_handles) == len(start_handles) + 1,
            message='No new tab has been opened.')

        handles = self.marionette.window_handles
        [new_handle] = list(set(handles) - set(start_handles))
        [new_tab] = [tab for tab in self.tabs if tab.handle == new_handle]

        # if the new tab is the currently selected tab, switch to it
        if new_tab == self.selected_tab:
            new_tab.switch_to()

Which is what Dave is suggesting anyways. I will model this a little closer.

"""Test close Tab."""
browser.tab_bar.open_new_tab()
browser.tab_bar.tabs[1].close()
assert len(browser.tab_bar.tabs) == 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also ensure the handle for the new tab is not in the list of handles.

def test_close_tab(browser, selenium):
"""Test close Tab."""
browser.tab_bar.open_new_tab()
browser.tab_bar.tabs[1].close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If open_new_tab returned the tab region, then we could simplify this to:

tab = browser.tab_bar.open_new_tab()
tab.close()

Returns: :py:class:`Tab`.
"""
self.selenium.switch_to.window(self.handle)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't show the tab if is not the current tab. In this case, we would want to click on the tab. It's also possible that the tab is scrolled out of view on the tab bar, so we'd need to bring it into view before clicking it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to bring it into view if selenium knows where it is? I mean technically it is there, its not hidden or anything.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if you'll be able to click on an element that's not in view. We're simulating a user, and a user can't switch to a tab without being able to click it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that Marionette currently doesn't know about tabs which haven't been restored via sessionstore. Those don't have a browser, and as such will not be returned.

tabs = browser.tab_bar.open_new_tab()
tabs[-1].select()
assert 'about:newtab' in selenium.current_url
tabs[0].select()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes me feel like we should have a way to access the currently displayed tab, but that can be a later enhancement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean? Selenium has to switch_to that tab even though firefox switches to it automatically.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean like browser.tab_bar.current_tab or an attribute on the tab to indicate if it's the currently selected tab. This would allow you to store the currently selected tab, open a tab, close the new tab, and assert that we're back to the original tab.

@b4handjr
Copy link
Contributor Author

b4handjr commented Oct 2, 2017

@m8ttyB This is the PR.

@b4handjr
Copy link
Contributor Author

b4handjr commented Oct 2, 2017

I found this for making sure the handle is not a window:

@staticmethod
    def get_handle_for_tab(marionette, tab_element):
        """Retrieves the marionette handle for the given :class:`Tab` instance.

        :param marionette: An instance of the Marionette client.

        :param tab_element: The DOM element corresponding to a tab inside the tabs toolbar.

        :returns: `handle` of the tab.
        """
        # TODO: This introduces coupling with marionette's window handles
        # implementation. To avoid this, the capacity to get the XUL
        # element corresponding to the active window according to
        # marionette or a similar ability should be added to marionette.
        handle = marionette.execute_script("""
          let win = arguments[0].linkedBrowser;
          if (!win) {
            return null;
          }
          return win.outerWindowID.toString();
        """, script_args=[tab_element])

        return handle

Thoughts? I see there is a TODO on that function. I am thinking we modify window_manager to have it tell us which window_handle is a tab, or a actual window.

@davehunt
Copy link
Member

davehunt commented Oct 3, 2017

@jrbenny35 you've mentioned this is blocking some other work. I'm wondering if we can reduce the scope of this so whatever it's blocking can be unblocked. Do you have details of what's required by the blocked work?

@b4handjr
Copy link
Contributor Author

b4handjr commented Oct 3, 2017

@jrbenny35 you've mentioned this is blocking some other work. I'm wondering if we can reduce the scope of this so whatever it's blocking can be unblocked. Do you have details of what's required by the blocked work?

@davehunt yeah this is for testing lockbox. I need to install the extension, select the tab the extension opens and then interact with what is on that page. I can use what I have now to get it done but we should still hash this out and find a good solution.

I've made a lot of changes that bring it more in line with puppeteer but the question of what handles are mapped to what windows is still what I am working on.

@b4handjr
Copy link
Contributor Author

b4handjr commented Oct 3, 2017

Why would we need to know what window handle is a window vs a tab. If the user opened a new window that has a tab, they could still interact with the window. i.e.: refresh/back/forward etc.

If the tab is in another window, firefox will switch to it and so will selenium.

Technically each tab can be its own 'window' as well.

@whimboo
Copy link
Collaborator

whimboo commented Jan 14, 2019

I assume we can close this PR given that it didn't receive any update for more than a year.

@whimboo whimboo closed this Jan 14, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants