1
0
mirror of https://github.com/adobe/brackets.git synced 2024-11-20 18:02:54 +01:00

Merge remote-tracking branch 'origin/master' into pflynn/document-apis

* origin/master: (45 commits)
  code review comments
  Update comments
  Update CodeMirror SHA
  code review fixes
  Restore Shift-Tab to outdent behavior
  Re-implement performance suite filtering due to RequireJS changes in SpecRunner
  Revert .json file listing of test suites.
  Add ExtensionUtils to root require context
  added a comment explaining why we aren't using jquery deferreds for the done loading notification
  Fix #971 (Find in Files treats \n, \r, etc. as regular expressions) -- Escape backslash too (and simplify escaping regexp to use char class instead of |).
  createTestWindowAndRun now waits for brackets to be completely done loading (so extensions are loaded before tests run)
  added a 'ready' event to brackets that fires when brackets is completely done loading
  Code review fixes. Combined both offset to lines functions into a single function
  Rewrote unit tests for JavaScriptInlineEditor to get JSUtils from the require context that was used to load the extension. We only need to do this in unit tests where we rely on JSUtils to get the DocumentManager, etc. from the current window.
  Modified ExtensionLoader to allow for retrieving the require context that loaded an extension
  Refactor loadStyleSheet. Change error handling.
  make red bolder
  provide way for unit tests to restore commands after a reset
  Changed click handler to use $.on so runtime added menus work
  Fixed bug where triangle wasn't updating correctly by toggling "scroll" events on double click show
  ...

Conflicts:
	test/BootstrapReporter.js
	test/UnitTestSuite.json
This commit is contained in:
Peter Flynn 2012-06-13 15:57:15 -07:00
commit 2f4972e544
34 changed files with 726 additions and 281 deletions

View File

@ -58,6 +58,7 @@ define(function LiveDevelopment(require, exports, module) {
var NativeApp = require("utils/NativeApp");
var Dialogs = require("widgets/Dialogs");
var Strings = require("strings");
var StringUtils = require("utils/StringUtils");
// Inspector
var Inspector = require("LiveDevelopment/Inspector/Inspector");
@ -354,7 +355,7 @@ define(function LiveDevelopment(require, exports, module) {
if (err === FileError.NOT_FOUND_ERR) {
message = Strings.ERROR_CANT_FIND_CHROME;
} else {
message = Strings.format(Strings.ERROR_LAUNCHING_BROWSER, err);
message = StringUtils.format(Strings.ERROR_LAUNCHING_BROWSER, err);
}
Dialogs.showModalDialog(

View File

@ -73,12 +73,45 @@ define(function (require, exports, module) {
SidebarView = require("project/SidebarView"),
Async = require("utils/Async");
// Local variables
var bracketsReady = false,
bracketsReadyHandlers = [];
//Load modules that self-register and just need to get included in the main project
require("editor/CodeHintManager");
require("editor/EditorCommandHandlers");
require("debug/DebugCommandHandlers");
require("view/ViewCommandHandlers");
require("search/FindInFiles");
require("utils/ExtensionUtils");
function _callBracketsReadyHandler(handler) {
try {
handler();
} catch (e) {
console.log("Exception when calling a 'brackets done loading' handler");
console.log(e);
}
}
function _onBracketsReady() {
var i;
bracketsReady = true;
for (i = 0; i < bracketsReadyHandlers.length; i++) {
_callBracketsReadyHandler(bracketsReadyHandlers[i]);
}
bracketsReadyHandlers = [];
}
// WARNING: This event won't fire if ANY extension fails to load or throws an error during init.
// To fix this, we need to make a change to _initExtensions (filed as issue 1029)
function _registerBracketsReadyHandler(handler) {
if (bracketsReady) {
_callBracketsReadyHandler(handler);
} else {
bracketsReadyHandlers.push(handler);
}
}
// TODO: Issue 949 - the following code should be shared
@ -119,10 +152,22 @@ define(function (require, exports, module) {
// Note: we change the name to "getModule" because this won't do exactly the same thing as 'require' in AMD-wrapped
// modules. The extension will only be able to load modules that have already been loaded once.
brackets.getModule = require;
// Provide a way for anyone (including code not using require) to register a handler for the brackets 'ready' event
// This event is like $(document).ready in that it will call the handler immediately if brackets is already done loading
//
// WARNING: This event won't fire if ANY extension fails to load or throws an error during init.
// To fix this, we need to make a change to _initExtensions (filed as issue 1029)
//
// TODO (issue 1034): We *could* use a $.Deferred for this, except deferred objects enter a broken
// state if any resolution callback throws an exception. Since third parties (e.g. extensions) may
// add callbacks to this, we need to be robust to exceptions
brackets.ready = _registerBracketsReadyHandler;
}
// TODO: (issue 1029) Add timeout to main extension loading promise, so that we always call this function
// Making this fix will fix a warning (search for issue 1029) related to the brackets 'ready' event.
function _initExtensions() {
// FUTURE (JRB): As we get more fine-grained performance measurement, move this out of core application startup
return Async.doInParallel(["default", "user"], function (item) {
return ExtensionLoader.loadAllExtensionsInNativeDirectory(
FileUtils.getNativeBracketsDirectoryPath() + "/extensions/" + item,
@ -156,8 +201,13 @@ define(function (require, exports, module) {
CSSUtils : require("language/CSSUtils"),
LiveDevelopment : require("LiveDevelopment/LiveDevelopment"),
Inspector : require("LiveDevelopment/Inspector/Inspector"),
NativeApp : require("utils/NativeApp")
NativeApp : require("utils/NativeApp"),
doneLoading : false
};
brackets.ready(function () {
brackets.test.doneLoading = true;
});
}
function _initDragAndDropListeners() {
@ -261,7 +311,7 @@ define(function (require, exports, module) {
// finish UI initialization before loading extensions
ProjectManager.loadProject().done(function () {
_initTest();
_initExtensions();
_initExtensions().always(_onBracketsReady);
});
}

View File

@ -38,6 +38,13 @@ define(function (require, exports, module) {
*/
var _commands = {};
/**
* Temporary copy of commands map for restoring after testing
* TODO (issue #1039): implement separate require contexts for unit tests
* @type Object.<commandID: string, Command>
*/
var _commandsOriginal = {};
/**
* @constructor
* @private
@ -174,11 +181,23 @@ define(function (require, exports, module) {
return command;
}
function _reset() {
/**
* Clear all commands for unit testing, but first make copy of commands so that
* they can be restored afterward
*/
function _testReset() {
_commandsOriginal = _commands;
_commands = {};
}
/**
* Restore original commands after test and release copy
*/
function _testRestore(commands) {
_commands = _commandsOriginal;
_commandsOriginal = {};
}
/**
* Retrieves a Command object by id
* @param {string} id
@ -207,5 +226,6 @@ define(function (require, exports, module) {
exports.register = register;
exports.execute = execute;
exports.get = get;
exports._reset = _reset;
exports._testReset = _testReset;
exports._testRestore = _testRestore;
});

View File

@ -65,6 +65,7 @@ define(function (require, exports, module) {
exports.VIEW_HIDE_SIDEBAR = "view.hideSidebar";
exports.VIEW_INCREASE_FONT_SIZE = "view.increaseFontSize";
exports.VIEW_DECREASE_FONT_SIZE = "view.decreaseFontSize";
exports.VIEW_RESTORE_FONT_SIZE = "view.restoreFontSize";
// Navigate
exports.NAVIGATE_NEXT_DOC = "navigate.nextDoc";

View File

@ -23,7 +23,7 @@
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, brackets */
/*global define, $, brackets, document */
define(function (require, exports, module) {
'use strict';
@ -585,6 +585,7 @@ define(function (require, exports, module) {
menu.addMenuDivider();
menu.addMenuItem("menu-view-increase-font", Commands.VIEW_INCREASE_FONT_SIZE, [{key: "Ctrl-=", displayKey: "Ctrl-+"}]);
menu.addMenuItem("menu-view-decrease-font", Commands.VIEW_DECREASE_FONT_SIZE, [{key: "Ctrl--", displayKey: "Ctrl-\u2212"}]);
menu.addMenuItem("menu-view-restore-font", Commands.VIEW_RESTORE_FONT_SIZE, "Ctrl-0");
/*
* Navigate menu
@ -623,16 +624,16 @@ define(function (require, exports, module) {
menu.addMenuItem("menu-debug-close-browser", Commands.DEBUG_CLOSE_ALL_LIVE_BROWSERS);
menu.addMenuItem("menu-debug-use-tab-chars", Commands.DEBUG_USE_TAB_CHARS);
$("#main-toolbar .dropdown")
// Prevent clicks on the top-level menu bar from taking focus
// Note, bootstrap handles this already for the menu drop downs
.mousedown(function (e) {
$(document).on("mousedown", "#main-toolbar .dropdown", function (e) {
e.preventDefault();
})
});
// Switch menus when the mouse enters an adjacent menu
// Only open the menu if another one has already been opened
// by clicking
.mouseenter(function (e) {
$(document).on("mouseenter", "#main-toolbar .dropdown", function (e) {
var open = $(this).siblings(".open");
if (open.length > 0) {
open.removeClass("open");

View File

@ -324,7 +324,7 @@ define(function (require, exports, module) {
return Dialogs.showModalDialog(
Dialogs.DIALOG_ID_ERROR,
Strings.ERROR_SAVING_FILE_TITLE,
Strings.format(
StringUtils.format(
Strings.ERROR_SAVING_FILE,
StringUtils.htmlEscape(path),
FileUtils.getFileErrorString(code)
@ -506,7 +506,7 @@ define(function (require, exports, module) {
Dialogs.showModalDialog(
Dialogs.DIALOG_ID_SAVE_CLOSE,
Strings.SAVE_CLOSE_TITLE,
Strings.format(Strings.SAVE_CLOSE_MESSAGE, StringUtils.htmlEscape(filename))
StringUtils.format(Strings.SAVE_CLOSE_MESSAGE, StringUtils.htmlEscape(filename))
).done(function (id) {
if (id === Dialogs.DIALOG_BTN_CANCEL) {
result.reject();

View File

@ -315,6 +315,7 @@ define(function (require, exports, module) {
// Editor supplies some standard keyboard behavior extensions of its own
var codeMirrorKeyMap = {
"Tab": _handleTabKey,
"Shift-Tab": "indentLess",
"Left": function (instance) {
if (!_handleSoftTabNavigation(instance, -1, "moveH")) {

View File

@ -34,6 +34,7 @@ define(function (require, exports, module) {
// Load brackets modules
var Async = brackets.getModule("utils/Async"),
DocumentManager = brackets.getModule("document/DocumentManager"),
StringUtils = brackets.getModule("utils/StringUtils"),
NativeFileSystem = brackets.getModule("file/NativeFileSystem").NativeFileSystem;
// Return an Array with names and offsets for all functions in the specified text
@ -94,9 +95,6 @@ define(function (require, exports, module) {
return length;
}
function _offsetToLineNum(text, offset) {
return text.substr(0, offset).split("\n").length - 1;
}
// Search function list for a specific function. If found, extract info about the function
// (line start, line end, etc.) and return.
@ -111,6 +109,7 @@ define(function (require, exports, module) {
DocumentManager.getDocumentForPath(fileInfo.fullPath)
.done(function (doc) {
var text = doc.getText();
var lines = StringUtils.getLines(text);
functions.forEach(function (funcEntry) {
if (funcEntry.functionName === functionName) {
@ -118,8 +117,8 @@ define(function (require, exports, module) {
matchingFunctions.push({
document: doc,
name: funcEntry.functionName,
lineStart: _offsetToLineNum(text, funcEntry.offset),
lineEnd: _offsetToLineNum(text, endOffset)
lineStart: StringUtils.offsetToLineNum(lines, funcEntry.offset),
lineEnd: StringUtils.offsetToLineNum(lines, endOffset)
});
}
});
@ -275,14 +274,15 @@ define(function (require, exports, module) {
function _findAllMatchingFunctionsInText(text, functionName) {
var allFunctions = _findAllFunctionsInText(text);
var result = [];
var lines = text.split("\n");
allFunctions.forEach(function (funcEntry) {
if (funcEntry.functionName === functionName) {
if (funcEntry.functionName === functionName || functionName === "*") {
var endOffset = _getFunctionEndOffset(text, funcEntry.offset);
result.push({
name: funcEntry.functionName,
lineStart: _offsetToLineNum(text, funcEntry.offset),
lineEnd: _offsetToLineNum(text, endOffset)
lineStart: StringUtils.offsetToLineNum(lines, funcEntry.offset),
lineEnd: StringUtils.offsetToLineNum(lines, endOffset)
});
}
});

View File

@ -44,7 +44,7 @@ define(function (require, exports, module) {
var extensionPath = FileUtils.getNativeModuleDirectoryPath(module);
describe("JSQuickEdit", function () {
describe("JavaScriptInlineEditor", function () {
var testPath = extensionPath + "/unittest-files",
testWindow,
@ -495,7 +495,6 @@ define(function (require, exports, module) {
SpecRunnerUtils.closeTestWindow();
});
/***
it("should return the correct offsets if the file has changed", function () {
var didOpen = false,
gotError = false;
@ -531,8 +530,11 @@ define(function (require, exports, module) {
FileIndexManager.getFileInfoList("all")
.done(function (fileInfos) {
var extensionRequire = brackets.getModule('utils/ExtensionLoader').getRequireContextForExtension('JavaScriptInlineEditor');
var JSUtilsInExtension = extensionRequire("JSUtils");
// Look for "edit2" function
JSUtils.findMatchingFunctions("edit2", fileInfos)
JSUtilsInExtension.findMatchingFunctions("edit2", fileInfos)
.done(function (result) { functions = result; });
});
});
@ -545,8 +547,7 @@ define(function (require, exports, module) {
expect(functions[0].lineEnd).toBe(13);
});
});
***/
/***
it("should return a newly created function in an unsaved file", function () {
var didOpen = false,
gotError = false;
@ -563,24 +564,31 @@ define(function (require, exports, module) {
runs(function () {
var doc = DocumentManager.getCurrentDocument();
// Add a new function to the file
doc.setText(doc.getText() + "\n\nfunction TESTFUNCTION() {\n return true;\n}\n");
// Look for the selector we just created
JSUtils.findMatchingFunctions("TESTFUNCTION", FileIndexManager.getFileInfoList("all"))
.done(function (result) { functions = result; });
FileIndexManager.getFileInfoList("all")
.done(function (fileInfos) {
var extensionRequire = brackets.getModule('utils/ExtensionLoader').getRequireContextForExtension('JavaScriptInlineEditor');
var JSUtilsInExtension = extensionRequire("JSUtils");
// Look for "TESTFUNCTION" function
JSUtilsInExtension.findMatchingFunctions("TESTFUNCTION", fileInfos)
.done(function (result) {
functions = result;
});
});
});
waitsFor(function () { return functions !== null; }, "JSUtils.findMatchingFunctions() timeout", 1000);
runs(function () {
expect(functions.length).toBe(1);
expect(functions[0].lineStart).toBe(24);
expect(functions[0].lineEnd).toBe(26);
expect(functions[0].lineStart).toBe(33);
expect(functions[0].lineEnd).toBe(35);
});
});
***/
});
}); //describe("JS Parsing")
});

View File

@ -166,7 +166,7 @@ define(function (require, exports, module) {
} else if (code === FileError.NO_MODIFICATION_ALLOWED_ERR) {
result = Strings.NO_MODIFICATION_ALLOWED_ERR_FILE;
} else {
result = Strings.format(Strings.GENERIC_ERROR, code);
result = StringUtils.format(Strings.GENERIC_ERROR, code);
}
return result;
@ -176,7 +176,7 @@ define(function (require, exports, module) {
return Dialogs.showModalDialog(
Dialogs.DIALOG_ID_ERROR,
Strings.ERROR_OPENING_FILE_TITLE,
Strings.format(
StringUtils.format(
Strings.ERROR_OPENING_FILE,
StringUtils.htmlEscape(path),
getFileErrorString(code)

View File

@ -212,7 +212,7 @@ define(function (require, exports, module) {
return Dialogs.showModalDialog(
Dialogs.DIALOG_ID_ERROR,
Strings.ERROR_RELOADING_FILE_TITLE,
Strings.format(
StringUtils.format(
Strings.ERROR_RELOADING_FILE,
StringUtils.htmlEscape(doc.file.fullPath),
FileUtils.getFileErrorString(error.code)
@ -261,7 +261,7 @@ define(function (require, exports, module) {
if (i < editConflicts.length) {
toClose = false;
dialogId = Dialogs.DIALOG_ID_EXT_CHANGED;
message = Strings.format(
message = StringUtils.format(
Strings.EXT_MODIFIED_MESSAGE,
StringUtils.htmlEscape(ProjectManager.makeProjectRelativeIfPossible(doc.file.fullPath))
);
@ -269,7 +269,7 @@ define(function (require, exports, module) {
} else {
toClose = true;
dialogId = Dialogs.DIALOG_ID_EXT_DELETED;
message = Strings.format(
message = StringUtils.format(
Strings.EXT_DELETED_MESSAGE,
StringUtils.htmlEscape(ProjectManager.makeProjectRelativeIfPossible(doc.file.fullPath))
);

View File

@ -540,7 +540,7 @@ define(function (require, exports, module) {
Dialogs.showModalDialog(
Dialogs.DIALOG_ID_ERROR,
Strings.ERROR_LOADING_PROJECT,
Strings.format(Strings.READ_DIRECTORY_ENTRIES_ERROR,
StringUtils.format(Strings.READ_DIRECTORY_ENTRIES_ERROR,
StringUtils.htmlEscape(dirEntry.fullPath),
error.code)
);
@ -637,7 +637,7 @@ define(function (require, exports, module) {
Dialogs.showModalDialog(
Dialogs.DIALOG_ID_ERROR,
Strings.ERROR_LOADING_PROJECT,
Strings.format(
StringUtils.format(
Strings.REQUEST_NATIVE_FILE_SYSTEM_ERROR,
StringUtils.htmlEscape(rootPath),
error.code,
@ -685,7 +685,7 @@ define(function (require, exports, module) {
Dialogs.showModalDialog(
Dialogs.DIALOG_ID_ERROR,
Strings.ERROR_LOADING_PROJECT,
Strings.format(Strings.OPEN_DIALOG_ERROR, error.code)
StringUtils.format(Strings.OPEN_DIALOG_ERROR, error.code)
);
}
);
@ -812,15 +812,15 @@ define(function (require, exports, module) {
Dialogs.showModalDialog(
Dialogs.DIALOG_ID_ERROR,
Strings.INVALID_FILENAME_TITLE,
Strings.format(Strings.FILE_ALREADY_EXISTS,
StringUtils.format(Strings.FILE_ALREADY_EXISTS,
StringUtils.htmlEscape(data.rslt.name))
);
} else {
var errString = error.code === FileError.NO_MODIFICATION_ALLOWED_ERR ?
Strings.NO_MODIFICATION_ALLOWED_ERR :
Strings.format(String.GENERIC_ERROR, error.code);
StringUtils.format(String.GENERIC_ERROR, error.code);
var errMsg = Strings.format(Strings.ERROR_CREATING_FILE,
var errMsg = StringUtils.format(Strings.ERROR_CREATING_FILE,
StringUtils.htmlEscape(data.rslt.name),
errString);
@ -851,11 +851,7 @@ define(function (require, exports, module) {
_projectTree.jstree("create", node, position, {data: initialName}, null, skipRename);
if (!skipRename) {
var $renameInput = _projectTree.find(".jstree-rename-input"),
projectTreeOffset = _projectTree.offset(),
projectTreeScroller = _projectTree.get(0),
renameInput = $renameInput.get(0),
renameInputOffset = $renameInput.offset();
var $renameInput = _projectTree.find(".jstree-rename-input");
$renameInput.on("keydown", function (event) {
// Listen for escape key on keydown, so we can remove the node in the create.jstree handler above
@ -864,21 +860,7 @@ define(function (require, exports, module) {
}
});
// make sure edit box is visible within the jstree, only scroll vertically when necessary
if (renameInputOffset.top + $renameInput.height() >= (projectTreeOffset.top + _projectTree.height())) {
// below viewport
renameInput.scrollIntoView(false);
} else if (renameInputOffset.top <= projectTreeOffset.top) {
// above viewport
renameInput.scrollIntoView(true);
}
// left-align renameInput
if (renameInputOffset.left < 0) {
_projectTree.scrollLeft(_projectTree.scrollLeft() + renameInputOffset.left);
} else if (renameInputOffset.left + $renameInput.width() >= projectTreeOffset.left + _projectTree.width()) {
_projectTree.scrollLeft(renameInputOffset.left - projectTreeOffset.left);
}
ViewUtils.scrollElementIntoView(_projectTree, $renameInput, true);
}
return result.promise();

View File

@ -23,7 +23,7 @@
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, document */
/*global define, $, document, window */
define(function (require, exports, module) {
'use strict';
@ -75,7 +75,7 @@ define(function (require, exports, module) {
if (typeof displayTriangle === "boolean") {
var display = (displayTriangle) ? "block" : "none";
$sidebar.find(".triangle-visible").css("display", display);
$sidebar.find(".sidebar-selection-triangle").css("display", display);
}
if (isSidebarClosed) {
@ -89,8 +89,6 @@ define(function (require, exports, module) {
// event that we can just call from anywhere instead of hard-coding it.
// waiting on a ProjectManager refactor to add that.
$sidebar.find(".sidebar-selection").width(width);
$projectFilesContainer.triggerHandler("scroll");
$openFilesContainer.triggerHandler("scroll");
if (width > 10) {
prefs.setValue("sidebarWidth", width);
@ -101,7 +99,6 @@ define(function (require, exports, module) {
var text = (isSidebarClosed) ? Strings.CMD_SHOW_SIDEBAR : Strings.CMD_HIDE_SIDEBAR;
CommandManager.get(Commands.VIEW_HIDE_SIDEBAR).setName(text);
}
EditorManager.resizeEditor();
}
@ -119,7 +116,6 @@ define(function (require, exports, module) {
var prefs = PreferencesManager.getPreferenceStorage(PREFERENCES_CLIENT_ID, defaultPrefs);
prefs.setValue("sidebarClosed", isSidebarClosed);
_setWidth(width, true, !isSidebarClosed);
}
@ -132,7 +128,9 @@ define(function (require, exports, module) {
$body = $(document.body),
prefs = PreferencesManager.getPreferenceStorage(PREFERENCES_CLIENT_ID, defaultPrefs),
sidebarWidth = prefs.getValue("sidebarWidth"),
startingSidebarPosition = sidebarWidth;
startingSidebarPosition = sidebarWidth,
animationRequest = null,
isMouseDown = false;
$sidebarResizer.css("left", sidebarWidth - 1);
@ -143,24 +141,39 @@ define(function (require, exports, module) {
}
$sidebarResizer.on("dblclick", function () {
if ($sidebar.width() === 1) {
// mousedown is fired first. Sidebar is already toggeled open to 1px.
if ($sidebar.width() < 10) {
//mousedown is fired first. Sidebar is already toggeled open to at least 10px.
_setWidth(null, true, true);
$projectFilesContainer.triggerHandler("scroll");
$openFilesContainer.triggerHandler("scroll");
} else {
toggleSidebar();
toggleSidebar(sidebarWidth);
}
});
$sidebarResizer.on("mousedown.sidebar", function (e) {
var startX = e.clientX;
var startX = e.clientX,
newWidth = Math.max(e.clientX, 0),
doResize = true;
isMouseDown = true;
// take away the shadows (for performance reasons during sidebarmovement)
$sidebar.find(".scroller-shadow").css("display", "none");
$body.toggleClass("resizing");
// check to see if we're currently in hidden mode
if (isSidebarClosed) {
toggleSidebar(1);
}
$mainView.on("mousemove.sidebar", function (e) {
var doResize = true,
newWidth = Math.max(e.clientX, 0);
animationRequest = window.webkitRequestAnimationFrame(function doRedraw() {
// only run this if the mouse is down so we don't constantly loop even
// after we're done resizing.
if (!isMouseDown) {
return;
}
// if we've gone below 10 pixels on a mouse move, and the
// sidebar is shrinking, hide the sidebar automatically an
@ -168,34 +181,45 @@ define(function (require, exports, module) {
if ((startX > 10) && (newWidth < 10)) {
toggleSidebar(startingSidebarPosition);
$mainView.off("mousemove.sidebar");
// turn off the mouseup event so that it doesn't fire twice and retoggle the
// resizing class
$mainView.off("mouseup.sidebar");
$body.toggleClass("resizing");
doResize = false;
} else if (startX < 10) {
// reset startX if we're going from a snapped closed position to open
startX = startingSidebarPosition;
startX = 0;
// force isMouseDown so that we don't keep calling requestAnimationFrame
// this keeps the sidebar from stuttering
isMouseDown = false;
}
if (doResize) {
// if we've moving past 10 pixels, make the triangle visible again
// and register that the sidebar is no longer snapped closed.
var forceTriangle = null;
if (newWidth > 10) {
forceTriangle = true;
// for right now, displayTriangle is always going to be false for _setWidth
// because we want to hide it when we move, and _setWidth only gets called
// on mousemove now.
_setWidth(newWidth, false, false);
}
_setWidth(newWidth, false, forceTriangle);
}
animationRequest = window.webkitRequestAnimationFrame(doRedraw);
});
if (newWidth === 0) {
$mainView.off("mousemove.sidebar");
$("body").toggleClass("resizing");
}
$mainView.on("mousemove.sidebar", function (e) {
newWidth = Math.max(e.clientX, 0);
e.preventDefault();
});
$mainView.one("mouseup.sidebar", function (e) {
isMouseDown = false;
// replace shadows and triangle
$sidebar.find(".sidebar-selection-triangle").css("display", "block");
$sidebar.find(".scroller-shadow").css("display", "block");
$projectFilesContainer.triggerHandler("scroll");
$openFilesContainer.triggerHandler("scroll");
$mainView.off("mousemove.sidebar");
$body.toggleClass("resizing");
startingSidebarPosition = $sidebar.width();

View File

@ -195,40 +195,6 @@ define(function (require, exports, module) {
_redraw();
}
/**
* @private
*/
function _updateListSelection() {
var doc;
if (FileViewController.getFileSelectionFocus() === FileViewController.WORKING_SET_VIEW) {
doc = DocumentManager.getCurrentDocument();
} else {
doc = null;
}
// Iterate through working set list and update the selection on each
var items = $openFilesContainer.find("ul").children().each(function () {
_updateListItemSelection(this, doc);
});
_fireSelectionChanged();
}
/**
* @private
*/
function _handleFileAdded(file) {
_createNewListItem(file);
_redraw();
}
/**
* @private
*/
function _handleDocumentSelectionChange() {
_updateListSelection();
}
/**
* Finds the listItem item assocated with the file. Returns null if not found.
* @private
@ -253,6 +219,64 @@ define(function (require, exports, module) {
return result;
}
/**
* @private
*/
function _scrollSelectedDocIntoView() {
if (FileViewController.getFileSelectionFocus() !== FileViewController.WORKING_SET_VIEW) {
return;
}
var doc = DocumentManager.getCurrentDocument();
if (!doc) {
return;
}
var $selectedDoc = _findListItemFromFile(doc.file);
if (!$selectedDoc) {
return;
}
ViewUtils.scrollElementIntoView($openFilesContainer, $selectedDoc, false);
}
/**
* @private
*/
function _updateListSelection() {
var doc;
if (FileViewController.getFileSelectionFocus() === FileViewController.WORKING_SET_VIEW) {
doc = DocumentManager.getCurrentDocument();
} else {
doc = null;
}
// Iterate through working set list and update the selection on each
var items = $openFilesContainer.find("ul").children().each(function () {
_updateListItemSelection(this, doc);
});
// Make sure selection is in view
_scrollSelectedDocIntoView();
_fireSelectionChanged();
}
/**
* @private
*/
function _handleFileAdded(file) {
_createNewListItem(file);
_redraw();
}
/**
* @private
*/
function _handleDocumentSelectionChange() {
_updateListSelection();
}
/**
* @private
* @param {FileEntry} file

View File

@ -36,7 +36,6 @@
* - Search files in working set that are *not* in the project
* - Handle matches that span mulitple lines
* - Refactor UI from functionality to enable unit testing
* - Cache result of getLine()
*/
@ -47,6 +46,7 @@ define(function (require, exports, module) {
CommandManager = require("command/CommandManager"),
Commands = require("command/Commands"),
Strings = require("strings"),
StringUtils = require("utils/StringUtils"),
DocumentManager = require("document/DocumentManager"),
EditorManager = require("editor/EditorManager"),
FileIndexManager = require("project/FileIndexManager");
@ -138,19 +138,12 @@ define(function (require, exports, module) {
var matchStart;
var matches = [];
function getLineNum(offset) {
return contents.substr(0, offset).split("\n").length - 1; // 0 based linenum
}
function getLine(lineNum) {
// Future: cache result
return contents.split("\n")[lineNum];
}
var match;
var lines = StringUtils.getLines(contents);
while ((match = queryExpr.exec(contents)) !== null) {
var lineNum = getLineNum(match.index);
var line = getLine(lineNum);
var lineNum = StringUtils.offsetToLineNum(lines, match.index);
var line = lines[lineNum];
var ch = match.index - contents.lastIndexOf("\n", match.index) - 1; // 0-based index
var matchLength = match[0].length;
@ -271,7 +264,7 @@ define(function (require, exports, module) {
// Query is a string. Turn it into a case-insensitive regexp
// Escape regex special chars
query = query.replace(/(\(|\)|\{|\}|\[|\]|\.|\^|\$|\||\?|\+|\*)/g, "\\$1");
query = query.replace(/([(){}\[\].\^$|?+*\\])/g, "\\$1");
return new RegExp(query, "gi");
}

View File

@ -28,28 +28,6 @@ define(function (require, exports, module) {
'use strict';
/**
* Format a string by replacing placeholder symbols with passed in arguments.
*
* Example: var formatted = Strings.format("Hello {0}", "World");
*
* @param {string} str The base string
* @param {...} Arguments to be substituted into the string
*
* @return {string} Formatted string
*/
function format(str) {
// arguments[0] is the base string, so we need to adjust index values here
var args = [].slice.call(arguments, 1);
return str.replace(/\{(\d+)\}/g, function (match, num) {
return typeof args[num] !== 'undefined' ? args[num] : match;
});
}
// Define public API
exports.format = format;
// General file io error strings
exports.GENERIC_ERROR = "(error {0})";
exports.NOT_FOUND_ERR = "The file could not be found.";
@ -152,6 +130,7 @@ define(function (require, exports, module) {
exports.CMD_SHOW_SIDEBAR = "Show Sidebar";
exports.CMD_INCREASE_FONT_SIZE = "Increase Font Size";
exports.CMD_DECREASE_FONT_SIZE = "Decrease Font Size";
exports.CMD_RESTORE_FONT_SIZE = "Restore Font Size";
// Navigate menu Commands
exports.NAVIGATE_MENU = "Navigate";

@ -1 +1 @@
Subproject commit c12c3d097493c065911a3eb95e05f38e35a17ffe
Subproject commit 5c2f30c606fc40a5271992b94141d79ce49c9309

View File

@ -34,7 +34,19 @@ define(function (require, exports, module) {
var NativeFileSystem = require("file/NativeFileSystem").NativeFileSystem,
FileUtils = require("file/FileUtils"),
Async = require("utils/Async");
Async = require("utils/Async"),
contexts = {};
/**
* Returns the require.js require context used to load an extension
*
* @param {!string} name, used to identify the extension
* @return {!Object} A require.js require object used to load the extension, or undefined if
* there is no require object ith that name
*/
function getRequireContextForExtension(name) {
return contexts[name];
}
/**
* Loads the extension that lives at baseUrl into its own Require.js context
@ -44,17 +56,18 @@ define(function (require, exports, module) {
* @param {!string} entryPoint, name of the main js file to load
* @return {!$.Promise} A promise object that is resolved when the extension is loaded.
*/
function loadExtension(name, baseUrl, entryPoint) {
function loadExtension(name, config, entryPoint) {
var result = new $.Deferred(),
extensionRequire = brackets.libRequire.config({
context: name,
baseUrl: baseUrl
baseUrl: config.baseUrl
});
contexts[name] = extensionRequire;
console.log("[Extension] starting to load " + baseUrl);
console.log("[Extension] starting to load " + config.baseUrl);
extensionRequire([entryPoint], function () {
console.log("[Extension] finished loading " + baseUrl);
console.log("[Extension] finished loading " + config.baseUrl);
result.resolve();
});
@ -69,13 +82,13 @@ define(function (require, exports, module) {
* @param {!string} entryPoint, name of the main js file to load
* @return {!$.Promise} A promise object that is resolved when all extensions complete loading.
*/
function testExtension(name, baseUrl, entryPoint) {
function testExtension(name, config, entryPoint) {
var result = new $.Deferred(),
extensionPath = FileUtils.getNativeBracketsDirectoryPath();
// Assumes the caller's window.location context is /test/SpecRunner.html
extensionPath = extensionPath.replace("brackets/test", "brackets/src"); // convert from "test" to "src"
extensionPath += "/" + baseUrl + "/" + entryPoint + ".js";
extensionPath += "/" + config.baseUrl + "/" + entryPoint + ".js";
var fileExists = false, statComplete = false;
brackets.fs.stat(extensionPath, function (err, stat) {
@ -84,12 +97,13 @@ define(function (require, exports, module) {
// unit test file exists
var extensionRequire = brackets.libRequire.config({
context: name,
baseUrl: "../src/" + baseUrl
baseUrl: "../src/" + config.baseUrl,
paths: config.paths
});
console.log("[Extension] loading unit test " + baseUrl);
console.log("[Extension] loading unit test " + config.baseUrl);
extensionRequire([entryPoint], function () {
console.log("[Extension] loaded unit tests " + baseUrl);
console.log("[Extension] loaded unit tests " + config.baseUrl);
result.resolve();
});
} else {
@ -111,7 +125,7 @@ define(function (require, exports, module) {
* @param {function} processExtension
* @return {!$.Promise} A promise object that is resolved when all extensions complete loading.
*/
function _loadAll(directory, baseUrl, entryPoint, processExtension) {
function _loadAll(directory, config, entryPoint, processExtension) {
var result = new $.Deferred();
NativeFileSystem.requestNativeFileSystem(directory,
@ -130,7 +144,11 @@ define(function (require, exports, module) {
}
Async.doInParallel(extensions, function (item) {
return processExtension(item, baseUrl + "/" + item, entryPoint);
var extConfig = {
baseUrl: config.baseUrl + "/" + item,
paths: config.paths
};
return processExtension(item, extConfig, entryPoint);
}).done(function () {
result.resolve();
}).fail(function () {
@ -158,7 +176,7 @@ define(function (require, exports, module) {
* @return {!$.Promise} A promise object that is resolved when all extensions complete loading.
*/
function loadAllExtensionsInNativeDirectory(directory, baseUrl) {
return _loadAll(directory, baseUrl, "main", loadExtension);
return _loadAll(directory, {baseUrl: baseUrl}, "main", loadExtension);
}
/**
@ -170,9 +188,20 @@ define(function (require, exports, module) {
* @return {!$.Promise} A promise object that is resolved when all extensions complete loading.
*/
function testAllExtensionsInNativeDirectory(directory, baseUrl) {
return _loadAll(directory, baseUrl, "unittests", testExtension);
var bracketsPath = FileUtils.getNativeBracketsDirectoryPath(),
config = {
baseUrl: baseUrl
};
config.paths = {
"perf": bracketsPath + "/perf",
"spec": bracketsPath + "/spec"
};
return _loadAll(directory, config, "unittests", testExtension);
}
exports.getRequireContextForExtension = getRequireContextForExtension;
exports.loadExtension = loadExtension;
exports.testExtension = testExtension;
exports.loadAllExtensionsInNativeDirectory = loadAllExtensionsInNativeDirectory;

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $ */
/**
* ExtensionUtils defines utility methods for implementing extensions.
*/
define(function (require, exports, module) {
'use strict';
/**
* Loads a style sheet relative to the extension module.
*
* @param {!module} module Module provided by RequireJS
* @param {!string} path Relative path from the extension folder to a CSS file
* @return {!$.Promise} A promise object that is resolved if the CSS file can be loaded.
*/
function loadStyleSheet(module, path) {
var url = encodeURI(module.uri.replace("main.js", "") + path),
result = new $.Deferred();
// Make a request for the same file in order to record success or failure.
// The link element's onload and onerror events are not consistently supported.
$.get(url).done(function () {
var $link = $("<link/>");
$link.attr({
type: "text/css",
rel: "stylesheet",
href: url
});
$("head").append($link[0]);
result.resolve();
}).fail(function (err) {
result.reject(err);
});
return result;
}
exports.loadStyleSheet = loadStyleSheet;
});

View File

@ -22,7 +22,7 @@
*/
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define */
/*global define, $ */
/**
* Utilities functions related to string manipulation
@ -31,6 +31,24 @@
define(function (require, exports, module) {
'use strict';
/**
* Format a string by replacing placeholder symbols with passed in arguments.
*
* Example: var formatted = StringUtils.format("Hello {0}", "World");
*
* @param {string} str The base string
* @param {...} Arguments to be substituted into the string
*
* @return {string} Formatted string
*/
function format(str) {
// arguments[0] is the base string, so we need to adjust index values here
var args = [].slice.call(arguments, 1);
return str.replace(/\{(\d+)\}/g, function (match, num) {
return typeof args[num] !== 'undefined' ? args[num] : match;
});
}
function htmlEscape(str) {
return String(str)
.replace(/&/g, '&amp;')
@ -44,6 +62,61 @@ define(function (require, exports, module) {
return str.replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1");
}
/**
* Splits the text by new line characters and returns an array of lines
* @param {string} text
* @return {Array.<string>} lines
*/
function getLines(text) {
return text.split("\n");
}
/**
* Returns a line number corresponding to an offset in some text. The text can
* be specified as a single string or as an array of strings that correspond to
* the lines of the string.
*
* Specify the text in lines when repeatedly calling the function on the same
* text in a loop. Use getLines() to divide the text into lines, then repeatedly call
* this function to compute a line number from the offset.
*
* @param {string | Array.<string>} textOrLines - string or array of lines from which
* to compute the line number from the offset
* @param {number} offset
* @return {number} line number
*/
function offsetToLineNum(textOrLines, offset) {
if ($.isArray(textOrLines)) {
var lines = textOrLines,
total = 0,
line;
for (line = 0; line < lines.length; line++) {
if (total < offset) {
// add 1 per line since /n were removed by splitting, but they needed to
// contribute to the total offset count
total += lines[line].length + 1;
} else if (total === offset) {
return line;
} else {
return line - 1;
}
}
// if offset is NOT over the total then offset is in the last line
if (offset <= total) {
return line - 1;
} else {
return undefined;
}
} else {
return textOrLines.substr(0, offset).split("\n").length - 1;
}
}
// Define public API
exports.format = format;
exports.htmlEscape = htmlEscape;
exports.regexEscape = regexEscape;
exports.getLines = getLines;
exports.offsetToLineNum = offsetToLineNum;
});

View File

@ -278,6 +278,47 @@ define(function (require, exports, module) {
});
}
/**
* Within a scrolling DOMElement, if necessary, scroll element into viewport.
*
* To Perform the minimum amount of scrolling necessary, cases should be handled as follows:
* - element already completely in view : no scrolling
* - element above viewport : scroll view so element is at top
* - element left of viewport : scroll view so element is at left
* - element below viewport : scroll view so element is at bottom
* - element right of viewport : scroll view so element is at right
*
* Assumptions:
* - $view is a scrolling container
*
* @param {!DOMElement} $view - A jQuery scrolling container
* @param {!DOMElement} $element - A jQuery element
* @param {?boolean} scrollHorizontal - whether to also scroll horizonally
*/
function scrollElementIntoView($view, $element, scrollHorizontal) {
var viewOffset = $view.offset(),
viewScroller = $view.get(0),
element = $element.get(0),
elementOffset = $element.offset();
// scroll minimum amount
if (elementOffset.top + $element.height() >= (viewOffset.top + $view.height())) {
// below viewport
element.scrollIntoView(false);
} else if (elementOffset.top <= viewOffset.top) {
// above viewport
element.scrollIntoView(true);
}
if (scrollHorizontal) {
if (elementOffset.left < 0) {
$view.scrollLeft($view.scrollLeft() + elementOffset.left);
} else if (elementOffset.left + $element.width() >= viewOffset.left + $view.width()) {
$view.scrollLeft(elementOffset.left - viewOffset.left);
}
}
}
// handle all resize handlers in a single listener
$(window).resize(_handleResize);
@ -286,4 +327,5 @@ define(function (require, exports, module) {
exports.addScrollerShadow = addScrollerShadow;
exports.removeScrollerShadow = removeScrollerShadow;
exports.sidebarList = sidebarList;
exports.scrollElementIntoView = scrollElementIntoView;
});

View File

@ -33,6 +33,19 @@ define(function (require, exports, module) {
ProjectManager = require("project/ProjectManager"),
EditorManager = require("editor/EditorManager");
/**
* @const
* @type {string}
*/
var DYNAMIC_FONT_STYLE_ID = "codemirror-dynamic-fonts";
function _removeDynamicFontSize(refresh) {
$("#" + DYNAMIC_FONT_STYLE_ID).remove();
if (refresh) {
EditorManager.getCurrentFullEditor().refreshAll();
}
}
/**
* @private
* Increases or decreases the editor's font size.
@ -79,8 +92,8 @@ define(function (require, exports, module) {
}
// It's necessary to inject a new rule to address all editors.
$("#" + styleId).remove();
var style = $("<style type='text/css'></style>").attr("id", styleId);
_removeDynamicFontSize(false);
var style = $("<style type='text/css'></style>").attr("id", DYNAMIC_FONT_STYLE_ID);
style.html(".CodeMirror-scroll {" +
"font-size: " + fsStr + " !important;" +
"line-height: " + lhStr + " !important;}");
@ -109,7 +122,11 @@ define(function (require, exports, module) {
_adjustFontSize(-1);
}
function _handleRestoreFontSize() {
_removeDynamicFontSize(true);
}
CommandManager.register(Strings.CMD_INCREASE_FONT_SIZE, Commands.VIEW_INCREASE_FONT_SIZE, _handleIncreaseFontSize);
CommandManager.register(Strings.CMD_DECREASE_FONT_SIZE, Commands.VIEW_DECREASE_FONT_SIZE, _handleDecreaseFontSize);
CommandManager.register(Strings.CMD_RESTORE_FONT_SIZE, Commands.VIEW_RESTORE_FONT_SIZE, _handleRestoreFontSize);
});

View File

@ -1,5 +1,6 @@
body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
/* make the container go all the way to the bottom of the topbar */
padding-top: 60px;
}
pre {
overflow-x: scroll;
@ -9,3 +10,8 @@ pre {
.nav .badge {
float: right;
}
/* Make fail (red) color bolder so easier to see */
.label-important, .badge-important {
background-color: #F00;
}

View File

@ -3,7 +3,7 @@
(function ($) {
'use strict';
jasmine.BootstrapReporter = function (doc) {
jasmine.BootstrapReporter = function (doc, filter) {
this._paramMap = {};
this.document = doc || document;
this._env = jasmine.getEnv();
@ -20,6 +20,12 @@
}
this._runAll = this._paramMap.spec === "All";
// _topLevelFilter is applied first - selects Performance vs. Unit test suites
this._topLevelFilter = filter;
// Jasmine's runner uses the specFilter to choose which tests to run.
// If you selected an option other than "All" this will be a subset of all tests loaded.
this._env.specFilter = this.createSpecFilter(this._paramMap.spec);
this._runner = this._env.currentRunner();
@ -44,8 +50,26 @@
this.$resultsContainer = $("#results-container");
};
/**
* @private
* Filters specs by full name. Applies _topLevelFilter first before checking
* for a matching starting substring.
*/
jasmine.BootstrapReporter.prototype.createSpecFilter = function (filterString) {
var self = this;
return function (spec) {
// filterString is undefined when no top-level suite is active (e.g. "All", "HTMLUtils", etc.)
// When undefined, all specs fail this filter and no tests are ran. This is by design.
// This setup allows the SpecRunner to load initially without automatically running all tests.
if (filterString === undefined) {
return false;
}
if (!self._topLevelFilter(spec)) {
return false;
}
if (filterString === "All") {
return true;
}
@ -91,7 +115,11 @@
self = this;
// count specs attached directly to this suite
count = suite.specs().length;
suite.specs().forEach(function (spec, index) {
if (self._topLevelFilter(spec)) {
count++;
}
});
// recursively count child suites
suite.suites().forEach(function (child, index) {
@ -119,11 +147,22 @@
return 0;
});
this.$suiteList.append(this._createSuiteListItem(null, this._runner.specs().length));
topLevel.forEach(function (suite, index) {
self.$suiteList.append(self._createSuiteListItem(suite, self._countSpecs(suite)));
var count = self._countSpecs(suite);
if (count > 0) {
self.$suiteList.append(self._createSuiteListItem(suite, count));
}
});
// count all speces
var allSpecsCount = 0;
$.each(this._topLevelSuiteMap, function (index, value) {
allSpecsCount += value.specCount;
});
// add an "all" top-level suite
this.$suiteList.prepend(this._createSuiteListItem(null, allSpecsCount));
};
jasmine.BootstrapReporter.prototype._showProgressBar = function (spec) {
@ -136,9 +175,9 @@
};
jasmine.BootstrapReporter.prototype.reportRunnerStarting = function (runner) {
var i,
specs = runner.specs(),
topLevelData;
var specs = runner.specs(),
topLevelData,
self = this;
// create top level suite list navigation
this._createSuiteList();
@ -153,11 +192,11 @@
this._specCount = 0;
this._specCompleteCount = 0;
for (i = 0; i < specs.length; i++) {
if (this._env.specFilter(specs[i])) {
this._specCount++;
}
specs.forEach(function (spec, index) {
if (self._env.specFilter(spec)) {
self._specCount++;
}
});
if (this._specCount) {
this._showProgressBar();
@ -239,13 +278,22 @@
jasmine.BootstrapReporter.prototype._updateStatus = function (spec) {
var data = this._getTopLevelSuiteData(spec),
allData = this._topLevelSuiteMap.All,
allData,
results;
// Top-level suite data will not exist if filtered
if (!data) {
return;
}
allData = this._topLevelSuiteMap.All;
results = spec.results();
this._updateSuiteStatus(data, results);
this._updateSuiteStatus(allData, results);
};
// Jasmine calls this function for all specs, not just filtered specs.
jasmine.BootstrapReporter.prototype.reportSpecResults = function (spec) {
var results = spec.results(),
$specLink,

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, indent: 4, maxerr: 50 */
/*global define */
define(function (require, exports, module) {
'use strict';
// Each suite or spec must have this.performance = true to be filtered properly
require("perf/Performance-test");
});

View File

@ -1,3 +0,0 @@
{
"specs": ["perf/Performance-test.js"]
}

View File

@ -27,9 +27,9 @@
<head>
<title>Jasmine Spec Runner</title>
<link href="BootstrapReporter.css" rel="stylesheet">
<link href="lib/bootstrap2/css/bootstrap.min.css" rel="stylesheet">
<link href="lib/bootstrap2/css/bootstrap-responsive.min.css" rel="stylesheet">
<link href="BootstrapReporter.css" rel="stylesheet">
<script src="lib/jasmine-core/jasmine.js"></script>
<script src="lib/jasmine-core/jasmine-html.js"></script>

View File

@ -26,8 +26,12 @@
// Set the baseUrl to brackets/src
require.config({
baseUrl: "../src"/*,
urlArgs: "bust=" + (new Date()).getTime() // cache busting */
baseUrl: "../src",
paths: {
"test": "../test",
"perf": "../test/perf",
"spec": "../test/spec"
}
});
define(function (require, exports, module) {
@ -38,7 +42,11 @@ define(function (require, exports, module) {
ExtensionLoader = require("utils/ExtensionLoader"),
FileUtils = require("file/FileUtils"),
Menus = require("command/Menus"),
PerformanceReporter = require("perf/PerformanceReporter.js").PerformanceReporter;
PerformanceReporter = require("perf/PerformanceReporter").PerformanceReporter;
// Load both top-level suites. Filtering is applied at the top-level as a filter to BootstrapReporter.
require("test/UnitTestSuite");
require("test/PerformanceTestSuite");
var suite;
@ -119,8 +127,6 @@ define(function (require, exports, module) {
currentWindowOnload();
}
jasmineEnv.addReporter(new jasmine.BootstrapReporter(document));
$("#show-dev-tools").click(function () {
brackets.app.showDeveloperTools();
});
@ -130,8 +136,30 @@ define(function (require, exports, module) {
suite = getParamMap().suite || localStorage.getItem("SpecRunner.suite") || "UnitTestSuite";
// Create a top-level filter to show/hide performance tests
var isPerfSuite = (suite === "PerformanceTestSuite"),
performanceFilter = function (spec) {
if (spec.performance === true) {
return isPerfSuite;
}
var suite = spec.suite;
while (suite) {
if (suite.performance === true) {
return isPerfSuite;
}
suite = suite.parentSuite;
}
return !isPerfSuite;
};
jasmineEnv.addReporter(new jasmine.BootstrapReporter(document, performanceFilter));
// add performance reporting
if (suite === "PerformanceTestSuite") {
if (isPerfSuite) {
jasmineEnv.addReporter(new PerformanceReporter());
}
@ -139,14 +167,7 @@ define(function (require, exports, module) {
$("#" + suite).closest("li").toggleClass("active", true);
var jsonResult = $.getJSON(suite + ".json");
jsonResult.done(function (data) {
// load specs and run jasmine
require(data.specs, function () {
jasmineEnv.execute();
});
});
};
}

48
test/UnitTestSuite.js Normal file
View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, indent: 4, maxerr: 50 */
/*global define */
define(function (require, exports, module) {
'use strict';
require("spec/CodeHintUtils-test");
require("spec/CommandManager-test");
require("spec/CSSUtils-test");
require("spec/Document-test");
require("spec/DocumentCommandHandlers-test");
require("spec/Editor-test");
require("spec/EditorCommandHandlers-test");
require("spec/FileIndexManager-test");
require("spec/InlineEditorProviders-test");
require("spec/KeyBindingManager-test");
require("spec/LiveDevelopment-test");
require("spec/LowLevelFileIO-test");
require("spec/Menu-test");
require("spec/MultiRangeInlineEditor-test");
require("spec/NativeFileSystem-test");
require("spec/PreferencesManager-test");
require("spec/ProjectManager-test");
require("spec/ViewUtils-test");
require("spec/WorkingSetView-test");
});

View File

@ -1,21 +0,0 @@
{
"specs": ["spec/LowLevelFileIO-test.js",
"spec/DocumentCommandHandlers-test.js",
"spec/NativeFileSystem-test.js",
"spec/PreferencesManager-test.js",
"spec/Editor-test.js",
"spec/Document-test.js",
"spec/EditorCommandHandlers-test.js",
"spec/ProjectManager-test.js",
"spec/WorkingSetView-test.js",
"spec/KeyBindingManager-test.js",
"spec/FileIndexManager-test.js",
"spec/CodeHintUtils-test.js",
"spec/CSSUtils-test.js",
"spec/InlineEditorProviders-test.js",
"spec/MultiRangeInlineEditor-test.js",
"spec/LiveDevelopment-test.js",
"spec/CommandManager-test.js",
"spec/ViewUtils-test.js",
"spec/Menu-test.js"]
}

View File

@ -36,13 +36,15 @@ define(function (require, exports, module) {
PerfUtils, // loaded from brackets.test
JSLintUtils, // loaded from brackets.test
DocumentManager, // loaded from brackets.test
SpecRunnerUtils = require("../spec/SpecRunnerUtils.js"),
PerformanceReporter = require("../perf/PerformanceReporter.js");
SpecRunnerUtils = require("spec/SpecRunnerUtils"),
PerformanceReporter = require("perf/PerformanceReporter");
var jsLintPrevSetting;
describe("Performance Tests", function () {
this.performance = true;
// Note: this tests assumes that the "brackets-scenario" repo is in the same folder
// as the "brackets-app"
//

View File

@ -27,7 +27,7 @@
define(function (require, exports, module) {
'use strict';
var SpecRunnerUtils = require("spec/SpecRunnerUtils.js");
var SpecRunnerUtils = require("spec/SpecRunnerUtils");
var records = {};

View File

@ -40,10 +40,11 @@ define(function (require, exports, module) {
beforeEach(function () {
executed = false;
CommandManager._reset();
CommandManager._testReset();
});
afterEach(function () {
CommandManager._testRestore();
});
it("register and get a command and validate parameters", function () {

View File

@ -123,10 +123,10 @@ define(function (require, exports, module) {
// FIXME (issue #249): Need an event or something a little more reliable...
waitsFor(
function () {
return testWindow.brackets && testWindow.brackets.test;
function isBracketsDoneLoading() {
return testWindow.brackets && testWindow.brackets.test && testWindow.brackets.test.doneLoading;
},
5000
10000
);
runs(function () {