electron-userland / spectron

DEPRECATED: 🔎 Test Electron apps using ChromeDriver
http://electronjs.org/spectron
MIT License
1.68k stars 229 forks source link

add `windowById` helper #149

Open teameh opened 7 years ago

teameh commented 7 years ago

as @kevinsawicki points out here https://github.com/electron/spectron/issues/6:

ChromeDriver seems to treat each tag as a separate window so you should be able to use the windowByIndex helper to focus a webview by location in the DOM and then you can call other helpers and it will be scoped to that window.

if you have multiple webviews, then working with just the index is very error prone. Would it be possible get the webview based on its id? Something like: windowById('#my-webview)?

Or could we maybe already achieve this with a couple of promises? I tried but couldn't make it work. Any idea's?

gautaz commented 7 years ago

I am also having the same issue.

Currently I am locating a particular element's id contained by the targeted webview by iterating over all window handles.

I'm using something like that:

const selectWindow = async function(app, selector) {
  const handles = await app.client.windowHandles();
  for (const handle of handles.value) {
    await app.client.window(handle);
    const element = await app.client.element(selector);
    if (element.status === 0) {
      return element;
    }
  }
  return {};
};

But processing this way seems rather awkward, inefficient and also error prone (duplicate element ids in two different webviews is authorised).

webdriverio window() call is able to retrieve a window by using the tab name attribute but I do not know if there is a way to associate a tab name to a webview element.

teameh commented 7 years ago

I thought of looping trough the webviews but couldn't get it working.. thanks for sharing your example.

Do i get it right that the code will focus each webview until one is found matching the selector? That seems indeed quite inefficient. We could maybe cache the webview.. but that will of course break if you dynamically change your webviews..

webdriverio window() call is able to retrieve a window by using the tab name attribute but I do not know if there is a way to associate a tab name to a webview element.

Me neither, that is something that you can use in the browser when using <a target="my-tab" href=... right?

teameh commented 7 years ago

Currently I am locating a particular element's id contained by the targeted webview by iterating over all window handles.

Finally got you example working, yeah that's not ideal. Targeting with the id of the webview itself would be better.

I went over the webdriver docs again but I fear this is something that needs changes to the webview implementation in electron..

gautaz commented 7 years ago

@tiemevanveen I sadly came up to the same conclusion.

@kevinsawicki I am pretty sure that you are already a lot busy with all the other issues but perhaps can you come up with a better work around than the current one ?

kevinsawicki commented 7 years ago

I am pretty sure that you are already a lot busy with all the other issues but perhaps can you come up with a better work around than the current one ?

Nothing I can think of initially but I think something could be added, will have to investigate further.

teameh commented 7 years ago

Thanks, that would be great!

evertonfraga commented 7 years ago

+1.

I'm also looping on the window handles, looking for the URL to determine the <webview> I need. But that can potentially reduce my tests' range.

ChrisHSandN commented 5 years ago

Combined with https://github.com/electron/spectron/issues/269 which stops the use of BrowserWindow.getAllWindows() / BrowserWindow.getChildWindows() Spectron is very painful to use with an app which uses multiple windows.

As help to anyone else finding this thread, make sure you use async/await https://github.com/electron/spectron/issues/149#issuecomment-267919689 so the checks run in series rather than a Promise.all(), I wasted much time with the latter before realizing the window needs to be held in focus while the getUrl()/getTitle() etc. is run.

Here is my WebdriverIO waitUntil() method which waits for (and leaves in focus) a window based on its url

.waitUntil(() => {
    return app
        .client
        .windowHandles()
        .then(async (windowHandles) => {
            for (const windowHandleValue of windowHandles.value) {
                await app.client.window(windowHandleValue);

                const getUrl = await app.client.getUrl();

                if (/foobar.html$/.test(getUrl)) {
                    return Promise.resolve(windowHandleValue);
                }
            }

            return Promise.resolve(false);
        });
})
mbaer3000 commented 5 years ago

Cf. @ChrisHSandN method to focus by url: we ran into issues in case the index has gone in the meantime. So we made our solution a bit more robust there:

/**
 * @param {string} urlFragment - substring
 * @returns {Promise<boolean>}
 */
async function focusWindowByUrlFragment(urlFragment) {
  const windowHandles = await client.windowHandles();
  for (let index = 0; index < windowHandles.value.length; index++) {
    let windowUrl;

    try {
      await raceWithTimeout(5000, client.windowByIndex(index));
      windowUrl = await raceWithTimeout(5000, client.getUrl());
    } catch (error) {
      // index might have gone in the meantime, hence this error handling
      return false;
    }

    if (windowUrl.match(urlFragment)) {
      return true;
    }
  }
  return false;
}
ChrisHSandN commented 4 years ago

Doing some more Spectron work and I have discovered since the last update this has now been implemented via: https://github.com/electron-userland/spectron/pull/365

app.client.switchWindow(String|RegExp);

@teameh I think this issue could be closed now.

teameh commented 4 years ago

Haha it's been more than 3 years. Cool to let us know!

I'm not doing much with electron at the moment so I can't verify this but feel free to close this issue if it's fixed!