cleanup(specs): Remove space-pen. Goodbye, jQuery!

This commit is contained in:
Ben Gotow 2016-04-26 13:13:35 -07:00
parent 1b62e09f71
commit 7c557c0c8f
12 changed files with 407 additions and 696 deletions

View file

@ -3094,7 +3094,6 @@
2998273: node_modules/theorist/lib/sequence.js
3004878: node_modules/coffeestack/index.js
3010047: src/window-event-handler.js
3033233: node_modules/space-pen/lib/space-pen.js
3054355: src/styles-element.js
1995661: src/flux/models/utils.js
3061775: src/flux/errors.js
@ -4562,7 +4561,6 @@
2998273: node_modules/theorist/lib/sequence.js
3004878: node_modules/coffeestack/index.js
3010047: src/window-event-handler.js
3033233: node_modules/space-pen/lib/space-pen.js
3054355: src/styles-element.js
1995661: src/flux/models/utils.js
3061775: src/flux/errors.js

View file

@ -8,10 +8,6 @@ _ = require 'underscore'
donna = require 'donna'
tello = require 'tello'
moduleBlacklist = [
'space-pen'
]
module.exports = (grunt) ->
{cp, mkdir, rm} = require('./task-helpers')(grunt)
@ -23,7 +19,6 @@ module.exports = (grunt) ->
return false if modulePath.match(/node_modules/g)
# Don't traverse blacklisted packages (that have docs, but we don't want to include)
return false if path.basename(modulePath) in moduleBlacklist
return true unless path.basename(modulePath) is 'package.json'
return true unless fs.isFileSync(modulePath)

View file

@ -67,7 +67,6 @@
"season": "^5.1",
"semver": "^4.2",
"source-map-support": "^0.3.2",
"space-pen": "3.8.2",
"spellchecker": "3.2.3",
"temp": "^0.8",
"underscore": "^1.8",

View file

@ -2,8 +2,6 @@ fs = require 'fs'
{remote} = require 'electron'
module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
{$, $$} = require '../src/space-pen-extensions'
window[key] = value for key, value of require './jasmine'
{TerminalReporter} = require 'jasmine-tagged'
@ -21,8 +19,7 @@ module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
remote.process.stdout.write(str)
if NylasEnv.getLoadSettings().showSpecsInWindow
N1SpecReporter = require './n1-spec-reporter'
reporter = new N1SpecReporter()
reporter = require './n1-spec-reporter'
else if NylasEnv.getLoadSettings().exitWhenDone
reporter = new TerminalReporter
color: true
@ -38,8 +35,7 @@ module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
else
NylasEnv.exit(0)
else
N1SpecReporter = require './n1-spec-reporter'
reporter = new N1SpecReporter()
reporter = require './n1-spec-reporter'
NylasEnv.initialize()
@ -50,7 +46,9 @@ module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
jasmineEnv.addReporter(timeReporter)
jasmineEnv.setIncludedTags([process.platform])
$('body').append $$ -> @div id: 'jasmine-content'
div = document.createElement('div')
div.id = 'jasmine-content'
document.body.appendChild(div)
jasmineEnv.execute()

View file

@ -1,181 +0,0 @@
(function(jQuery) {
jasmine.JQuery = function() {};
jasmine.JQuery.browserTagCaseIndependentHtml = function(html) {
return jQuery('<div/>').append(html).html();
};
jasmine.JQuery.elementToString = function(element) {
return jQuery('<div />').append(element.clone()).html();
};
jasmine.JQuery.matchersClass = {};
(function(namespace) {
var data = {
spiedEvents: {},
handlers: []
};
namespace.events = {
spyOn: function(selector, eventName) {
var handler = function(e) {
data.spiedEvents[[selector, eventName]] = e;
};
jQuery(selector).bind(eventName, handler);
data.handlers.push(handler);
},
wasTriggered: function(selector, eventName) {
return !!(data.spiedEvents[[selector, eventName]]);
},
cleanUp: function() {
data.spiedEvents = {};
data.handlers = [];
}
}
})(jasmine.JQuery);
(function(){
var jQueryMatchers = {
toHaveClass: function(className) {
return this.actual.hasClass(className);
},
toBeVisible: function() {
return this.actual.is(':visible');
},
toBeHidden: function() {
return this.actual.is(':hidden');
},
toBeSelected: function() {
return this.actual.is(':selected');
},
toBeChecked: function() {
return this.actual.is(':checked');
},
toBeEmpty: function() {
return this.actual.is(':empty');
},
toExist: function() {
return this.actual.size() > 0;
},
toHaveAttr: function(attributeName, expectedAttributeValue) {
return hasProperty(this.actual.attr(attributeName), expectedAttributeValue);
},
toHaveId: function(id) {
return this.actual.attr('id') == id;
},
toHaveHtml: function(html) {
return this.actual.html() == jasmine.JQuery.browserTagCaseIndependentHtml(html);
},
toHaveText: function(text) {
if (text && jQuery.isFunction(text.test)) {
return text.test(this.actual.text());
} else {
return this.actual.text() == text;
}
},
toHaveValue: function(value) {
return this.actual.val() == value;
},
toHaveData: function(key, expectedValue) {
return hasProperty(this.actual.data(key), expectedValue);
},
toMatchSelector: function(selector) {
return this.actual.is(selector);
},
toContain: function(selector) {
return this.actual.find(selector).size() > 0;
},
toBeDisabled: function(selector){
return this.actual.is(':disabled');
},
// tests the existence of a specific event binding
toHandle: function(eventName) {
var events = this.actual.data("events");
return events && events[eventName].length > 0;
},
// tests the existence of a specific event binding + handler
toHandleWith: function(eventName, eventHandler) {
var stack = this.actual.data("events")[eventName];
var i;
for (i = 0; i < stack.length; i++) {
if (stack[i].handler == eventHandler) {
return true;
}
}
return false;
}
};
var hasProperty = function(actualValue, expectedValue) {
if (expectedValue === undefined) {
return actualValue !== undefined;
}
return actualValue == expectedValue;
};
var bindMatcher = function(methodName) {
var builtInMatcher = jasmine.Matchers.prototype[methodName];
jasmine.JQuery.matchersClass[methodName] = function() {
if (this.actual instanceof HTMLElement) {
this.actual = jQuery(this.actual);
}
if (this.actual && this.actual.jquery) {
var result = jQueryMatchers[methodName].apply(this, arguments);
this.actual = jasmine.JQuery.elementToString(this.actual);
return result;
}
if (builtInMatcher) {
return builtInMatcher.apply(this, arguments);
}
return false;
};
};
for(var methodName in jQueryMatchers) {
bindMatcher(methodName);
}
})();
beforeEach(function() {
this.addMatchers(jasmine.JQuery.matchersClass);
this.addMatchers({
toHaveBeenTriggeredOn: function(selector) {
this.message = function() {
return [
"Expected event " + this.actual + " to have been triggered on" + selector,
"Expected event " + this.actual + " not to have been triggered on" + selector
];
};
return jasmine.JQuery.events.wasTriggered(selector, this.actual);
}
})
});
afterEach(function() {
jasmine.JQuery.events.cleanUp();
});
})(require('../src/space-pen-extensions').jQuery);

315
spec/n1-spec-reporter.cjsx Normal file
View file

@ -0,0 +1,315 @@
path = require 'path'
_ = require 'underscore'
_str = require 'underscore.string'
{convertStackTrace} = require 'coffeestack'
React = require 'react'
ReactDOM = require 'react-dom'
grim = require 'grim'
marked = require 'marked'
sourceMaps = {}
formatStackTrace = (spec, message='', stackTrace, indent="") ->
return stackTrace unless stackTrace
jasminePattern = /^\s*at\s+.*\(?.*[/\\]jasmine(-[^/\\]*)?\.js:\d+:\d+\)?\s*$/
firstJasmineLinePattern = /^\s*at [/\\].*[/\\]jasmine(-[^/\\]*)?\.js:\d+:\d+\)?\s*$/
convertedLines = []
for line in stackTrace.split('\n')
convertedLines.push(line) unless jasminePattern.test(line)
break if firstJasmineLinePattern.test(line)
stackTrace = convertStackTrace(convertedLines.join('\n'), sourceMaps)
lines = stackTrace.split('\n')
# Remove first line of stack when it is the same as the error message
errorMatch = lines[0]?.match(/^Error: (.*)/)
lines.shift() if message.trim() is errorMatch?[1]?.trim()
for line, index in lines
# Remove prefix of lines matching: at [object Object].<anonymous> (path:1:2)
prefixMatch = line.match(/at \[object Object\]\.<anonymous> \(([^)]+)\)/)
line = "at #{prefixMatch[1]}" if prefixMatch
# Relativize locations to spec directory
lines[index] = line.replace("at #{spec.specDirectory}#{path.sep}", 'at ')
lines = lines.map (line) -> indent + line.trim()
lines.join('\n')
indentationString: (suite, plus=0) ->
rootSuite = suite
indentLevel = 0 + plus
while rootSuite.parentSuite
rootSuite = rootSuite.parentSuite
indentLevel += 1
return [0...indentLevel].map(-> " ").join("")
suiteString: (spec) ->
descriptions = [spec.suite.description]
rootSuite = spec.suite
while rootSuite.parentSuite
indent = indentationString(rootSuite)
descriptions.unshift(indent + rootSuite.description)
rootSuite = rootSuite.parentSuite
descriptions.join("\n")
class N1SpecReporter extends React.Component
constructor: (@props) ->
render: ->
<div className="spec-reporter">
<div className="padded pull-right">
<button className="btn reload-button" onClick={@onReloadSpecs}>Reload Specs</button>
</div>
<div className="symbol-area">
<div className="symbol-header">Core</div>
<div className="symbol-summary list-unstyled">{@_renderSpecsOfType('core')}</div>
</div>
<div className="symbol-area">
<div className="symbol-header">Bundled</div>
<div className="symbol-summary list-unstyled">{@_renderSpecsOfType('bundled')}</div>
</div>
<div className="symbol-area">
<div className="symbol-header">User</div>
<div className="symbol-summary list-unstyled">{@_renderSpecsOfType('user')}</div>
</div>
{@_renderStatus()}
<div className="results">
{@_renderFailures()}
</div>
{@_renderDeprecations()}
<div className="plain-text-output">
{@props.plainTextOutput}
</div>
</div>
_renderSpecsOfType: (type) =>
items = []
@props.specs.forEach (spec, idx) =>
return unless spec.specType is type
statusClass = "pending"
title = undefined
results = spec.results()
if results
if results.skipped
statusClass = "skipped"
else if results.failedCount > 0
statusClass = "failed"
title = spec.getFullName()
else if spec.endedAt
statusClass = "passed"
items.push <li key={idx} title={title} className="spec-summary #{statusClass}"/>
items
_renderFailures: =>
# We have an array of specs with `suite` and potentially N `parentSuite` from there.
# Create a tree instead.
topLevelSuites = []
failedSpecs = @props.specs.filter (spec) ->
spec.endedAt and spec.results().failedCount > 0
for spec in failedSpecs
suite = spec.suite
suite = suite.parentSuite while suite.parentSuite
if topLevelSuites.indexOf(suite) is -1
topLevelSuites.push(suite)
topLevelSuites.map (suite, idx) =>
<SuiteResultView suite={suite} key={idx} allSpecs={failedSpecs} />
_renderDeprecations: =>
return if @props.deprecations.length is 0
if @props.deprecations.length is 1
label = "1 deprecation"
else
label = "#{@props.deprecations.length} deprecations"
<div className="status alert alert-warning">
<span>{label}</span>
<div className="deprecation-toggle">
<div className="deprecation-list">
{@_renderDeprecationList()}
</div>
</div>
</div>
_renderDeprecationList: =>
@props.deprecations.map (deprecation) =>
<div className="padded" key={deprecation.message}>
<div className="result-message fail deprecation-message">
{deprecation.message}
{
deprecation.getStacks().map (stack) =>
fullStack = stack.map ({functionName, location}) ->
if functionName is '<unknown>'
" at #{location}"
else
" at #{functionName} (#{location})"
<pre className="stack-trace padded">
{formatStackTrace(deprecation.spec, deprecation.message, fullStack.join('\n'))}
</pre>
}
</div>
</div>
_renderStatus: =>
failedCount = 0
skippedCount = 0
completeCount = 0
for spec in @props.specs
results = spec.results()
continue unless spec.endedAt
failedCount += 1 if results.failedCount > 0
skippedCount += 1 if results.skipped
completeCount += 1 if results.passedCount > 0 and results.failedCount is 0
if failedCount is 1
message = "#{failedCount} failure"
else
message = "#{failedCount} failures"
if skippedCount
specCount = "#{completeCount - skippedCount}/#{@props.specs.length - skippedCount} (#{skippedCount} skipped)"
else
specCount = "#{completeCount}/#{@props.specs.length}"
<div className="status alert alert-info">
<div className="time"></div>
<div className="spec-count">{specCount}</div>
<div className="message">{message}</div>
</div>
onReloadSpecs: =>
require('electron').ipcRenderer.send('call-webcontents-method', 'reload')
class SuiteResultView extends React.Component
@propTypes: ->
suite: React.PropTypes.object
allSpecs: React.PropTypes.array
render: ->
items = []
subsuites = []
@props.allSpecs.forEach (spec) =>
if spec.suite is @props.suite
items.push(spec)
else
suite = spec.suite
while suite.parentSuite
if suite.parentSuite is @props.suite
subsuites.push(suite)
return
suite = suite.parentSuite
items = items.map (spec, idx) =>
<SpecResultView key={idx} spec={spec} />
subsuites = subsuites.map (suite, idx) =>
<SuiteResultView key={idx} suite={suite} allSpecs={@props.allSpecs} />
<div className="suite">
<div className="description">{@props.suite.description}</div>
<div className="results">
{items}
{subsuites}
</div>
</div>
class SpecResultView extends React.Component
@propTypes: ->
spec: React.PropTypes.object
render: ->
description = @props.spec.description
resultItems = @props.spec.results().getItems()
description = "it #{description}" if description.indexOf('it ') isnt 0
failures = []
for result, idx in resultItems
continue if result.passed()
stackTrace = formatStackTrace(@props.spec, result.message, result.trace.stack)
failures.push(
<div key={idx}>
<div className="result-message fail">{result.message}</div>
<div className="stack-trace padded">{stackTrace}</div>
</div>
)
<div className="spec">
<div className="description">{description}</div>
<div className="spec-failures">{failures}</div>
</div>
el = document.createElement('div')
document.body.appendChild(el)
startedAt = null
specs = []
deprecations = []
plainTextOutput = ""
update = =>
component = <N1SpecReporter
startedAt={startedAt}
specs={specs}
deprecations={deprecations}
/>
ReactDOM.render(component, el)
updateSoon = _.debounce(update, 125)
module.exports =
reportRunnerStarting: (runner) ->
specs = runner.specs()
startedAt = Date.now()
updateSoon()
reportRunnerResults: (runner) ->
updateSoon()
reportSuiteResults: (suite) ->
reportSpecResults: (spec) ->
spec.endedAt = Date.now()
specDeprecations = grim.getDeprecations()
d.spec = spec for d in specDeprecations
deprecations = deprecations.concat(specDeprecations)
grim.clearDeprecations()
updateSoon()
reportPlainTextSpecResult: (spec) ->
str = ""
if spec.results().failedCount > 0
str += suiteString(spec) + "\n"
indent = indentationString(spec.suite, 1)
stackIndent = indentationString(spec.suite, 2)
description = spec.description
description = "it #{description}" if description.indexOf('it ') isnt 0
str += indent + description + "\n"
for result in spec.results().getItems()
continue if result.passed()
str += indent + result.message + "\n"
stackTrace = formatStackTrace(spec, result.message, result.trace.stack, stackIndent)
str += stackTrace + "\n"
str += "\n\n"
plainTextOutput = plainTextOutput + str
updateSoon()
reportSpecStarting: (spec) ->
updateSoon()

View file

@ -1,317 +0,0 @@
path = require 'path'
_ = require 'underscore'
_str = require 'underscore.string'
{convertStackTrace} = require 'coffeestack'
{View, $, $$} = require '../src/space-pen-extensions'
grim = require 'grim'
marked = require 'marked'
sourceMaps = {}
formatStackTrace = (spec, message='', stackTrace, indent="") ->
return stackTrace unless stackTrace
jasminePattern = /^\s*at\s+.*\(?.*[/\\]jasmine(-[^/\\]*)?\.js:\d+:\d+\)?\s*$/
firstJasmineLinePattern = /^\s*at [/\\].*[/\\]jasmine(-[^/\\]*)?\.js:\d+:\d+\)?\s*$/
convertedLines = []
for line in stackTrace.split('\n')
convertedLines.push(line) unless jasminePattern.test(line)
break if firstJasmineLinePattern.test(line)
stackTrace = convertStackTrace(convertedLines.join('\n'), sourceMaps)
lines = stackTrace.split('\n')
# Remove first line of stack when it is the same as the error message
errorMatch = lines[0]?.match(/^Error: (.*)/)
lines.shift() if message.trim() is errorMatch?[1]?.trim()
for line, index in lines
# Remove prefix of lines matching: at [object Object].<anonymous> (path:1:2)
prefixMatch = line.match(/at \[object Object\]\.<anonymous> \(([^)]+)\)/)
line = "at #{prefixMatch[1]}" if prefixMatch
# Relativize locations to spec directory
lines[index] = line.replace("at #{spec.specDirectory}#{path.sep}", 'at ')
lines = lines.map (line) -> indent + line.trim()
lines.join('\n')
module.exports =
class N1SpecReporter extends View
@content: ->
@div class: 'spec-reporter', =>
@div class: 'padded pull-right', =>
@button outlet: 'reloadButton', class: 'btn reload-button', 'Reload Specs'
@div outlet: 'coreArea', class: 'symbol-area', =>
@div outlet: 'coreHeader', class: 'symbol-header'
@ul outlet: 'coreSummary', class: 'symbol-summary list-unstyled'
@div outlet: 'bundledArea', class: 'symbol-area', =>
@div outlet: 'bundledHeader', class: 'symbol-header'
@ul outlet: 'bundledSummary', class: 'symbol-summary list-unstyled'
@div outlet: 'userArea', class: 'symbol-area', =>
@div outlet: 'userHeader', class: 'symbol-header'
@ul outlet: 'userSummary', class: 'symbol-summary list-unstyled'
@div outlet: "status", class: 'status alert alert-info', =>
@div outlet: "time", class: 'time'
@div outlet: "specCount", class: 'spec-count'
@div outlet: "message", class: 'message'
@div outlet: "results", class: 'results'
@div outlet: "deprecations", class: 'status alert alert-warning', style: 'display: none', =>
@span outlet: 'deprecationStatus', '0 deprecations'
@div class: 'deprecation-toggle'
@div outlet: 'deprecationList', class: 'deprecation-list'
@pre outlet: "plainTextOutput", class: 'plain-text-output'
startedAt: null
runningSpecCount: 0
completeSpecCount: 0
passedCount: 0
failedCount: 0
skippedCount: 0
totalSpecCount: 0
deprecationCount: 0
@timeoutId: 0
reportRunnerStarting: (runner) ->
@handleEvents()
@startedAt = Date.now()
specs = runner.specs()
@totalSpecCount = specs.length
@addSpecs(specs)
$(document.body).append this
@on 'click', '.stack-trace', ->
$(this).toggleClass('expanded')
@reloadButton.on 'click', -> require('electron').ipcRenderer.send('call-webcontents-method', 'reload')
reportRunnerResults: (runner) ->
@updateSpecCounts()
@status.addClass('alert-success').removeClass('alert-info') if @failedCount is 0
if @failedCount is 1
@message.text "#{@failedCount} failure"
else
@message.text "#{@failedCount} failures"
@status.addClass("specs-complete")
reportSuiteResults: (suite) ->
reportSpecResults: (spec) ->
@completeSpecCount++
spec.endedAt = Date.now()
@specComplete(spec)
@updateStatusView(spec)
@reportPlainTextSpecResult(spec)
reportPlainTextSpecResult: (spec) ->
str = ""
if spec.results().failedCount > 0
str += @suiteString(spec) + "\n"
indent = @indentationString(spec.suite, 1)
stackIndent = @indentationString(spec.suite, 2)
description = spec.description
description = "it #{description}" if description.indexOf('it ') isnt 0
str += indent + description + "\n"
for result in spec.results().getItems()
continue if result.passed()
str += indent + result.message + "\n"
stackTrace = formatStackTrace(spec, result.message, result.trace.stack, stackIndent)
str += stackTrace + "\n"
str += "\n\n"
@plainTextOutput.append(str)
indentationString: (suite, plus=0) ->
rootSuite = suite
indentLevel = 0 + plus
while rootSuite.parentSuite
rootSuite = rootSuite.parentSuite
indentLevel += 1
return [0...indentLevel].map(-> " ").join("")
suiteString: (spec) ->
descriptions = [spec.suite.description]
rootSuite = spec.suite
while rootSuite.parentSuite
indent = @indentationString(rootSuite)
descriptions.unshift(indent + rootSuite.description)
rootSuite = rootSuite.parentSuite
descriptions.join("\n")
reportSpecStarting: (spec) ->
@specStarted(spec)
addDeprecations: (spec) ->
deprecations = grim.getDeprecations()
@deprecationCount += deprecations.length
@deprecations.show() if @deprecationCount > 0
if @deprecationCount is 1
@deprecationStatus.text("1 deprecation")
else
@deprecationStatus.text("#{@deprecationCount} deprecations")
for deprecation in deprecations
@deprecationList.append $$ ->
@div class: 'padded', =>
@div class: 'result-message fail deprecation-message', =>
@raw marked(deprecation.message)
for stack in deprecation.getStacks()
fullStack = stack.map ({functionName, location}) ->
if functionName is '<unknown>'
" at #{location}"
else
" at #{functionName} (#{location})"
@pre class: 'stack-trace padded', formatStackTrace(spec, deprecation.message, fullStack.join('\n'))
grim.clearDeprecations()
handleEvents: ->
$(document).on "click", ".spec-toggle", ({currentTarget}) ->
element = $(currentTarget)
specFailures = element.parent().find('.spec-failures')
specFailures.toggle()
element.toggleClass('folded')
false
$(document).on "click", ".deprecation-toggle", ({currentTarget}) ->
element = $(currentTarget)
deprecationList = $(document).find('.deprecation-list')
deprecationList.toggle()
element.toggleClass('folded')
false
updateSpecCounts: ->
if @skippedCount
specCount = "#{@completeSpecCount - @skippedCount}/#{@totalSpecCount - @skippedCount} (#{@skippedCount} skipped)"
else
specCount = "#{@completeSpecCount}/#{@totalSpecCount}"
@specCount[0].textContent = specCount
updateStatusView: (spec) ->
if @failedCount > 0
@status.addClass('alert-danger').removeClass('alert-info')
@updateSpecCounts()
rootSuite = spec.suite
rootSuite = rootSuite.parentSuite while rootSuite.parentSuite
@message.text rootSuite.description
time = "#{Math.round((spec.endedAt - @startedAt) / 10)}"
time = "0#{time}" if time.length < 3
@time[0].textContent = "#{time[0...-2]}.#{time[-2..]}s"
addSpecs: (specs) ->
coreSpecs = 0
bundledPackageSpecs = 0
userPackageSpecs = 0
for spec in specs
symbol = $$ -> @li id: "spec-summary-#{spec.id}", class: "spec-summary pending"
switch spec.specType
when 'core'
coreSpecs++
@coreSummary.append symbol
when 'bundled'
bundledPackageSpecs++
@bundledSummary.append symbol
when 'user'
userPackageSpecs++
@userSummary.append symbol
if coreSpecs > 0
@coreHeader.text("Core Specs (#{coreSpecs})")
else
@coreArea.hide()
if bundledPackageSpecs > 0
@bundledHeader.text("Bundled Package Specs (#{bundledPackageSpecs})")
else
@bundledArea.hide()
if userPackageSpecs > 0
if coreSpecs is 0 and bundledPackageSpecs is 0
# Package specs being run, show a more descriptive label
{specDirectory} = specs[0]
packageFolderName = path.basename(path.dirname(specDirectory))
packageName = _str.humanize(packageFolderName)
@userHeader.text("#{packageName} Specs")
else
@userHeader.text("User Package Specs (#{userPackageSpecs})")
else
@userArea.hide()
specStarted: (spec) ->
@runningSpecCount++
specComplete: (spec) ->
specSummaryElement = $("#spec-summary-#{spec.id}")
specSummaryElement.removeClass('pending')
specSummaryElement.attr('title', spec.getFullName())
results = spec.results()
if results.skipped
specSummaryElement.addClass("skipped")
@skippedCount++
else if results.passed()
specSummaryElement.addClass("passed")
@passedCount++
else
specSummaryElement.addClass("failed")
specView = new SpecResultView(spec)
specView.attach()
@failedCount++
@addDeprecations(spec)
class SuiteResultView extends View
@content: ->
@div class: 'suite', =>
@div outlet: 'description', class: 'description'
initialize: (@suite) ->
@attr('id', "suite-view-#{@suite.id}")
@description.text(@suite.description)
attach: ->
(@parentSuiteView() or $('.results')).append this
parentSuiteView: ->
return unless @suite.parentSuite
if not suiteView = $("#suite-view-#{@suite.parentSuite.id}").view()
suiteView = new SuiteResultView(@suite.parentSuite)
suiteView.attach()
suiteView
class SpecResultView extends View
@content: ->
@div class: 'spec', =>
@div class: 'spec-toggle'
@div outlet: 'description', class: 'description'
@div outlet: 'specFailures', class: 'spec-failures'
initialize: (@spec) ->
@addClass("spec-view-#{@spec.id}")
description = @spec.description
description = "it #{description}" if description.indexOf('it ') isnt 0
@description.text(description)
for result in @spec.results().getItems() when not result.passed()
stackTrace = formatStackTrace(@spec, result.message, result.trace.stack)
@specFailures.append $$ ->
@div result.message, class: 'result-message fail'
@pre stackTrace, class: 'stack-trace padded' if stackTrace
attach: ->
@parentSuiteView().append this
parentSuiteView: ->
if not suiteView = $("#suite-view-#{@spec.suite.id}").view()
suiteView = new SuiteResultView(@spec.suite)
suiteView.attach()
suiteView

View file

@ -456,9 +456,9 @@ describe "PackageManager", ->
one = require.resolve("./fixtures/packages/package-with-style-sheets-manifest/styles/1.css")
two = require.resolve("./fixtures/packages/package-with-style-sheets-manifest/styles/2.less")
three = require.resolve("./fixtures/packages/package-with-style-sheets-manifest/styles/3.css")
expect(NylasEnv.themes.stylesheetElementForId(one)).not.toExist()
expect(NylasEnv.themes.stylesheetElementForId(two)).not.toExist()
expect(NylasEnv.themes.stylesheetElementForId(three)).not.toExist()
expect(NylasEnv.themes.stylesheetElementForId(one)).toBe(null)
expect(NylasEnv.themes.stylesheetElementForId(two)).toBe(null)
expect(NylasEnv.themes.stylesheetElementForId(three)).toBe(null)
it "invokes ::onDidDeactivatePackage listeners with the deactivated package", ->
waitsForPromise ->

View file

@ -1,4 +1,3 @@
{$} = require '../src/space-pen-extensions'
path = require 'path'
Package = require '../src/package'
ThemePackage = require '../src/theme-package'
@ -41,51 +40,57 @@ describe "Package", ->
theme = null
beforeEach ->
$("#jasmine-content").append $("<nylas-theme-wrap></nylas-theme-wrap>")
@wrap = document.createElement('nylas-theme-wrap')
document.getElementById("jasmine-content").appendChild(@wrap)
afterEach ->
theme.deactivate() if theme?
describe "when the theme contains a single style file", ->
it "loads and applies css", ->
expect($("nylas-theme-wrap").css("padding-bottom")).not.toBe "1234px"
expect(window.getComputedStyle(@wrap)['padding-bottom']).not.toBe "1234px"
themePath = resolveFixturePath('theme-with-index-css')
theme = new ThemePackage(themePath)
theme.activate()
expect($("nylas-theme-wrap").css("padding-top")).toBe "1234px"
expect(window.getComputedStyle(@wrap)['padding-top']).toBe "1234px"
it "parses, loads and applies less", ->
expect($("nylas-theme-wrap").css("padding-bottom")).not.toBe "1234px"
expect(window.getComputedStyle(@wrap)['padding-bottom']).not.toBe "1234px"
themePath = resolveFixturePath('theme-with-index-less')
theme = new ThemePackage(themePath)
theme.activate()
expect($("nylas-theme-wrap").css("padding-top")).toBe "4321px"
expect(window.getComputedStyle(@wrap)['padding-top']).toBe "4321px"
describe "when the theme contains a package.json file", ->
it "loads and applies stylesheets from package.json in the correct order", ->
expect($("nylas-theme-wrap").css("padding-top")).not.toBe("101px")
expect($("nylas-theme-wrap").css("padding-right")).not.toBe("102px")
expect($("nylas-theme-wrap").css("padding-bottom")).not.toBe("103px")
styles = window.getComputedStyle(@wrap)
expect(styles["padding-top"]).not.toBe("101px")
expect(styles["padding-right"]).not.toBe("102px")
expect(styles["padding-bottom"]).not.toBe("103px")
themePath = resolveFixturePath('theme-with-package-file')
theme = new ThemePackage(themePath)
theme.activate()
expect($("nylas-theme-wrap").css("padding-top")).toBe("101px")
expect($("nylas-theme-wrap").css("padding-right")).toBe("102px")
expect($("nylas-theme-wrap").css("padding-bottom")).toBe("103px")
styles = window.getComputedStyle(@wrap)
expect(styles["padding-top"]).toBe("101px")
expect(styles["padding-right"]).toBe("102px")
expect(styles["padding-bottom"]).toBe("103px")
describe "when the theme does not contain a package.json file and is a directory", ->
it "loads all stylesheet files in the directory", ->
expect($("nylas-theme-wrap").css("padding-top")).not.toBe "10px"
expect($("nylas-theme-wrap").css("padding-right")).not.toBe "20px"
expect($("nylas-theme-wrap").css("padding-bottom")).not.toBe "30px"
styles = window.getComputedStyle(@wrap)
expect(styles["padding-top"]).not.toBe("10px")
expect(styles["padding-right"]).not.toBe("20px")
expect(styles["padding-bottom"]).not.toBe("30px")
themePath = resolveFixturePath('theme-without-package-file')
theme = new ThemePackage(themePath)
theme.activate()
expect($("nylas-theme-wrap").css("padding-top")).toBe "10px"
expect($("nylas-theme-wrap").css("padding-right")).toBe "20px"
expect($("nylas-theme-wrap").css("padding-bottom")).toBe "30px"
styles = window.getComputedStyle(@wrap)
expect(styles["padding-top"]).toBe("10px")
expect(styles["padding-right"]).toBe("20px")
expect(styles["padding-bottom"]).toBe("30px")
describe "reloading a theme", ->
beforeEach ->

View file

@ -7,15 +7,11 @@ require '../src/window'
NylasEnv.restoreWindowDimensions()
require 'jasmine-json'
require './jasmine-jquery'
Grim = require 'grim'
TimeOverride = require './time-override'
KeymapManager = require '../src/keymap-manager'
# FIXME: Remove jquery from this
{$} = require '../src/space-pen-extensions'
Config = require '../src/config'
pathwatcher = require 'pathwatcher'
{clipboard} = require 'electron'
@ -40,7 +36,9 @@ window.addEventListener 'core:close', -> window.close()
window.addEventListener 'beforeunload', ->
NylasEnv.storeWindowDimensions()
NylasEnv.saveSync()
$('html,body').css('overflow', 'auto')
document.querySelector('html').style.overflow = 'initial'
document.querySelector('body').style.overflow = 'initial'
# Allow document.title to be assigned in specs without screwing up spec window title
documentTitle = null
@ -142,7 +140,6 @@ beforeEach ->
TaskQueue._completed = []
TaskQueue._onlineStatus = true
$.fx.off = true
documentTitle = null
NylasEnv.styles.restoreSnapshot(styleElementsToRestore)
NylasEnv.workspaceViewParentSelector = '#jasmine-content'
@ -243,8 +240,8 @@ afterEach ->
delete NylasEnv.state?.packageStates
$('#jasmine-content').empty() unless window.debugContent
unless window.debugContent
document.getElementById('jasmine-content').innerHTML = ''
ReactTestUtils.unmountAll()
jasmine.unspy(NylasEnv, 'saveSync')
@ -354,8 +351,8 @@ window.keydownEvent = (key, properties={}) ->
originalEventProperties.target = properties.target?[0] ? properties.target
originalEventProperties.which = properties.which
originalEvent = KeymapManager.keydownEvent(key, originalEventProperties)
properties = $.extend({originalEvent}, properties)
$.Event("keydown", properties)
properties = _.extend({originalEvent}, properties)
new CustomEvent('keydown', properties)
window.mouseEvent = (type, properties) ->
if properties.point
@ -364,7 +361,7 @@ window.mouseEvent = (type, properties) ->
properties.pageX = left + 1
properties.pageY = top + 1
properties.originalEvent ?= {detail: 1}
$.Event type, properties
new CustomEvent(type, properties)
window.clickEvent = (properties={}) ->
window.mouseEvent("click", properties)

View file

@ -1,6 +1,5 @@
path = require 'path'
{$, $$} = require '../src/space-pen-extensions'
fs = require 'fs-plus'
temp = require 'temp'
@ -101,7 +100,7 @@ describe "ThemeManager", ->
runs ->
didChangeActiveThemesHandler.reset()
expect($('style.theme')).toHaveLength 0
expect(document.querySelectorAll('style.theme')).toHaveLength 0
NylasEnv.config.set('core.themes', ['ui-dark'])
waitsFor ->
@ -109,8 +108,9 @@ describe "ThemeManager", ->
runs ->
didChangeActiveThemesHandler.reset()
expect($('style[priority=1]')).toHaveLength 1
expect($('style[priority=1]:eq(0)').attr('source-path')).toMatch /ui-dark/
sheets = Array.from(document.querySelectorAll('style[priority="1"]'))
expect(sheets).toHaveLength 1
expect(sheets[0].getAttribute('source-path')).toMatch /ui-dark/
NylasEnv.config.set('core.themes', ['ui-light', 'ui-dark'])
waitsFor ->
@ -118,9 +118,10 @@ describe "ThemeManager", ->
runs ->
didChangeActiveThemesHandler.reset()
expect($('style[priority=1]')).toHaveLength 2
expect($('style[priority=1]:eq(0)').attr('source-path')).toMatch /ui-dark/
expect($('style[priority=1]:eq(1)').attr('source-path')).toMatch /ui-light/
sheets = Array.from(document.querySelectorAll('style[priority="1"]'))
expect(sheets).toHaveLength 2
expect(sheets[0].getAttribute('source-path')).toMatch /ui-dark/
expect(sheets[1].getAttribute('source-path')).toMatch /ui-light/
NylasEnv.config.set('core.themes', [])
waitsFor ->
@ -128,7 +129,8 @@ describe "ThemeManager", ->
runs ->
didChangeActiveThemesHandler.reset()
expect($('style[priority=1]')).toHaveLength(1)
sheets = Array.from(document.querySelectorAll('style[priority="1"]'))
expect(sheets).toHaveLength(1)
# ui-dark has an directory path, the syntax one doesn't
NylasEnv.config.set('core.themes', ['theme-with-index-less', 'ui-light'])
@ -136,7 +138,8 @@ describe "ThemeManager", ->
didChangeActiveThemesHandler.callCount == 1
runs ->
expect($('style[priority=1]')).toHaveLength 2
sheets = Array.from(document.querySelectorAll('style[priority="1"]'))
expect(sheets).toHaveLength 2
importPaths = themeManager.getImportPaths()
expect(importPaths.length).toBe 1
expect(importPaths[0]).toContain 'ui-light'
@ -151,7 +154,7 @@ describe "ThemeManager", ->
themeManager.activateThemes()
runs ->
expect(workspaceElement).toHaveClass 'theme-ui-light'
expect(workspaceElement.classList.contains('theme-ui-light')).toBe(true)
themeManager.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy()
NylasEnv.config.set('core.themes', ['theme-with-ui-variables'])
@ -161,8 +164,8 @@ describe "ThemeManager", ->
runs ->
# `theme-` twice as it prefixes the name with `theme-`
expect(workspaceElement).toHaveClass 'theme-theme-with-ui-variables'
expect(workspaceElement).not.toHaveClass 'theme-ui-dark'
expect(workspaceElement.classList.contains('theme-theme-with-ui-variables')).toBe(true)
expect(workspaceElement.classList.contains('theme-ui-dark')).toBe(false)
describe "when a theme fails to load", ->
it "logs a warning", ->
@ -183,37 +186,37 @@ describe "ThemeManager", ->
themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
cssPath = path.join(__dirname, 'fixtures', 'css.css')
lengthBefore = $('head style').length
lengthBefore = document.querySelectorAll('head style').length
themeManager.requireStylesheet(cssPath)
expect($('head style').length).toBe lengthBefore + 1
expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1
expect(styleElementAddedHandler).toHaveBeenCalled()
expect(stylesheetAddedHandler).toHaveBeenCalled()
expect(stylesheetsChangedHandler).toHaveBeenCalled()
element = $('head style[source-path*="css.css"]')
expect(element.attr('source-path')).toBe themeManager.stringToId(cssPath)
expect(element.text()).toBe fs.readFileSync(cssPath, 'utf8')
expect(element[0].sheet).toBe stylesheetAddedHandler.argsForCall[0][0]
element = document.querySelector('head style[source-path*="css.css"]')
expect(element.getAttribute('source-path')).toBe themeManager.stringToId(cssPath)
expect(element.textContent).toBe fs.readFileSync(cssPath, 'utf8')
# doesn't append twice
styleElementAddedHandler.reset()
themeManager.requireStylesheet(cssPath)
expect($('head style').length).toBe lengthBefore + 1
expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1
expect(styleElementAddedHandler).not.toHaveBeenCalled()
$('head style[id*="css.css"]').remove()
element .remove()
it "synchronously loads and parses less files at the given path and installs a style tag for it in the head", ->
lessPath = path.join(__dirname, 'fixtures', 'sample.less')
lengthBefore = $('head style').length
lengthBefore = document.querySelectorAll('head style').length
themeManager.requireStylesheet(lessPath)
expect($('head style').length).toBe lengthBefore + 1
lengthAfter = document.querySelectorAll('head style').length
expect(lengthAfter).toBe lengthBefore + 1
element = $('head style[source-path*="sample.less"]')
expect(element.attr('source-path')).toBe themeManager.stringToId(lessPath)
expect(element.text()).toBe """
element = document.querySelector('head style[source-path*="sample.less"]')
expect(element.getAttribute('source-path')).toBe themeManager.stringToId(lessPath)
expect(element.textContent).toBe """
#header {
color: #4d926f;
}
@ -225,24 +228,24 @@ describe "ThemeManager", ->
# doesn't append twice
themeManager.requireStylesheet(lessPath)
expect($('head style').length).toBe lengthBefore + 1
$('head style[id*="sample.less"]').remove()
expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1
element.remove()
it "supports requiring css and less stylesheets without an explicit extension", ->
themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'css')
expect($('head style[source-path*="css.css"]').attr('source-path')).toBe themeManager.stringToId(path.join(__dirname, 'fixtures', 'css.css'))
expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')).toBe themeManager.stringToId(path.join(__dirname, 'fixtures', 'css.css'))
themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'sample')
expect($('head style[source-path*="sample.less"]').attr('source-path')).toBe themeManager.stringToId(path.join(__dirname, 'fixtures', 'sample.less'))
expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')).toBe themeManager.stringToId(path.join(__dirname, 'fixtures', 'sample.less'))
$('head style[id*="css.css"]').remove()
$('head style[id*="sample.less"]').remove()
document.querySelector('head style[source-path*="css.css"]').remove()
document.querySelector('head style[source-path*="sample.less"]').remove()
it "returns a disposable allowing styles applied by the given path to be removed", ->
cssPath = require.resolve('./fixtures/css.css')
expect($(document.body).css('font-weight')).not.toBe("bold")
expect(window.getComputedStyle(document.body)['font-weight']).not.toBe("bold")
disposable = themeManager.requireStylesheet(cssPath)
expect($(document.body).css('font-weight')).toBe("bold")
expect(window.getComputedStyle(document.body)['font-weight']).toBe("bold")
NylasEnv.styles.onDidRemoveStyleElement styleElementRemovedHandler = jasmine.createSpy("styleElementRemovedHandler")
themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
@ -250,7 +253,7 @@ describe "ThemeManager", ->
disposable.dispose()
expect($(document.body).css('font-weight')).not.toBe("bold")
expect(window.getComputedStyle(document.body)['font-weight']).not.toBe("bold")
expect(styleElementRemovedHandler).toHaveBeenCalled()
expect(stylesheetRemovedHandler).toHaveBeenCalled()
@ -291,9 +294,11 @@ describe "ThemeManager", ->
expect(getComputedStyle(workspaceElement)["background-color"]).toBe "rgb(0, 0, 255)"
# a value that is not overridden in the theme
expect($("nylas-theme-wrap").css("padding-top")).toBe "150px"
expect($("nylas-theme-wrap").css("padding-right")).toBe "150px"
expect($("nylas-theme-wrap").css("padding-bottom")).toBe "150px"
node = document.querySelector('nylas-theme-wrap')
nodeStyle = window.getComputedStyle(node)
expect(nodeStyle['padding-top']).toBe "150px"
expect(nodeStyle['padding-right']).toBe "150px"
expect(nodeStyle['padding-bottom']).toBe "150px"
describe "when there is a theme with incomplete variables", ->
it "loads the correct values from the fallback ui-variables", ->
@ -304,8 +309,10 @@ describe "ThemeManager", ->
# an override loaded in the base css of theme-with-incomplete-ui-variables
expect(getComputedStyle(workspaceElement)["background-color"]).toBe "rgb(0, 0, 255)"
# a value that is not overridden in the theme
expect($("nylas-theme-wrap").css("background-color")).toBe "rgb(152, 123, 0)"
# a value that is not overridden in the theme
node = document.querySelector('nylas-theme-wrap')
nodeStyle = window.getComputedStyle(node)
expect(nodeStyle['background-color']).toBe "rgb(152, 123, 0)"
describe "user stylesheet", ->
userStylesheetPath = null
@ -337,14 +344,16 @@ describe "ThemeManager", ->
themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
spyOn(themeManager, 'loadUserStylesheet').andCallThrough()
expect($(document.body).css('border-style')).toBe 'dotted'
bodyStyle = window.getComputedStyle(document.body)
expect(bodyStyle['border-style']).toBe "dotted"
fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}')
waitsFor ->
themeManager.loadUserStylesheet.callCount is 1
runs ->
expect($(document.body).css('border-style')).toBe 'dashed'
bodyStyle = window.getComputedStyle(document.body)
expect(bodyStyle['border-style']).toBe "dashed"
expect(styleElementRemovedHandler).toHaveBeenCalled()
expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dotted'
@ -382,7 +391,9 @@ describe "ThemeManager", ->
match = null
for rule in stylesheetRemovedHandler.argsForCall[0][0].cssRules
match = rule if rule.selectorText is 'body'
match.style.border is 'dashed' and $(document.body).css('border-style') is 'none'
bodyStyle = window.getComputedStyle(document.body)
match.style.border is 'dashed' and bodyStyle['border-style'] is 'none'
runs ->
expect(stylesheetsChangedHandler).toHaveBeenCalled()

View file

@ -1,109 +0,0 @@
_ = require 'underscore'
_ = _.extend(_, require('./space-pen-utils'))
SpacePen = require 'space-pen'
{Subscriber} = require 'emissary'
###
Edgehill Note:
I want this file goneplan is to slowly delete stuff below as we clean spacepen
and it's helpers out of the project.
- Ben
###
Subscriber.includeInto(SpacePen.View)
jQuery = SpacePen.jQuery
JQueryCleanData = jQuery.cleanData
jQuery.cleanData = (elements) ->
jQuery(element).view()?.unsubscribe?() for element in elements
JQueryCleanData(elements)
SpacePenCallRemoveHooks = SpacePen.callRemoveHooks
SpacePen.callRemoveHooks = (element) ->
view.unsubscribe?() for view in SpacePen.viewsForElement(element)
SpacePenCallRemoveHooks(element)
NativeEventNames = new Set
NativeEventNames.add(nativeEvent) for nativeEvent in ["blur", "focus", "focusin",
"focusout", "load", "resize", "scroll", "unload", "click", "dblclick", "mousedown",
"mouseup", "mousemove", "mouseover", "mouseout", "mouseenter", "mouseleave", "change",
"select", "submit", "keydown", "keypress", "keyup", "error", "contextmenu", "textInput",
"textinput", "beforeunload"]
JQueryTrigger = jQuery.fn.trigger
jQuery.fn.trigger = (eventName, data) ->
if NativeEventNames.has(eventName) or typeof eventName is 'object'
JQueryTrigger.call(this, eventName, data)
else
data ?= {}
data.jQueryTrigger = true
for element in this
NylasEnv.commands.dispatch(eventName, data)
this
HandlersByOriginalHandler = new WeakMap
CommandDisposablesByElement = new WeakMap
AddEventListener = (element, type, listener) ->
if NativeEventNames.has(type)
element.addEventListener(type, listener)
else
disposable = NylasEnv.commands.add(element, type, listener)
unless disposablesByType = CommandDisposablesByElement.get(element)
disposablesByType = {}
CommandDisposablesByElement.set(element, disposablesByType)
unless disposablesByListener = disposablesByType[type]
disposablesByListener = new WeakMap
disposablesByType[type] = disposablesByListener
disposablesByListener.set(listener, disposable)
RemoveEventListener = (element, type, listener) ->
if NativeEventNames.has(type)
element.removeEventListener(type, listener)
else
CommandDisposablesByElement.get(element)?[type]?.get(listener)?.dispose()
JQueryEventAdd = jQuery.event.add
jQuery.event.add = (elem, types, originalHandler, data, selector) ->
handler = (event) ->
if arguments.length is 1 and event.originalEvent?.detail?
{detail} = event.originalEvent
if Array.isArray(detail)
originalHandler.apply(this, [event].concat(detail))
else
originalHandler.call(this, event, detail)
else
originalHandler.apply(this, arguments)
HandlersByOriginalHandler.set(originalHandler, handler)
JQueryEventAdd.call(this, elem, types, handler, data, selector, AddEventListener if NylasEnv?.commands?)
JQueryEventRemove = jQuery.event.remove
jQuery.event.remove = (elem, types, originalHandler, selector, mappedTypes) ->
if originalHandler?
handler = HandlersByOriginalHandler.get(originalHandler) ? originalHandler
JQueryEventRemove(elem, types, handler, selector, mappedTypes, RemoveEventListener if NylasEnv?.commands?)
JQueryContains = jQuery.contains
jQuery.contains = (a, b) ->
shadowRoot = null
currentNode = b
while currentNode
if currentNode instanceof ShadowRoot and a.contains(currentNode.host)
return true
currentNode = currentNode.parentNode
JQueryContains.call(this, a, b)
Object.defineProperty jQuery.fn, 'element', get: -> @[0]
module.exports = SpacePen