electron-userland / spectron

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

Spectron's app.client does not reflect a newly created window #208

Closed Exegetech closed 7 years ago

Exegetech commented 7 years ago

In my Electron app, I switch back and forth between one window and the other window, by destroying and creating the window (BrowserWindow). A non authenticated user starts the app, there is a window that prompts for their username and password, and once they click login and authenticated, that window is destroyed another window is created. This newly created window is the actual app where user does stuffs.

Since I store the user token inside Electron's localStorage, I thought I could inspect the values.

This is the code

app.start()
  .then(() => {
    const browser = app.client
    browser.waitUntilWindowLoaded()
      .then(() => browser.localStorage())
      .then((storage) => console.log("storage", storage))
      .then(() => browser.setValue("input[type='email']", 'user@mail.com'))
      .then(() => browser.setValue("input[type='password']", 'userpass'))
      .then(() => browser.click("button[type='button']"))
      .then(() => browser.waitUntilWindowLoaded())
      .then(() => browser.sessionStorage())
      .then((storage) => console.log("storage", storage))
      .then(() => browser.getHTML('.userAvatar', false))
      .then((data) => console.log("userAvatr", data))
      .catch((err) => console.log("ERR", err))
  })

However, this code logs an error. The error is caused because there is no such element .userAvatar in that page. The element .userAvatar is located in the newly created browser window (authenticated section). If I proceed to look for the elements that was in the previously destroyed non-authenticated window (such as input[type='email']) I can find it.

In the code above, logging the localStorage also gives me something that I don't expect. (I expect something like { token: 342341523424 } because the localStorage saves the token from the authentication). However, this is what I got from running the code

storage { sessionId: '99aefcc5ce89d90ad790c747e77abac7',
  status: 0,
  value: [] }
storage { sessionId: '99aefcc5ce89d90ad790c747e77abac7',
  status: 0,
  value: [] }
ERR { Error: An element could not be located on the page using the given search parameters (".userAvatar").
  type: 'NoSuchElement',
  message: 'An element could not be located on the page using the given search parameters (".userAvatar").' }

But suppose I shut down the app without logging out. What will happen is, I read the localStorage and go to authenticated view right away. When I run the code above again, that is what will happen, so it will log

storage { sessionId: '09e0691eb153129e21d6cc6200ba6e4d',
  status: 0,
  value:
   [ 'activeUser',
     'billingStatus',
     'endpointUsed',
     'lastTimeEndpointCommunicated',
     'token' ] }
ERR { Error: An element could not be located on the page using the given search parameters ("input[type='email']").
  type: 'NoSuchElement',
  message: 'An element could not be located on the page using the given search parameters ("input[type=\'email\']").' }

Which now, the localStorage exists and the old element input[type=email] does not exist.

TLDR

If my app destroys and creates new window, the app.client contents do not reflect the newly created window.

Exegetech commented 7 years ago

I found the solution.

It turns out I have to refocus the window again after using some time out.

Here is the code


app.start()
  .then(() => {
    const browser = app.client
    browser.waitUntilWindowLoaded()
      .then(() => browser.localStorage())
      .then((storage) => console.log("storage", storage))
      .then(() => browser.setValue("input[type='email']", 'myusername@mail.com'))
      .then(() => browser.setValue("input[type='password']", 'mypassword'))
      .then(() => browser.click("button[type='button']"))
      .then(() => wait(3000))
      .then(() => browser.windowHandles())
      .then((handles) => browser.window(handles.value[0]))
      .then(() => browser.getHTML('.userAvatar', false))
      .then((data) => console.log("userAvatar", data))
      .then(() => browser.localStorage())
      .then((storage) => console.log("storage", storage))
      .catch((err) => console.log("Err", err))
  })

I wonder if there's better way than timeout? I have tried things such as waiting until certain text is visible, but still does not work.