jamesshore / quixote

CSS unit and integration testing
Other
848 stars 45 forks source link

Provide a way to reset/reload test frame so SPA frameworks (Angular, React, etc.) re-initialize properly #18

Closed cognivator closed 8 years ago

cognivator commented 8 years ago

Problem

The existing frame.reset() method does a simple replacement of body.InnerHTML with it's original contents. Certain (most?) client-side MV* frameworks -- AngularJS, ReactJS, etc. -- have a robust bootstrapping process that requires more than a simple reset of body.InnerHTML.

Workarounds

(there may be others)

Recreate the iframe

This workaround requires calling frame.remove() followed by quixote.createFrame(...) in lieu of frame.reset(). This solution comprises a complete reset of the iframe itself, and as such seems a little heavyweight... but it works. Pros

Cons

Messages or events can be passed to a listener in the iframe page under test, and the listener can take the necessary reset action. The simple version of this solution uses the native window.postMessage to pass messages to and from the iframe content without running afoul of XSS security protections in the browser. Pros

Cons

Issue the equivalent of a browser refresh to reload the page source from the 'server.' This has the advantage of restarting an SPA the way it expects to be loaded, but without the overhead of recreating the iframe itself.

Implementation Option 1 - refactor existing frame.reset() method Add the reload possibilities as a configuration parameter to frame.reset()

Implementation Option 2 - add a new frame.reload() method Keep the reload separate from the reset.

Optional functionality

jamesshore commented 8 years ago

At first glance, I'm in favor of the reload() approach (option 2).

First, though, do you know why reset isn't working? Is it just a matter of events like DOMContentLoaded not firing? If so, we can trigger those manually.

Or is it that inline scripts don't execute when we do replace innerHTML?

cognivator commented 8 years ago

I'm 99% certain it's the latter: inline scripts that need to run to bootstrap the framework.

Angular, for instance, has a dependency injection and service ecology that needs to initialize, followed by a compilation of the HTML content.

I'm less familiar with the inner workings of React, but it appeared from my sandbox project that the root React component wasn't properly attached to the DOM following the innerHTML replacement. But React components aside, the rest of a React / Flux app would have a Flux Store that needs to be reset as well... more inline script issues, probably.

I can image a future state of Quixote that integrates with the frameworks in some deeper way, similar to how Protractor knows to wait for Angular to become ready before starting the Selenium Webdriver inspections.

But the short path for now feels like a page reload.

Any objections to me taking a pass at the reload functionality? I have two sandbox apps to test with, one Angular and one React. Once we've ironed out how best to have the iframe reload its content, we can decide on a good place in the API... an enhancement to reset or a new reload method.

jamesshore commented 8 years ago

Nope, be my guest.

An easy way to check if the current frame.reset() executes inline scripts would be to create a little web page that alerts and see what happens when you call reset() on it.

cognivator commented 8 years ago

Fair point, and an easy test.

Another question is if there's a way to re-read of scripts in the <head> tag without doing a complete page reload.

jamesshore commented 8 years ago

Mmm, good point about the <head> scripts. Hadn't considered those. It's possible reset could reset the head as well as the body, though, if it works for the body.

jamesshore commented 8 years ago

BTW, I have an Ember app you can test at https://github.com/jamesshore/lab16_emberjs.

jamesshore commented 8 years ago

Pull request: #19.

jamesshore commented 8 years ago

Released in v0.11. Thanks @cognivator!