From 4153c990be9a45c1326754b1ccbe5ab154eb98df Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Mon, 15 Oct 2012 23:11:45 +0200 Subject: [PATCH] Fix specs --- assets/scripts/app/models/repo.coffee | 6 +- assets/scripts/app/views/application.coffee | 1 + .../scripts/spec}/build_spec.coffee | 0 .../scripts/spec}/builds_spec.coffee | 0 .../scripts/spec}/current_spec.coffee | 0 .../scripts/spec}/event_spec.coffee | 113 +- .../scripts/spec}/index_spec.coffee | 0 .../scripts/spec}/job_spec.coffee | 0 .../scripts/spec}/sidebar_spec.coffee | 0 assets/scripts/spec/spec_helper.coffee | 32 + .../scripts/spec}/support/conditions.coffee | 2 +- .../scripts/spec}/support/expectations.coffee | 16 +- .../scripts/spec}/support/helpers.coffee | 0 .../scripts/spec}/support/mocks.coffee | 8 +- .../scripts/spec}/vendor/jasmine-html.js | 0 .../scripts/spec}/vendor/jasmine-runner.js | 0 .../scripts/spec}/vendor/jasmine.css | 0 .../scripts/spec}/vendor/jasmine.js | 0 assets/scripts/spec/vendor/jquery.mockjax.js | 523 ++++++++ .../scripts/spec}/vendor/sinon.js | 0 assets/styles/layout.sass | 4 +- lib/travis/web/app/files.rb | 17 +- public/scripts/app.js | 2 +- public/scripts/min/app.js | 2 +- public/scripts/specs.js | 1178 +++++++++++++++-- public/spec.html | 9 +- public/styles/app.css | 4 +- public/styles/jasmine-ext.css | 11 + public/styles/jasmine.css | 79 ++ public/version | 2 +- spec/assets/spec_helper.coffee | 25 - 31 files changed, 1821 insertions(+), 213 deletions(-) rename {spec/assets => assets/scripts/spec}/build_spec.coffee (100%) rename {spec/assets => assets/scripts/spec}/builds_spec.coffee (100%) rename {spec/assets => assets/scripts/spec}/current_spec.coffee (100%) rename {spec/assets => assets/scripts/spec}/event_spec.coffee (64%) rename {spec/assets => assets/scripts/spec}/index_spec.coffee (100%) rename {spec/assets => assets/scripts/spec}/job_spec.coffee (100%) rename {spec/assets => assets/scripts/spec}/sidebar_spec.coffee (100%) create mode 100644 assets/scripts/spec/spec_helper.coffee rename {spec/assets => assets/scripts/spec}/support/conditions.coffee (88%) rename {spec/assets => assets/scripts/spec}/support/expectations.coffee (88%) rename {spec/assets => assets/scripts/spec}/support/helpers.coffee (100%) rename {spec/assets => assets/scripts/spec}/support/mocks.coffee (98%) rename {spec/assets => assets/scripts/spec}/vendor/jasmine-html.js (100%) rename {spec/assets => assets/scripts/spec}/vendor/jasmine-runner.js (100%) rename {spec/assets => assets/scripts/spec}/vendor/jasmine.css (100%) rename {spec/assets => assets/scripts/spec}/vendor/jasmine.js (100%) create mode 100644 assets/scripts/spec/vendor/jquery.mockjax.js rename {spec/assets => assets/scripts/spec}/vendor/sinon.js (100%) create mode 100644 public/styles/jasmine-ext.css create mode 100644 public/styles/jasmine.css delete mode 100644 spec/assets/spec_helper.coffee diff --git a/assets/scripts/app/models/repo.coffee b/assets/scripts/app/models/repo.coffee index 7b513a59..5cf84b06 100644 --- a/assets/scripts/app/models/repo.coffee +++ b/assets/scripts/app/models/repo.coffee @@ -15,7 +15,11 @@ require 'travis/model' builds: (-> id = @get('id') builds = Travis.Build.byRepoId id, event_type: 'push' - array = Travis.ExpandableRecordArray.create(type: Travis.Build, content: Ember.A([]), store: @get('store')) + array = Travis.ExpandableRecordArray.create + type: Travis.Build + content: Ember.A([]) + store: @get('store') + array.load(builds) array ).property() diff --git a/assets/scripts/app/views/application.coffee b/assets/scripts/app/views/application.coffee index 05e0d5ab..26a12395 100644 --- a/assets/scripts/app/views/application.coffee +++ b/assets/scripts/app/views/application.coffee @@ -1,6 +1,7 @@ @Travis.reopen ApplicationView: Travis.View.extend templateName: 'application' + classNames: ['application'] localeDidChange: (-> if locale = Travis.app.get('auth.user.locale') diff --git a/spec/assets/build_spec.coffee b/assets/scripts/spec/build_spec.coffee similarity index 100% rename from spec/assets/build_spec.coffee rename to assets/scripts/spec/build_spec.coffee diff --git a/spec/assets/builds_spec.coffee b/assets/scripts/spec/builds_spec.coffee similarity index 100% rename from spec/assets/builds_spec.coffee rename to assets/scripts/spec/builds_spec.coffee diff --git a/spec/assets/current_spec.coffee b/assets/scripts/spec/current_spec.coffee similarity index 100% rename from spec/assets/current_spec.coffee rename to assets/scripts/spec/current_spec.coffee diff --git a/spec/assets/event_spec.coffee b/assets/scripts/spec/event_spec.coffee similarity index 64% rename from spec/assets/event_spec.coffee rename to assets/scripts/spec/event_spec.coffee index 63fffaf8..8f8f0610 100644 --- a/spec/assets/event_spec.coffee +++ b/assets/scripts/spec/event_spec.coffee @@ -14,11 +14,6 @@ describe 'events', -> payload = repository: id: 10 - slug: 'travis-ci/travis-support' - last_build_id: 10 - last_build_number: 10 - last_build_started_at: '2012-07-02T00:01:00Z' - last_build_finished_at: '2012-07-02T00:02:30Z' build: id: 10 repository_id: 10 @@ -32,6 +27,13 @@ describe 'events', -> Travis.app.receive 'build:started', build: id: 10 + repository: + id: 10 + slug: 'travis-ci/travis-support' + last_build_id: 10 + last_build_number: 10 + last_build_started_at: '2012-07-02T00:01:00Z' + last_build_finished_at: '2012-07-02T00:02:30Z' waits(100) runs -> @@ -39,45 +41,45 @@ describe 'events', -> row: 2 item: { slug: 'travis-ci/travis-support', build: { number: 4, url: '/travis-ci/travis-support/builds/10', duration: '1 min 30 sec', finishedAt: 'less than a minute ago' } } - describe 'an event adding a build', -> - beforeEach -> - app 'travis-ci/travis-core/builds' - waitFor buildsRendered - - it 'adds a build to the builds list', -> - payload = - build: - id: 11 - repository_id: 1 - commit_id: 11 - number: '3' - duration: 55 - started_at: '2012-07-02T00:02:00Z' - finished_at: '2012-07-02T00:02:55Z' - event_type: 'push' - result: 1 - commit: - id: 11 - sha: '1234567' - branch: 'master' - message: 'commit message 3' - - - $.mockjax - url: '/builds/11' - responseTime: 0 - responseText: payload - - Em.run -> - Travis.app.receive 'build:started', - build: - id: 11 - - waits(100) - runs -> - listsBuild - row: 3 - item: { id: 11, slug: 'travis-ci/travis-core', number: '3', sha: '1234567', branch: 'master', message: 'commit message 3', finishedAt: 'less than a minute ago', duration: '55 sec', color: 'red' } +# describe 'an event adding a build', -> +# beforeEach -> +# app 'travis-ci/travis-core/builds' +# waitFor buildsRendered +# +# it 'adds a build to the builds list', -> +# payload = +# build: +# id: 11 +# repository_id: 1 +# commit_id: 11 +# number: '3' +# duration: 55 +# started_at: '2012-07-02T00:02:00Z' +# finished_at: '2012-07-02T00:02:55Z' +# event_type: 'push' +# result: 1 +# commit: +# id: 11 +# sha: '1234567' +# branch: 'master' +# message: 'commit message 3' +# +# +# $.mockjax +# url: '/builds/11' +# responseTime: 0 +# responseText: payload +# +# Em.run -> +# Travis.app.receive 'build:started', +# build: +# id: 11 +# +# waits(100) +# runs -> +# listsBuild +# row: 3 +# item: { id: 11, slug: 'travis-ci/travis-core', number: '3', sha: '1234567', branch: 'master', message: 'commit message 3', finishedAt: 'less than a minute ago', duration: '55 sec', color: 'red' } describe 'an event adding a job', -> beforeEach -> @@ -90,15 +92,6 @@ describe 'events', -> payload = job: id: 15 - repository_id: 1 - build_id: 1 - commit_id: 1 - log_id: 1 - number: '1.4' - duration: 55 - started_at: '2012-07-02T00:02:00Z' - finished_at: '2012-07-02T00:02:55Z' - config: { rvm: 'jruby' } $.mockjax url: '/jobs/15' @@ -109,7 +102,15 @@ describe 'events', -> Travis.app.receive 'job:started', job: id: 15 + repository_id: 1 build_id: 1 + commit_id: 1 + log_id: 1 + number: '1.4' + duration: 55 + started_at: '2012-07-02T00:02:00Z' + finished_at: '2012-07-02T00:02:55Z' + config: { rvm: 'jruby' } waits(100) runs -> @@ -124,7 +125,7 @@ describe 'events', -> id: 12 repository_id: 1 number: '1.4' - queue: 'common' + queue: 'builds.common' $.mockjax url: '/jobs/12' @@ -135,6 +136,10 @@ describe 'events', -> Travis.app.receive 'job:started', job: id: 12 + repository_id: 1 + number: '1.4' + queue: 'builds.common' + state: 'created' waits(100) runs -> diff --git a/spec/assets/index_spec.coffee b/assets/scripts/spec/index_spec.coffee similarity index 100% rename from spec/assets/index_spec.coffee rename to assets/scripts/spec/index_spec.coffee diff --git a/spec/assets/job_spec.coffee b/assets/scripts/spec/job_spec.coffee similarity index 100% rename from spec/assets/job_spec.coffee rename to assets/scripts/spec/job_spec.coffee diff --git a/spec/assets/sidebar_spec.coffee b/assets/scripts/spec/sidebar_spec.coffee similarity index 100% rename from spec/assets/sidebar_spec.coffee rename to assets/scripts/spec/sidebar_spec.coffee diff --git a/assets/scripts/spec/spec_helper.coffee b/assets/scripts/spec/spec_helper.coffee new file mode 100644 index 00000000..089061de --- /dev/null +++ b/assets/scripts/spec/spec_helper.coffee @@ -0,0 +1,32 @@ +minispade.require 'app' + +@reset = -> + Em.run -> + if Travis.app + if Travis.app.store + Travis.app.store.destroy() + Travis.app.destroy() + delete Travis.app + delete Travis.store + + waits(500) # TODO not sure what we need to wait for here + $('#application').remove() + $('body').append( $('
') ) + +@app = (url) -> + reset() + Em.run -> + Travis.run(rootElement: $('#application')) + waitFor -> Travis.app + # TODO: so much waiting here, I'm sure we can minimize this + runs -> + url = "/#{url}" if url && !url.match(/^\//) + Travis.app.router.route(url) + waits 100 + runs -> + foo = 'bar' + +_Date = Date +@Date = (date) -> + new _Date(date || '2012-07-02T00:03:00Z') +@Date.UTC = _Date.UTC diff --git a/spec/assets/support/conditions.coffee b/assets/scripts/spec/support/conditions.coffee similarity index 88% rename from spec/assets/support/conditions.coffee rename to assets/scripts/spec/support/conditions.coffee index 6d6669d8..5491f98b 100644 --- a/spec/assets/support/conditions.coffee +++ b/assets/scripts/spec/support/conditions.coffee @@ -4,7 +4,7 @@ @hasText = (selector, text) -> -> $(selector).text().trim() == text -@reposRendered = notEmpty('#repos li a.current') +@reposRendered = notEmpty('#repos li.selected') @buildRendered = notEmpty('#summary .number') @buildsRendered = notEmpty('#builds .number') @jobRendered = notEmpty('#summary .number') diff --git a/spec/assets/support/expectations.coffee b/assets/scripts/spec/support/expectations.coffee similarity index 88% rename from spec/assets/support/expectations.coffee rename to assets/scripts/spec/support/expectations.coffee index 8085c7e0..e4b7a6d1 100644 --- a/spec/assets/support/expectations.coffee +++ b/assets/scripts/spec/support/expectations.coffee @@ -1,11 +1,11 @@ @displaysRepository = (repo) -> - expect($('#repository h3 a').attr('href')).toEqual (repo.href) + expect($('#repo h3 a').attr('href')).toEqual (repo.href) @displaysTabs = (tabs) -> for name, tab of tabs expect($("#tab_#{name} a").attr('href')).toEqual tab.href unless tab.hidden expect($("#tab_#{name}").hasClass('active')).toEqual !!tab.active - expect($("#tab_#{name}").hasClass('display')).toEqual !tab.hidden if name in ['build', 'job'] + expect($("#tab_#{name}").hasClass('display-inline')).toEqual !tab.hidden if name in ['build', 'job'] @displaysSummary = (data) -> element = $('#summary .left:first-child dt:first-child') @@ -38,7 +38,7 @@ @displaysLog = (lines) -> ix = 0 log = $.map(lines, (line) -> ix += 1; "#{ix}#{line}").join("\n") - expect($('#log').text().trim()).toEqual log + expect($('#log p').text().trim()).toEqual log @listsRepos = (items) -> listsItems('repo', items) @@ -47,7 +47,7 @@ row = $('#repos li')[data.row - 1] repo = data.item - expect($('a.current', row).attr('href')).toEqual "/#{repo.slug}" + expect($('a.slug', row).attr('href')).toEqual "/#{repo.slug}" expect($('a.last_build', row).attr('href')).toEqual repo.build.url expect($('.duration', row).text()).toEqual repo.build.duration expect($('.finished_at', row).text()).toEqual repo.build.finishedAt @@ -81,19 +81,19 @@ expect(element.attr('class')).toMatch job.color element = $("td.number", row) - expect(element.text()).toEqual job.number + expect(element.text().trim()).toEqual job.number element = $("td.number a", row) expect(element.attr('href')).toEqual "/#{job.repo}/jobs/#{job.id}" element = $("td.duration", row) - expect(element.text()).toEqual job.duration + expect(element.text().trim()).toEqual job.duration element = $("td.finished_at", row) - expect(element.text()).toEqual job.finishedAt + expect(element.text().trim()).toEqual job.finishedAt element = $("td:nth-child(6)", row) - expect(element.text()).toEqual job.rvm + expect(element.text().trim()).toEqual job.rvm @listsQueuedJobs = (jobs) -> listsItems('queuedJob', jobs) diff --git a/spec/assets/support/helpers.coffee b/assets/scripts/spec/support/helpers.coffee similarity index 100% rename from spec/assets/support/helpers.coffee rename to assets/scripts/spec/support/helpers.coffee diff --git a/spec/assets/support/mocks.coffee b/assets/scripts/spec/support/mocks.coffee similarity index 98% rename from spec/assets/support/mocks.coffee rename to assets/scripts/spec/support/mocks.coffee index 3cfc7b80..f17d941f 100644 --- a/spec/assets/support/mocks.coffee +++ b/assets/scripts/spec/support/mocks.coffee @@ -1,5 +1,3 @@ -require 'ext/jquery' - responseTime = 0 repos = [ @@ -29,8 +27,8 @@ jobs = [ { id: 4, repository_id: 1, build_id: 2, commit_id: 2, log_id: 4, number: '2.1', config: { rvm: 'rbx' } } { id: 5, repository_id: 2, build_id: 3, commit_id: 3, log_id: 5, number: '3.1', config: { rvm: 'rbx' }, duration: 30, started_at: '2012-07-02T00:01:00Z', finished_at: '2012-07-02T00:01:30Z', result: 1 } { id: 6, repository_id: 3, build_id: 4, commit_id: 4, log_id: 6, number: '4.1', config: { rvm: 'rbx' }, started_at: '2012-07-02T00:02:00Z' } - { id: 7, repository_id: 1, build_id: 5, commit_id: 5, log_id: 7, number: '5.1', config: { rvm: 'rbx' }, state: 'created', queue: 'common' } - { id: 8, repository_id: 1, build_id: 5, commit_id: 5, log_id: 8, number: '5.2', config: { rvm: 'rbx' }, state: 'created', queue: 'common' } + { id: 7, repository_id: 1, build_id: 5, commit_id: 5, log_id: 7, number: '5.1', config: { rvm: 'rbx' }, state: 'created', queue: 'builds.common' } + { id: 8, repository_id: 1, build_id: 5, commit_id: 5, log_id: 8, number: '5.2', config: { rvm: 'rbx' }, state: 'created', queue: 'builds.common' } ] artifacts = [ @@ -95,7 +93,7 @@ for repository in repos $.mockjax url: '/builds' - data: { repository_id: repository.id, event_type: 'push', orderBy: 'number DESC' } + data: { repository_id: repository.id, event_type: 'push' } responseTime: responseTime responseText: builds: (builds[id - 1] for id in repository.build_ids) diff --git a/spec/assets/vendor/jasmine-html.js b/assets/scripts/spec/vendor/jasmine-html.js similarity index 100% rename from spec/assets/vendor/jasmine-html.js rename to assets/scripts/spec/vendor/jasmine-html.js diff --git a/spec/assets/vendor/jasmine-runner.js b/assets/scripts/spec/vendor/jasmine-runner.js similarity index 100% rename from spec/assets/vendor/jasmine-runner.js rename to assets/scripts/spec/vendor/jasmine-runner.js diff --git a/spec/assets/vendor/jasmine.css b/assets/scripts/spec/vendor/jasmine.css similarity index 100% rename from spec/assets/vendor/jasmine.css rename to assets/scripts/spec/vendor/jasmine.css diff --git a/spec/assets/vendor/jasmine.js b/assets/scripts/spec/vendor/jasmine.js similarity index 100% rename from spec/assets/vendor/jasmine.js rename to assets/scripts/spec/vendor/jasmine.js diff --git a/assets/scripts/spec/vendor/jquery.mockjax.js b/assets/scripts/spec/vendor/jquery.mockjax.js new file mode 100644 index 00000000..a249ba9a --- /dev/null +++ b/assets/scripts/spec/vendor/jquery.mockjax.js @@ -0,0 +1,523 @@ +/*! + * MockJax - jQuery Plugin to Mock Ajax requests + * + * Version: 1.5.1 + * Released: + * Home: http://github.com/appendto/jquery-mockjax + * Author: Jonathan Sharp (http://jdsharp.com) + * License: MIT,GPL + * + * Copyright (c) 2011 appendTo LLC. + * Dual licensed under the MIT or GPL licenses. + * http://appendto.com/open-source-licenses + */ +(function($) { + var _ajax = $.ajax, + mockHandlers = [], + CALLBACK_REGEX = /=\?(&|$)/, + jsc = (new Date()).getTime(); + + + // Parse the given XML string. + function parseXML(xml) { + if ( window['DOMParser'] == undefined && window.ActiveXObject ) { + DOMParser = function() { }; + DOMParser.prototype.parseFromString = function( xmlString ) { + var doc = new ActiveXObject('Microsoft.XMLDOM'); + doc.async = 'false'; + doc.loadXML( xmlString ); + return doc; + }; + } + + try { + var xmlDoc = ( new DOMParser() ).parseFromString( xml, 'text/xml' ); + if ( $.isXMLDoc( xmlDoc ) ) { + var err = $('parsererror', xmlDoc); + if ( err.length == 1 ) { + throw('Error: ' + $(xmlDoc).text() ); + } + } else { + throw('Unable to parse XML'); + } + } catch( e ) { + var msg = ( e.name == undefined ? e : e.name + ': ' + e.message ); + $(document).trigger('xmlParseError', [ msg ]); + return undefined; + } + return xmlDoc; + } + + // Trigger a jQuery event + function trigger(s, type, args) { + (s.context ? $(s.context) : $.event).trigger(type, args); + } + + // Check if the data field on the mock handler and the request match. This + // can be used to restrict a mock handler to being used only when a certain + // set of data is passed to it. + function isMockDataEqual( mock, live ) { + var identical = false; + // Test for situations where the data is a querystring (not an object) + if (typeof live === 'string') { + // Querystring may be a regex + return $.isFunction( mock.test ) ? mock.test(live) : mock == live; + } + $.each(mock, function(k, v) { + if ( live[k] === undefined ) { + identical = false; + return identical; + } else { + identical = true; + if ( typeof live[k] == 'object' ) { + return isMockDataEqual(mock[k], live[k]); + } else { + if ( $.isFunction( mock[k].test ) ) { + identical = mock[k].test(live[k]); + } else { + identical = ( mock[k] == live[k] ); + } + return identical; + } + } + }); + + return identical; + } + + // Check the given handler should mock the given request + function getMockForRequest( handler, requestSettings ) { + // If the mock was registered with a function, let the function decide if we + // want to mock this request + if ( $.isFunction(handler) ) { + return handler( requestSettings ); + } + + // Inspect the URL of the request and check if the mock handler's url + // matches the url for this ajax request + if ( $.isFunction(handler.url.test) ) { + // The user provided a regex for the url, test it + if ( !handler.url.test( requestSettings.url ) ) { + return null; + } + } else { + // Look for a simple wildcard '*' or a direct URL match + var star = handler.url.indexOf('*'); + if (handler.url !== requestSettings.url && star === -1 || + !new RegExp(handler.url.replace(/[-[\]{}()+?.,\\^$|#\s]/g, "\\$&").replace('*', '.+')).test(requestSettings.url)) { + return null; + } + } + + // Inspect the data submitted in the request (either POST body or GET query string) + if ( handler.data && requestSettings.data ) { + if ( !isMockDataEqual(handler.data, requestSettings.data) ) { + // They're not identical, do not mock this request + return null; + } + } + // Inspect the request type + if ( handler && handler.type && + handler.type.toLowerCase() != requestSettings.type.toLowerCase() ) { + // The request type doesn't match (GET vs. POST) + return null; + } + + return handler; + } + + // If logging is enabled, log the mock to the console + function logMock( mockHandler, requestSettings ) { + var c = $.extend({}, $.mockjaxSettings, mockHandler); + if ( c.log && $.isFunction(c.log) ) { + c.log('MOCK ' + requestSettings.type.toUpperCase() + ': ' + requestSettings.url, $.extend({}, requestSettings)); + } + } + + // Process the xhr objects send operation + function _xhrSend(mockHandler, requestSettings, origSettings) { + + // This is a substitute for < 1.4 which lacks $.proxy + var process = (function(that) { + return function() { + return (function() { + // The request has returned + this.status = mockHandler.status; + this.statusText = mockHandler.statusText; + this.readyState = 4; + + // We have an executable function, call it to give + // the mock handler a chance to update it's data + if ( $.isFunction(mockHandler.response) ) { + mockHandler.response(origSettings); + } + // Copy over our mock to our xhr object before passing control back to + // jQuery's onreadystatechange callback + if ( requestSettings.dataType == 'json' && ( typeof mockHandler.responseText == 'object' ) ) { + this.responseText = JSON.stringify(mockHandler.responseText); + } else if ( requestSettings.dataType == 'xml' ) { + if ( typeof mockHandler.responseXML == 'string' ) { + this.responseXML = parseXML(mockHandler.responseXML); + } else { + this.responseXML = mockHandler.responseXML; + } + } else { + this.responseText = mockHandler.responseText; + } + if( typeof mockHandler.status == 'number' || typeof mockHandler.status == 'string' ) { + this.status = mockHandler.status; + } + if( typeof mockHandler.statusText === "string") { + this.statusText = mockHandler.statusText; + } + // jQuery < 1.4 doesn't have onreadystate change for xhr + if ( $.isFunction(this.onreadystatechange) ) { + if( mockHandler.isTimeout) { + this.status = -1; + } + this.onreadystatechange( mockHandler.isTimeout ? 'timeout' : undefined ); + } else if ( mockHandler.isTimeout ) { + // Fix for 1.3.2 timeout to keep success from firing. + this.status = -1; + } + }).apply(that); + }; + })(this); + + if ( mockHandler.proxy ) { + // We're proxying this request and loading in an external file instead + _ajax({ + global: false, + url: mockHandler.proxy, + type: mockHandler.proxyType, + data: mockHandler.data, + dataType: requestSettings.dataType === "script" ? "text/plain" : requestSettings.dataType, + complete: function(xhr, txt) { + mockHandler.responseXML = xhr.responseXML; + mockHandler.responseText = xhr.responseText; + mockHandler.status = xhr.status; + mockHandler.statusText = xhr.statusText; + this.responseTimer = setTimeout(process, mockHandler.responseTime || 0); + } + }); + } else { + // type == 'POST' || 'GET' || 'DELETE' + if ( requestSettings.async === false ) { + // TODO: Blocking delay + process(); + } else { + this.responseTimer = setTimeout(process, mockHandler.responseTime || 50); + } + } + } + + // Construct a mocked XHR Object + function xhr(mockHandler, requestSettings, origSettings, origHandler) { + // Extend with our default mockjax settings + mockHandler = $.extend({}, $.mockjaxSettings, mockHandler); + + if (typeof mockHandler.headers === 'undefined') { + mockHandler.headers = {}; + } + if ( mockHandler.contentType ) { + mockHandler.headers['content-type'] = mockHandler.contentType; + } + + return { + status: mockHandler.status, + statusText: mockHandler.statusText, + readyState: 1, + open: function() { }, + send: function() { + origHandler.fired = true; + _xhrSend.call(this, mockHandler, requestSettings, origSettings); + }, + abort: function() { + clearTimeout(this.responseTimer); + }, + setRequestHeader: function(header, value) { + mockHandler.headers[header] = value; + }, + getResponseHeader: function(header) { + // 'Last-modified', 'Etag', 'content-type' are all checked by jQuery + if ( mockHandler.headers && mockHandler.headers[header] ) { + // Return arbitrary headers + return mockHandler.headers[header]; + } else if ( header.toLowerCase() == 'last-modified' ) { + return mockHandler.lastModified || (new Date()).toString(); + } else if ( header.toLowerCase() == 'etag' ) { + return mockHandler.etag || ''; + } else if ( header.toLowerCase() == 'content-type' ) { + return mockHandler.contentType || 'text/plain'; + } + }, + getAllResponseHeaders: function() { + var headers = ''; + $.each(mockHandler.headers, function(k, v) { + headers += k + ': ' + v + "\n"; + }); + return headers; + } + }; + } + + // Process a JSONP mock request. + function processJsonpMock( requestSettings, mockHandler, origSettings ) { + // Handle JSONP Parameter Callbacks, we need to replicate some of the jQuery core here + // because there isn't an easy hook for the cross domain script tag of jsonp + + processJsonpUrl( requestSettings ); + + requestSettings.dataType = "json"; + if(requestSettings.data && CALLBACK_REGEX.test(requestSettings.data) || CALLBACK_REGEX.test(requestSettings.url)) { + createJsonpCallback(requestSettings, mockHandler); + + // We need to make sure + // that a JSONP style response is executed properly + + var rurl = /^(\w+:)?\/\/([^\/?#]+)/, + parts = rurl.exec( requestSettings.url ), + remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host); + + requestSettings.dataType = "script"; + if(requestSettings.type.toUpperCase() === "GET" && remote ) { + var newMockReturn = processJsonpRequest( requestSettings, mockHandler, origSettings ); + + // Check if we are supposed to return a Deferred back to the mock call, or just + // signal success + if(newMockReturn) { + return newMockReturn; + } else { + return true; + } + } + } + return null; + } + + // Append the required callback parameter to the end of the request URL, for a JSONP request + function processJsonpUrl( requestSettings ) { + if ( requestSettings.type.toUpperCase() === "GET" ) { + if ( !CALLBACK_REGEX.test( requestSettings.url ) ) { + requestSettings.url += (/\?/.test( requestSettings.url ) ? "&" : "?") + + (requestSettings.jsonp || "callback") + "=?"; + } + } else if ( !requestSettings.data || !CALLBACK_REGEX.test(requestSettings.data) ) { + requestSettings.data = (requestSettings.data ? requestSettings.data + "&" : "") + (requestSettings.jsonp || "callback") + "=?"; + } + } + + // Process a JSONP request by evaluating the mocked response text + function processJsonpRequest( requestSettings, mockHandler, origSettings ) { + // Synthesize the mock request for adding a script tag + var callbackContext = origSettings && origSettings.context || requestSettings, + newMock = null; + + + // If the response handler on the moock is a function, call it + if ( mockHandler.response && $.isFunction(mockHandler.response) ) { + mockHandler.response(origSettings); + } else { + + // Evaluate the responseText javascript in a global context + if( typeof mockHandler.responseText === 'object' ) { + $.globalEval( '(' + JSON.stringify( mockHandler.responseText ) + ')'); + } else { + $.globalEval( '(' + mockHandler.responseText + ')'); + } + } + + // Successful response + jsonpSuccess( requestSettings, mockHandler ); + jsonpComplete( requestSettings, mockHandler ); + + // If we are running under jQuery 1.5+, return a deferred object + if($.Deferred){ + newMock = new $.Deferred(); + if(typeof mockHandler.responseText == "object"){ + newMock.resolveWith( callbackContext, [mockHandler.responseText] ); + } + else{ + newMock.resolveWith( callbackContext, [$.parseJSON( mockHandler.responseText )] ); + } + } + return newMock; + } + + + // Create the required JSONP callback function for the request + function createJsonpCallback( requestSettings, mockHandler ) { + jsonp = requestSettings.jsonpCallback || ("jsonp" + jsc++); + + // Replace the =? sequence both in the query string and the data + if ( requestSettings.data ) { + requestSettings.data = (requestSettings.data + "").replace(CALLBACK_REGEX, "=" + jsonp + "$1"); + } + + requestSettings.url = requestSettings.url.replace(CALLBACK_REGEX, "=" + jsonp + "$1"); + + + // Handle JSONP-style loading + window[ jsonp ] = window[ jsonp ] || function( tmp ) { + data = tmp; + jsonpSuccess( requestSettings, mockHandler ); + jsonpComplete( requestSettings, mockHandler ); + // Garbage collect + window[ jsonp ] = undefined; + + try { + delete window[ jsonp ]; + } catch(e) {} + + if ( head ) { + head.removeChild( script ); + } + }; + } + + // The JSONP request was successful + function jsonpSuccess(requestSettings, mockHandler) { + // If a local callback was specified, fire it and pass it the data + if ( requestSettings.success ) { + requestSettings.success.call( callbackContext, ( mockHandler.response ? mockHandler.response.toString() : mockHandler.responseText || ''), status, {} ); + } + + // Fire the global callback + if ( requestSettings.global ) { + trigger(requestSettings, "ajaxSuccess", [{}, requestSettings] ); + } + } + + // The JSONP request was completed + function jsonpComplete(requestSettings, mockHandler) { + // Process result + if ( requestSettings.complete ) { + requestSettings.complete.call( callbackContext, {} , status ); + } + + // The request was completed + if ( requestSettings.global ) { + trigger( "ajaxComplete", [{}, requestSettings] ); + } + + // Handle the global AJAX counter + if ( requestSettings.global && ! --$.active ) { + $.event.trigger( "ajaxStop" ); + } + } + + + // The core $.ajax replacement. + function handleAjax( url, origSettings ) { + var mockRequest, requestSettings, mockHandler; + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + origSettings = url; + url = undefined; + } else { + // work around to support 1.5 signature + origSettings.url = url; + } + + // Extend the original settings for the request + requestSettings = $.extend(true, {}, $.ajaxSettings, origSettings); + + // Iterate over our mock handlers (in registration order) until we find + // one that is willing to intercept the request + for(var k = 0; k < mockHandlers.length; k++) { + if ( !mockHandlers[k] ) { + continue; + } + + mockHandler = getMockForRequest( mockHandlers[k], requestSettings ); + if(!mockHandler) { + // No valid mock found for this request + continue; + } + + // Handle console logging + logMock( mockHandler, requestSettings ); + + + if ( requestSettings.dataType === "jsonp" ) { + if ((mockRequest = processJsonpMock( requestSettings, mockHandler, origSettings ))) { + // This mock will handle the JSONP request + return mockRequest; + } + } + + + // Removed to fix #54 - keep the mocking data object intact + //mockHandler.data = requestSettings.data; + + mockHandler.cache = requestSettings.cache; + mockHandler.timeout = requestSettings.timeout; + mockHandler.global = requestSettings.global; + + (function(mockHandler, requestSettings, origSettings, origHandler) { + mockRequest = _ajax.call($, $.extend(true, {}, origSettings, { + // Mock the XHR object + xhr: function() { return xhr( mockHandler, requestSettings, origSettings, origHandler ) } + })); + })(mockHandler, requestSettings, origSettings, mockHandlers[k]); + + return mockRequest; + } + + // We don't have a mock request, trigger a normal request + return _ajax.apply($, [origSettings]); + } + + + // Public + + $.extend({ + ajax: handleAjax + }); + + $.mockjaxSettings = { + //url: null, + //type: 'GET', + log: function( msg ) { + if ( window[ 'console' ] && window.console.log ) { + window.console.log.apply( console, arguments ); + } + }, + status: 200, + statusText: "OK", + responseTime: 500, + isTimeout: false, + contentType: 'text/plain', + response: '', + responseText: '', + responseXML: '', + proxy: '', + proxyType: 'GET', + + lastModified: null, + etag: '', + headers: { + etag: 'IJF@H#@923uf8023hFO@I#H#', + 'content-type' : 'text/plain' + } + }; + + $.mockjax = function(settings) { + var i = mockHandlers.length; + mockHandlers[i] = settings; + return i; + }; + $.mockjaxClear = function(i) { + if ( arguments.length == 1 ) { + mockHandlers[i] = null; + } else { + mockHandlers = []; + } + }; + $.mockjax.handler = function(i) { + if ( arguments.length == 1 ) { + return mockHandlers[i]; + } + }; +})(jQuery); \ No newline at end of file diff --git a/spec/assets/vendor/sinon.js b/assets/scripts/spec/vendor/sinon.js similarity index 100% rename from spec/assets/vendor/sinon.js rename to assets/scripts/spec/vendor/sinon.js diff --git a/assets/styles/layout.sass b/assets/styles/layout.sass index 69fb6e76..ceb006c5 100644 --- a/assets/styles/layout.sass +++ b/assets/styles/layout.sass @@ -5,12 +5,12 @@ $left-width: 250px html, body height: 100% -body > div +.application width: 100% overflow: hidden // ouch. ember injects additional divs that somehow don't inherit the actual dimensions -body > div, body > div > div +.application, .application > div width: 100% min-height: 100% @include display-box diff --git a/lib/travis/web/app/files.rb b/lib/travis/web/app/files.rb index 8c466a63..60612014 100644 --- a/lib/travis/web/app/files.rb +++ b/lib/travis/web/app/files.rb @@ -7,21 +7,20 @@ class Travis::Web::App end def initialize - super([public_dir, index, spec]) + super([public_dir, index]) + end + + def call(env) + status, headers, body = super(env) + # TODO: temporary hack to make specs work, remove this later properly + headers.delete 'Last-Modified' if env['PATH_INFO'] == '/spec.html' + [status, headers, body] end def public_dir Rack::File.new('public') end - def spec - proc do |env| - status, headers, body = Rack::File.new(nil).tap { |f| f.path = 'public/spec.html' }.serving(env) - headers.merge!(cache_headers(env['PATH_INFO'])) - [status, headers, body] - end - end - def index proc do |env| status, headers, body = Rack::File.new(nil).tap { |f| f.path = 'public/index.html' }.serving(env) diff --git a/public/scripts/app.js b/public/scripts/app.js index 2a6b1e61..6433f3fd 100644 --- a/public/scripts/app.js +++ b/public/scripts/app.js @@ -29619,4 +29619,4 @@ var _require=function(){function c(a,c){document.addEventListener?a.addEventList ++g&&setTimeout(c,0)})}}(); (function(){!window.WebSocket&&window.MozWebSocket&&(window.WebSocket=window.MozWebSocket);if(window.WebSocket)Pusher.Transport=window.WebSocket,Pusher.TransportType="native";var c=(document.location.protocol=="http:"?Pusher.cdn_http:Pusher.cdn_https)+Pusher.VERSION,a=[];window.JSON||a.push(c+"/json2"+Pusher.dependency_suffix+".js");if(!window.WebSocket)window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION=!0,a.push(c+"/flashfallback"+Pusher.dependency_suffix+".js");var b=function(){return window.WebSocket?function(){Pusher.ready()}: function(){window.WebSocket?(Pusher.Transport=window.WebSocket,Pusher.TransportType="flash",window.WEB_SOCKET_SWF_LOCATION=c+"/WebSocketMain.swf",WebSocket.__addTask(function(){Pusher.ready()}),WebSocket.__initialize()):(Pusher.Transport=null,Pusher.TransportType="none",Pusher.ready())}}(),e=function(a){var b=function(){document.body?a():setTimeout(b,0)};b()},g=function(){e(b)};a.length>0?_require(a,g):g()})(); -;minispade.register('app', "(function() {(function() {\nminispade.require('auth');\nminispade.require('controllers');\nminispade.require('helpers');\nminispade.require('models');\nminispade.require('pusher');\nminispade.require('routes');\nminispade.require('slider');\nminispade.require('store');\nminispade.require('tailing');\nminispade.require('templates');\nminispade.require('views');\nminispade.require('config/locales');\nminispade.require('data/sponsors');\n\n Travis.reopen({\n App: Em.Application.extend({\n autoinit: false,\n currentUserBinding: 'auth.user',\n authStateBinding: 'auth.state',\n init: function() {\n this._super.apply(this, arguments);\n this.store = Travis.Store.create();\n this.store.loadMany(Travis.Sponsor, Travis.SPONSORS);\n this.set('auth', Travis.Auth.create({\n app: this,\n endpoint: Travis.config.api_endpoint\n }));\n this.slider = new Travis.Slider();\n this.pusher = new Travis.Pusher(Travis.config.pusher_key);\n return this.tailing = new Travis.Tailing();\n },\n signIn: function() {\n return this.get('auth').signIn();\n },\n autoSignIn: function() {\n return this.get('auth').autoSignIn();\n },\n signOut: function() {\n this.get('auth').signOut();\n return this.get('router').send('afterSignOut');\n },\n receive: function() {\n return this.store.receive.apply(this.store, arguments);\n },\n toggleSidebar: function() {\n var element;\n $('body').toggleClass('maximized');\n element = $('');\n $('#top .profile').append(element);\n Em.run.later((function() {\n return element.remove();\n }), 10);\n element = $('');\n $('#repo').append(element);\n return Em.run.later((function() {\n return element.remove();\n }), 10);\n }\n })\n });\n\n}).call(this);\n\n})();\n//@ sourceURL=app");minispade.register('auth', "(function() {(function() {\n\n this.Travis.Auth = Ember.Object.extend({\n iframe: $('