diff --git a/spec/javascripts/helpers/SpecHelper.js b/spec/javascripts/helpers/SpecHelper.js index d6e1fd836968700dedbc59e26e8567f584dfe242..f7aa903333bd35cc8a69fc7264cecfe6bbdabf24 100644 --- a/spec/javascripts/helpers/SpecHelper.js +++ b/spec/javascripts/helpers/SpecHelper.js @@ -8,3 +8,79 @@ // } // }) //}); + +beforeEach(function() { + $('#jasmine_content').empty(); + spec.clearLiveEventBindings(); + jasmine.Clock.useMock(); +}); + +afterEach(function() { + spec.clearLiveEventBindings(); + expect(spec.loadFixtureCount).toBeLessThan(2); + spec.loadFixtureCount = 0; +}); + +var context = describe; +var spec = {}; + +spec.clearLiveEventBindings = function() { + var events = jQuery.data(document, "events"); + for (prop in events) { + delete events[prop]; + } +}; + +spec.content = function() { + return $('#jasmine_content'); +}; + +// Loads fixure markup into the DOM as a child of the jasmine_content div +spec.loadFixture = function(fixtureName) { + var $destination = $('#jasmine_content'); + + // get the markup, inject it into the dom + $destination.html(spec.fixtureHtml(fixtureName)); + + // keep track of fixture count to fail specs that + // call loadFixture() more than once + spec.loadFixtureCount++; +}; + +// Returns fixture markup as a string. Useful for fixtures that +// represent the response text of ajax requests. +spec.readFixture = function(fixtureName) { + return spec.fixtureHtml(fixtureName); +}; + +spec.fixtureHtml = function(fixtureName) { + if (!spec.cachedFixtures[fixtureName]) { + spec.cachedFixtures[fixtureName] = spec.retrieveFixture(fixtureName); + } + return spec.cachedFixtures[fixtureName]; +}; + +spec.retrieveFixture = function(fixtureName) { + + // construct a path to the fixture, including a cache-busting timestamp + var path = '/tmp/js_dom_fixtures/' + fixtureName + ".fixture.html?" + new Date().getTime(); + var xhr; + + // retrieve the fixture markup via xhr request to jasmine server + try { + xhr = new jasmine.XmlHttpRequest(); + xhr.open("GET", path, false); + xhr.send(null); + } catch(e) { + throw new Error("couldn't fetch " + path + ": " + e); + } + var regExp = new RegExp(/Couldn\\\'t load \/fixture/); + if (regExp.test(xhr.responseText)) { + throw new Error("Couldn't load fixture with key: '" + fixtureName + "'. No such file: '" + path + "'."); + } + + return xhr.responseText; +}; + +spec.loadFixtureCount = 0; +spec.cachedFixtures = {}; \ No newline at end of file diff --git a/spec/support/fixture_generation.rb b/spec/support/fixture_generation.rb new file mode 100644 index 0000000000000000000000000000000000000000..57b7c4a1642319076ac61418abbe984db2462987 --- /dev/null +++ b/spec/support/fixture_generation.rb @@ -0,0 +1,43 @@ +Rspec::Rails::ControllerExampleGroup.class_eval do + + # Saves the markup to a fixture file using the given name + def save_fixture(markup, name) + fixture_path = File.join(Rails.root, 'tmp', 'js_dom_fixtures') + Dir.mkdir(fixture_path) unless File.exists?(fixture_path) + + fixture_file = File.join(fixture_path, "#{name}.fixture.html") + File.open(fixture_file, 'w') do |file| + file.puts(markup) + end + end + + # From the controller spec response body, extracts html identified + # by the css selector. + def html_for(selector) + doc = Nokogiri::HTML(response.body) + + remove_third_party_scripts(doc) + content = doc.css(selector).first.to_s + + return convert_body_tag_to_div(content) + end + + # Remove scripts such as Google Analytics to avoid running them + # when we load into the dom during js specs. + def remove_third_party_scripts(doc) + scripts = doc.at('#third-party-scripts') + scripts.remove if scripts + end + + # Many of our css and jQuery selectors rely on a class attribute we + # normally embed in the <body>. For example: + # + # <body class="workspaces show"> + # + # Here we convert the body tag to a div so that we can load it into + # the document running js specs without embedding a <body> within a <body>. + def convert_body_tag_to_div(markup) + return markup.gsub("<body", '<div').gsub("</body>", "</div>") + end +end +