Closed greyepoxy closed 4 years ago
Thanks for your detailed suggestion. I'm not personally familiar with Cypress, but I'm open to changing Quixote to make it easier to support it.
It looks like the main thing we would need is the ability to make QElements. Currently, to make a QElement, we need two things: the DOM node (for obvious reasons) and the QFrame it lives in. You suggested that we remove the QFrame. Currently, we use the QFrame in these places:
QElement, where it's used a) in getRawStyle()
work around a Firefox bug; and b) in parent()
to detect the body element so it won't be returned.
ElementEdge, where it's used to detect if an element is rendered.
ElementRenderedEdge, where it's used to find the page borders, so we can tell if an edge of an element is off-screen. (ElementRenderedEdge is in turn used by ElementRendered to see if the whole element is off-screen.)
I'm not convinced we can get rid of the QFrame requirement. If you see something I don't, let me know. I haven't studied it in depth.
Other than the QFrame issue, this seems fairly straightforward. We'd just need a QElement.fromDomElement()
factory function to go along with our existing QElement.toDomElement()
method. We could also make a corresponding QFrame.fromDomElement()
factory.
Thanks James, appreciate you taking a look. I opened a pull request demonstrating the removal of QFrame. Let me know what you think
What do you think about having a method such as quixote.frameFromDom(domElement)
instead of having a separate QContentHost class? It would allow you to make a QFrame out of your Cypress iframe.
hmmm I had not thought of that. I believe it would work but not ideal since cypress does not expose the frame as part of its api (just window and document ). So would have to figure out a way to get at it (maybe a little fragile?) or ask them to extend their api.
Would you like me to look into that?
No need, I'm working on it now. I think frameFromDom()
would only work if we could somehow get the frame from an arbitrary element inside the frame, which may not be possible. Or if we don't need the actual iframe element, which might be. The main thing I want to avoid is having a separate QContentHost class--it makes the API more complicated and doesn't benefit the majority of users.
Yeah I completely agree, curious to see what you figure out
Looking at it further, the only thing in QFrame that needs the actual iframe element is frame.resize()
. As your code shows, everything else of interest can be done with nothing more than domElement.ownerDocument.
(Or have I missed something? Let me know.)
So what I'm thinking is that I'll just modify QFrame so they can be constructed from an
Here's my plan:
quixote.browser
into its own module.quixote.frameFromDom()
. It will be smart enough to work with either an arbitrary element or an actual frame element.
quixote.frameFromDom()
.quixote.elementFromDom()
. It will take an optional QFrame parameter. If it doesn't get one, it will call quixote.frameFromDom()
.Let me know what you think.
hmmm there are a couple of methods that I do not believe make sense without the iFrame (or would behave differently depending on if constructed with the frame or the ownerDocument),
frame.remove
frame.reload
frame.reset
frame.toDomElement
frame.resize
Cannot say I am crazy about matrixing the QFrame
logic depending on how it was constructed. I think I would prefer the more explicit objects/interfaces. For a consumer, knowing which functions are valid depending on how QFrame
was constructed will be something new to figure out. Seems like that might lead to its own confusions?
How about the following adjustment to https://github.com/jamesshore/quixote/pull/56,
QFrame.toContentHost
"internal" so that we do not change the QFrame
api at allThen that will mean there is just the two new public api methods quixote.elementFromDom
and QElement.host()
. Or I if we want to just do a single new public api then something like quixote.hostFromDom
would also work (although QElement.host()
would still be needed internally).
Not sure if this would address your core concerns or not, what do you think?
I prefer explicit interfaces too.
My concern here is that this is a very narrow use case. Most people will just use quixote.createFrame directly. I want to make common things easy, using terminology people are familiar with. I want to support niche use cases too, but not at the expense of introducing jargon ("content host") that affects the mainstream case.
I'm going to take a second look at QElement and see if there's a way of removing the dependency on QFrame entirely. That would sidestep the entire issue.
Okay, having looked at it further, I'm leaning toward using your QContentHost but not exposing it to the public. Instead, QFrame will encapsulate it and QElement will use it in place of the current frame. I think this is a fairly minor change to your code in #56. I'll give it a try and see where it takes me.
I think I'll still provide a quixote.frameFromDom
as planned, for completeness, but it will just take an
ahh okay so if I understand correctly, two new public apis
var element = quixote.elementFromDom(domElement)
var frame = quixote.frameFromDom(iFrameDomNode)
No public apis that expose the QContentHost
(like QElement.host
does today)
Assuming I understand correctly, đ that should be just a small modification and should totally work, thanks for exploring these different options!
Yes, exactly.
This has been integrated and will be in the next release.
I've decided not to add quixote.frameFromDom()
this time. QFrame is kind of a mess and there's weird interactions with reset() and reload() that I'm not sure how I want to handle. Since nobody's asking for it and I don't have a lot of spare time right now, I'm going to drop it.
Released in v0.15.
Awesome thanks James! đ
Thanks for your hard work!
Thanks James for your great work on this library! Really appreciate that you are pushing the bounds of what people think is possible
Recently I have been using cypress.io to do my UI testing and have been greatly enjoying how it doesn't require arbitrary waits, allows you to visualize everything that occurred in your test during or after it runs, and also encourages writing fast tests.
I was curious on your thoughts for adding quixote support to cypress? I can think of a couple different ways of integrating,
Expose a way of creating QElement's from a dom node
Since cypress already hosts the UI under tests in an iFrame, there is no need for creating a corresponding QFrame. If a QElement from dom node constructor was exposed then after getting nodes in cypress could create the QElements and then assert as one would today (using descriptors) and cyrpess's
should
command https://docs.cypress.io/guides/references/assertions.html#Should-callback.I believe this would be relatively straight forward, looking at the source https://github.com/jamesshore/quixote/blob/release/src/q_element.js#L13, I believe we just need to remove the frame from the constructor and everytime you want to access the document or body (like here https://github.com/jamesshore/quixote/blob/release/src/q_element.js#L47) use
ownerDocument
and thendefaultView
orparentView
instead.As a Custom Assertion library
I saw the discussion in the other thread https://github.com/jamesshore/quixote/issues/47 about updating the assertion library and one of the ideas called out was to create custom chai assertions. Assuming the object being asserted upon is create able from just dom nodes I believe this would be easy to use in cypress https://docs.cypress.io/guides/references/assertions.html#Adding-New-Assertions.
Can get a QFrame from a cypress test frame
Not sure you can get at the iFrame directly but cypress does expose commands to get the window or document https://docs.cypress.io/api/commands/window.html#Syntax.
As a Plugin
There are a bunch of classic visual testing tools integrated as plugins https://docs.cypress.io/plugins/#visual-testing. Not exactly sure if that makes sense with quixote, do not really know how the integration would work.