Skip to content

Commit

Permalink
preserve href of cross-domain stylesheets
Browse files Browse the repository at this point in the history
so that cross-domain styles aren’t lost when snapshots are restored
  • Loading branch information
chrisbreiding committed Sep 26, 2016
1 parent efe4cc3 commit 40e2281
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 33 deletions.
6 changes: 4 additions & 2 deletions app/js/driver/cypress/log.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,14 @@ $Cypress.Log = do ($Cypress, _, Backbone) ->
at: null
next: null

{body, htmlClasses} = @Cypress.createSnapshot(@get("$el"))
{body, htmlClasses, headStyles, bodyStyles} = @Cypress.createSnapshot(@get("$el"))

obj = {
name: name
state: body
body: body
htmlClasses: htmlClasses
headStyles: headStyles
bodyStyles: bodyStyles
}

snapshots = @get("snapshots") ? []
Expand Down
63 changes: 44 additions & 19 deletions app/js/driver/cypress/snapshot.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ do ($Cypress, _) ->
reduceText = (arr, fn) ->
_.reduce arr, ((memo, item) -> memo += fn(item)), ""

getCssRulesString = (stylesheet) ->
## some browsers may throw a SecurityError if the stylesheet is cross-domain
## https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet#Notes
## for others, it will just be null
return try
if rules = stylesheet.rules or stylesheet.cssRules
reduceText rules, (rule) -> rule.cssText
else
null
catch e
null

$Cypress.extend
highlightAttr: "data-cypress-el"

## careful renaming or removing this method, the runner depends on it
getStylesString: ->
reduceText @cy.private("document").styleSheets, (stylesheet) ->
## some browsers may throw a SecurityError if the stylesheet is cross-domain
## https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet#Notes
## for others, it will just be null
try
reduceText stylesheet.cssRules, (rule) ->
rule.cssText
catch e
return ""

createSnapshot: ($el) ->
## create a unique selector for this el
$el.attr(@highlightAttr, true) if $el?.attr
Expand All @@ -26,19 +26,18 @@ do ($Cypress, _) ->

body = @cy.$$("body").clone()

## extract all CSS into a string
styles = @getStylesString()
## for the head and body, get an array of all CSS,
## whether it's links or style tags
## if it's same-origin, it will get the actual styles as a string
## it it's cross-domain, it will get a reference to the link's href
{headStyles, bodyStyles} = @getStyles()

## replaces iframes with placeholders
@_replaceIframes(body)

## remove tags we don't want in body
body.find("script,link[rel='stylesheet'],style").remove()

## put all the extracted CSS in the body, because that's all
## that gets swapped out when restoring a snapshot
body.append("<style>#{styles}</style>")

## here we need to figure out if we're in a remote manual environment
## if so we need to stringify the DOM:
## 1. grab all inputs / textareas / options and set their value on the element
Expand All @@ -58,7 +57,33 @@ do ($Cypress, _) ->
## preserve classes on the <html> tag
htmlClasses = @cy.$$("html")[0].className

return {body, htmlClasses}
return {body, htmlClasses, headStyles, bodyStyles}

## careful renaming or removing this method, the runner depends on it
getStyles: ->
@_stylesheets = @_indexedStylesheets()

return {
headStyles: @_getStylesFor("head")
bodyStyles: @_getStylesFor("body")
}

_getStylesFor: (location) ->
_.map @cy.$$(location).find("link[rel='stylesheet'],style"), (stylesheet) =>
if stylesheet.href
## if there's an href, it's a link tag
## return the CSS rules as a string, or, if cross-domain,
## a reference to the stylesheet's href
getCssRulesString(@_stylesheets[stylesheet.href]) or {href: stylesheet.href}
else
## otherwise, it's a style tag, and we can just grab its content
@cy.$$(stylesheet).text()

_indexedStylesheets: ->
_.reduce @cy.private("document").styleSheets, (memo, stylesheet) ->
memo[stylesheet.href] = stylesheet
return memo
, {}

_replaceIframes: (body) ->
## remove iframes because we don't want extra requests made, JS run, etc
Expand Down
61 changes: 53 additions & 8 deletions spec/client/driver/cy/snapshot_spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,66 @@ describe "$Cypress.Cy Snapshot Extension", ->
{body} = @Cypress.createSnapshot(@el)
expect(body.find("link")).not.to.exist

it "clones styles from head and body into single <style /> in body", ->
$("<style>body { background: red; }</style>").appendTo(@cy.$$("head"))
$("<style>body { color: blue }</style>").appendTo(@cy.$$("body"))
it "does not clone style tags", ->
$("<style>.foo { color: blue }</style>").appendTo(@cy.$$("body"))

style = @Cypress.createSnapshot(@el).body.find("style")
expect(style.length).to.equal(1)
expect(style.text()).to.contain("body { background: red; }")
expect(style.text()).to.contain("body { color: blue; }")
{body} = @Cypress.createSnapshot(@el)
expect(body.find("style")).not.to.exist

it "preserves styles on the <html> tag", ->
it "preserves classes on the <html> tag", ->
@cy.$$("html").addClass("foo bar")

{htmlClasses} = @Cypress.createSnapshot(@el)
expect(htmlClasses).to.equal("foo bar")

it "provides contents of style tags in head", ->
$("<style>.foo { color: red }</style>").appendTo(@cy.$$("head"))

{headStyles} = @Cypress.createSnapshot(@el)
expect(headStyles[0]).to.include(".foo { color: red }")

it "provides contents of local stylesheet links in head", (done) ->
$("<link rel='stylesheet' href='generic_styles.css' />").appendTo(@cy.$$("head"))

setTimeout ->
## need to wait a tick for appended stylesheet to take affect
{headStyles} = @Cypress.createSnapshot(@el)
expect(headStyles[0]).to.include(".foo { color: green; }")
done()

it "provides object with href of external stylesheets in head", (done) ->
$("<link rel='stylesheet' href='http://localhost:3501/fixtures/html/generic_styles.css' />").appendTo(@cy.$$("head"))

setTimeout ->
## need to wait a tick for appended stylesheet to take affect
{headStyles} = @Cypress.createSnapshot(@el)
expect(headStyles[0]).to.eql({href: "http://localhost:3501/fixtures/html/generic_styles.css"})
done()

it "provides contents of style tags in body", ->
$("<style>.foo { color: red }</style>").appendTo(@cy.$$("body"))

{bodyStyles} = @Cypress.createSnapshot(@el)
expect(bodyStyles[bodyStyles.length - 1]).to.include(".foo { color: red }")

it "provides contents of local stylesheet links in body", (done) ->
$("<link rel='stylesheet' href='generic_styles.css' />").appendTo(@cy.$$("body"))

setTimeout ->
## need to wait a tick for appended stylesheet to take affect
{bodyStyles} = @Cypress.createSnapshot(@el)
expect(bodyStyles[bodyStyles.length - 1]).to.include(".foo { color: green; }")
done()

it "provides object with href of external stylesheets in body", (done) ->
$("<link rel='stylesheet' href='http://localhost:3501/fixtures/html/generic_styles.css' />").appendTo(@cy.$$("body"))

setTimeout ->
## need to wait a tick for appended stylesheet to take affect
{bodyStyles} = @Cypress.createSnapshot(@el)
expect(bodyStyles[bodyStyles.length - 1]).to.eql({href: "http://localhost:3501/fixtures/html/generic_styles.css"})
done()

it "sets data-cypress-el attr", ->
attr = @sandbox.spy @el, "attr"
@Cypress.createSnapshot(@el)
Expand Down
8 changes: 4 additions & 4 deletions spec/client/driver/cypress/log_spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe "$Cypress.Log API", ->
}

it "#snapshot", ->
createSnapshot = @sandbox.stub @Cypress, "createSnapshot"
createSnapshot = @sandbox.stub(@Cypress, "createSnapshot").returns({})

div = $("<div />")
@log.set "$el", div
Expand Down Expand Up @@ -439,7 +439,7 @@ describe "$Cypress.Log API", ->

describe "#constructor", ->
it "snapshots if snapshot attr is true", ->
createSnapshot = @sandbox.stub @Cypress, "createSnapshot"
createSnapshot = @sandbox.stub(@Cypress, "createSnapshot").returns({})

new $Cypress.Log @Cypress, snapshot: true

Expand All @@ -464,7 +464,7 @@ describe "$Cypress.Log API", ->
describe "#wrapConsoleProps", ->
it "automatically adds Command with name", ->
@log.set("name", "foo")
@log.set("snapshots", [{name: null, state: {}}])
@log.set("snapshots", [{name: null, body: {}}])
@log.set("consoleProps", -> {bar: "baz"})
@log.wrapConsoleProps()
expect(@log.attributes.consoleProps()).to.deep.eq {
Expand Down Expand Up @@ -823,4 +823,4 @@ describe "$Cypress.Log API", ->
}
done()

@cy.get("button").eq(0).contains("asdfasdfasdfasdf")
@cy.get("button").eq(0).contains("asdfasdfasdfasdf")
3 changes: 3 additions & 0 deletions spec/fixtures/html/generic_styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.foo {
color: green;
}

0 comments on commit 40e2281

Please sign in to comment.