/
githubmirror
/
brackets
Обзор
Документация
Войти
/
githubmirror
/
brackets
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/spec/InlineEditorProviders-test.js
1 685 строк
75 KB
Martin Zagora
fix unit tests because of new sticky flag
22 фев 2017, 04:22
22 фев 2017, 04:22
c590f02
Код
Авторство
О чём код?
/* * Copyright (c) 2012 - present 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. * */ /*global describe, it, xit, expect, beforeEach, afterEach, waits, waitsFor, waitsForDone, waitsForFail, runs, beforeFirst, afterLast */ define(function (require, exports, module) { 'use strict'; var CommandManager, // loaded from brackets.test Commands, // loaded from brackets.test EditorManager, // loaded from brackets.test FileSyncManager, // loaded from brackets.test DocumentManager, // loaded from brackets.test MainViewManager, // loaded from brackets.test FileViewController, // loaded from brackets.test InlineWidget = require("editor/InlineWidget").InlineWidget, Dialogs = require("widgets/Dialogs"), KeyEvent = require("utils/KeyEvent"), FileUtils = require("file/FileUtils"), SpecRunnerUtils = require("spec/SpecRunnerUtils"), Strings = require("strings"); // Helper functions for testing cursor position / selection range function fixPos(pos) { if (!("sticky" in pos)) { pos.sticky = null; } return pos; } describe("InlineEditorProviders", function () { this.category = "integration"; var testPath = SpecRunnerUtils.getTestPath("/spec/InlineEditorProviders-test-files"), tempPath = SpecRunnerUtils.getTempDirectory(), testWindow, infos = {}; function toRange(startLine, endLine) { return {startLine: startLine, endLine: endLine}; } function rewriteProject() { var result = new $.Deferred(), options = { parseOffsets : true, infos : infos, removePrefix : true }; SpecRunnerUtils.copyPath(testPath, tempPath, options).done(function () { result.resolve(); }).fail(function () { result.reject(); }); return result.promise(); } /** * Performs setup for an inline editor test. Parses offsets (saved to infos) for all files in * the test project (testPath) and saves files back to disk without offset markup. * When finished, open an editor for the specified project relative file path * then attempts opens an inline editor at the given offset. Installs an after() * function restore all file content back to original state with offset markup. * * @param {string} openFile Project relative file path to open in a main editor. * @param {number} openOffset The offset index location within openFile to open an inline editor. * @param {boolean=} expectInline Use false to verify that an inline editor should not be opened. Omit otherwise. * @param {Array<{string}>=} documentList Optional array of files to open in working set */ function initInlineTest(openFile, openOffset, expectInline, documentList) { var editor; documentList = documentList || []; expectInline = (expectInline !== undefined) ? expectInline : true; runs(function () { documentList.push(openFile); waitsForDone(SpecRunnerUtils.openProjectFiles(documentList), "FILE_OPEN timeout", 1000); }); runs(function () { editor = EditorManager.getCurrentFullEditor(); // open inline editor at specified offset index var inlineEditorResult = SpecRunnerUtils.toggleQuickEditAtOffset( editor, infos[openFile].offsets[openOffset] ); if (expectInline) { waitsForDone(inlineEditorResult, "inline editor opened", 1000); } else { waitsForFail(inlineEditorResult, "inline editor not opened", 1000); } }); runs(function () { if (expectInline) { var inlineWidgets = editor.getInlineWidgets(); expect(inlineWidgets.length).toBe(1); // By the time we're called, the content of the widget should be in the DOM and have a nontrivial height. expect($.contains(testWindow.document.documentElement, inlineWidgets[0].htmlContent)).toBe(true); expect(inlineWidgets[0].$htmlContent.height()).toBeGreaterThan(50); } editor = null; }); } // Utilities for testing Editor state function expectText(editor) { return expect(editor._codeMirror.getValue()); } function expectTextToBeEqual(editor1, editor2) { expect(editor1._codeMirror.getValue()).toBe(editor2._codeMirror.getValue()); } function isLineHidden(cm, lineNum) { var markers = cm.findMarksAt({line: lineNum, pos: 0}), i; for (i = 0; i < markers.length; i++) { if (markers[i].collapsed) { return true; } } return false; } function expectPopoverMessageWithText(text) { var $popover; runs(function () { waitsFor(function () { $popover = testWindow.$(".popover-message"); return $popover.length === 1; }, "Expect popover window"); }); runs(function () { var popoverText = testWindow.$(".text", $popover).html(); expect(popoverText).toEqual(text); }); } function getBounds(object, useOffset) { var left = (useOffset ? object.offset().left : parseInt(object.css("left"), 10)), top = (useOffset ? object.offset().top : parseInt(object.css("top"), 10)); return { left: left, top: top, right: left + object.outerWidth(), bottom: top + object.outerHeight() }; } function boundsInsideWindow(object) { // For the popover, we can't use offset(), because jQuery gets confused by the // scale factor and transform origin that the animation uses. Instead, we rely on // the fact that its offset parent is body, and just test its explicit left/top values. var bounds = getBounds(object, false), editorBounds = getBounds(testWindow.$("#editor-holder"), true); return bounds.left >= editorBounds.left && bounds.right <= editorBounds.right && bounds.top >= editorBounds.top && bounds.bottom <= editorBounds.bottom; } function toggleOption(commandID, text) { runs(function () { var promise = CommandManager.execute(commandID); waitsForDone(promise, text); }); } /* * Note that the bulk of selector matching tests are in CSSutils-test.js. * These tests are primarily focused on the InlineEditorProvider module. */ describe("htmlToCSSProvider", function () { beforeFirst(function () { SpecRunnerUtils.createTempDirectory(); // rewrite the project for each spec runs(function () { waitsForDone(rewriteProject(), "rewriteProject timeout", 1000); }); // Create a new window that will be shared by ALL tests in this spec. SpecRunnerUtils.createTestWindowAndRun(this, function (w) { testWindow = w; // Load module instances from brackets.test CommandManager = testWindow.brackets.test.CommandManager; Commands = testWindow.brackets.test.Commands; EditorManager = testWindow.brackets.test.EditorManager; FileSyncManager = testWindow.brackets.test.FileSyncManager; DocumentManager = testWindow.brackets.test.DocumentManager; MainViewManager = testWindow.brackets.test.MainViewManager; FileViewController = testWindow.brackets.test.FileViewController; }); }); afterLast(function () { testWindow = null; Commands = null; EditorManager = null; FileSyncManager = null; DocumentManager = null; MainViewManager = null; FileViewController = null; SpecRunnerUtils.closeTestWindow(); SpecRunnerUtils.removeTempDirectory(); }); beforeEach(function () { this.addMatchers({ toHaveInlineEditorRange: function (range) { var i = 0, editor = this.actual, hidden, lineCount = editor.lineCount(), shouldHide = [], shouldShow = [], startLine = range.startLine, endLine = range.endLine, visibleRangeCheck; for (i = 0; i < lineCount; i++) { hidden = isLineHidden(editor._codeMirror, i); if (i < startLine) { if (!hidden) { shouldHide.push(i); // lines above start line should be hidden } } else if ((i >= startLine) && (i <= endLine)) { if (hidden) { shouldShow.push(i); // lines in the range should be visible } } else if (i > endLine) { if (!hidden) { shouldHide.push(i); // lines below end line should be hidden } } } visibleRangeCheck = (editor._visibleRange.startLine === startLine) && (editor._visibleRange.endLine === endLine); this.message = function () { var msg = ""; if (shouldHide.length > 0) { msg += "Expected inline editor to hide [" + shouldHide.toString() + "].\n"; } if (shouldShow.length > 0) { msg += "Expected inline editor to show [" + shouldShow.toString() + "].\n"; } if (!visibleRangeCheck) { msg += "Editor._visibleRange [" + editor._visibleRange.startLine + "," + editor._visibleRange.endLine + "] should be [" + startLine + "," + endLine + "]."; } return msg; }; return (shouldHide.length === 0) && (shouldShow.length === 0) && visibleRangeCheck; } }); SpecRunnerUtils.loadProjectInTestWindow(tempPath); }); afterEach(function () { //debug visual confirmation of inline editor //waits(1000); // revert files to original content with offset markup testWindow.closeAllFiles(); }); it("should open a type selector and show correct range including the embedded php", function () { initInlineTest("test1.php", 1); runs(function () { var inlineWidget = EditorManager.getCurrentFullEditor().getInlineWidgets()[0]; var inlinePos = inlineWidget.editor.getCursorPos(); // verify cursor position and displayed range in inline editor expect(fixPos(inlinePos)).toEqual(fixPos(infos["test1.php"].offsets[0])); expect(inlineWidget.editor).toHaveInlineEditorRange(toRange(4, 8)); inlineWidget = null; }); }); it("should open a type selector on opening tag", function () { initInlineTest("test1.html", 0); runs(function () { var inlineWidget = EditorManager.getCurrentFullEditor().getInlineWidgets()[0]; var inlinePos = inlineWidget.editor.getCursorPos(); // verify cursor position in inline editor expect(fixPos(inlinePos)).toEqual(fixPos(infos["test1.css"].offsets[0])); inlineWidget = null; }); }); it("should open a type selector on closing tag", function () { initInlineTest("test1.html", 9); runs(function () { var inlineWidget = EditorManager.getCurrentFullEditor().getInlineWidgets()[0]; var inlinePos = inlineWidget.editor.getCursorPos(); // verify cursor position in inline editor expect(fixPos(inlinePos)).toEqual(fixPos(infos["test1.css"].offsets[0])); inlineWidget = null; }); }); it("should open a class selector", function () { initInlineTest("test1.html", 1); runs(function () { var inlineWidget = EditorManager.getCurrentFullEditor().getInlineWidgets()[0]; var inlinePos = inlineWidget.editor.getCursorPos(); // verify cursor position in inline editor expect(fixPos(inlinePos)).toEqual(fixPos(infos["test1.css"].offsets[1])); inlineWidget = null; }); }); it("should also open a class selector", function () { initInlineTest("test1.html", 7); runs(function () { var inlineWidget = EditorManager.getCurrentFullEditor().getInlineWidgets()[0]; var inlinePos = inlineWidget.editor.getCursorPos(); // verify cursor position in inline editor expect(fixPos(inlinePos)).toEqual(fixPos(infos["test1.css"].offsets[1])); inlineWidget = null; }); }); it("should open an embedded class selector", function () { initInlineTest("test1.html", 10); runs(function () { var inlineWidget = EditorManager.getCurrentFullEditor().getInlineWidgets()[0]; var inlinePos = inlineWidget.editor.getCursorPos(); // verify cursor position in inline editor expect(fixPos(inlinePos)).toEqual(fixPos(infos["test1.html"].offsets[11])); inlineWidget = null; }); }); it("should open an id selector", function () { initInlineTest("test1.html", 2); runs(function () { var inlineWidget = EditorManager.getCurrentFullEditor().getInlineWidgets()[0]; var inlinePos = inlineWidget.editor.getCursorPos(); // verify cursor position in inline editor expect(fixPos(inlinePos)).toEqual(fixPos(infos["test1.css"].offsets[2])); inlineWidget = null; }); }); it("should work with multiple classes when the cursor is on the first class in the list", function () { initInlineTest("test1.html", 14); runs(function () { var inlineWidget = EditorManager.getCurrentFullEditor().getInlineWidgets()[0]; var inlinePos = inlineWidget.editor.getCursorPos(); // verify cursor position in inline editor expect(inlinePos).toEqual(infos["test1.css"].offsets[1]); inlineWidget = null; }); }); it("should work when the cursor is between classes", function () { initInlineTest("test1.html", 15, false); runs(function () { // verify no inline editor open expect(EditorManager.getCurrentFullEditor().getInlineWidgets().length).toBe(0); // verify popover message is displayed with correct string expectPopoverMessageWithText(Strings.ERROR_CSSQUICKEDIT_BETWEENCLASSES); // verify popover message is automatically dismissed after short wait // current delay is 5 sec + 0.5 sec fade-out transition waits(6000); }); }); it("should work with multiple classes when the cursor is on the first class in the list", function () { initInlineTest("test1.html", 16); runs(function () { var inlineWidget = EditorManager.getCurrentFullEditor().getInlineWidgets()[0]; var inlinePos = inlineWidget.editor.getCursorPos(); // verify cursor position in inline editor expect(fixPos(inlinePos)).toEqual(fixPos(infos["test1.css"].offsets[8])); inlineWidget = null; }); }); it("should close, then remove the inline widget and restore focus", function () { initInlineTest("test1.html", 0); var hostEditor, inlineWidget, inlinePos, savedPos; runs(function () { hostEditor = EditorManager.getCurrentFullEditor(); savedPos = hostEditor.getCursorPos(); inlineWidget = hostEditor.getInlineWidgets()[0]; inlinePos = inlineWidget.editor.getCursorPos(); // verify cursor position & focus in inline editor expect(inlinePos).toEqual(infos["test1.css"].offsets[0]); expect(inlineWidget.hasFocus()).toEqual(true); expect(hostEditor.hasFocus()).toEqual(false); // close the editor waitsForDone(EditorManager.closeInlineWidget(hostEditor, inlineWidget), "closing inline widget"); }); runs(function () { // verify no inline widgets in list expect(hostEditor.getInlineWidgets().length).toBe(0); // verify that the inline widget's content has been removed from the DOM expect($.contains(testWindow.document.documentElement, inlineWidget.htmlContent)).toBe(false); // verify full editor cursor & focus restored expect(savedPos).toEqual(hostEditor.getCursorPos()); expect(hostEditor.hasFocus()).toEqual(true); hostEditor = inlineWidget = null; }); }); it("should close inline widget on Esc Key", function () { initInlineTest("test1.html", 0); runs(function () { var hostEditor = EditorManager.getCurrentFullEditor(), inlineWidget = hostEditor.getInlineWidgets()[0]; // verify inline widget expect(hostEditor.getInlineWidgets().length).toBe(1); // close the editor by simulating Esc key var key = KeyEvent.DOM_VK_ESCAPE, doc = testWindow.document, element = doc.getElementsByClassName("inline-widget")[0]; SpecRunnerUtils.simulateKeyEvent(key, "keydown", element); // verify no inline widgets expect(hostEditor.getInlineWidgets().length).toBe(0); doc = hostEditor = inlineWidget = null; }); }); it("should not open an inline editor when positioned on textContent", function () { initInlineTest("test1.html", 3, false); runs(function () { // verify no inline editor open expect(EditorManager.getCurrentFullEditor().getInlineWidgets().length).toBe(0); // verify popover message is displayed with correct string expectPopoverMessageWithText(Strings.ERROR_QUICK_EDIT_PROVIDER_NOT_FOUND); }); }); // This recently added test is broken by a more recent change in CM, so disabling this test. // See discussion in https://github.com/adobe/brackets/issues/8344 // The call to `waits(4000)` in this test seems to dismiss the popover xit("should not open an inline editor when positioned on title attribute", function () { initInlineTest("test1.html", 12, false); runs(function () { // verify no inline editor open expect(EditorManager.getCurrentFullEditor().getInlineWidgets().length).toBe(0); // verify popover message is displayed with correct string expectPopoverMessageWithText(Strings.ERROR_CSSQUICKEDIT_UNSUPPORTEDATTR); // verify popover message is still open after 4 sec waits(4000); }); runs(function () { // verify popover message var $popover = testWindow.$(".popover-message"); expect($popover.length).toEqual(1); }); runs(function () { // verify popover message is automatically dismissed after 2 more seconds // total delay is 5 sec + 0.5 sec fade-out transition waits(2000); }); runs(function () { // verify no popover message var $popover = testWindow.$(".popover-message"); expect($popover.length).toEqual(0); }); }); it("should not open an inline editor when positioned on a tag in a comment", function () { initInlineTest("test1.html", 4, false); runs(function () { // verify no inline editor open expect(EditorManager.getCurrentFullEditor().getInlineWidgets().length).toBe(0); }); }); it("should open an inline editor when positioned on the non standard html tag <to>, but no content from @keyframes", function () { initInlineTest("test1.html", 18); runs(function () { // verify inline editor is open for adding a new rule expect(EditorManager.getCurrentFullEditor().getInlineWidgets().length).toBe(1); var inlineEditor = EditorManager.getCurrentFullEditor().getInlineWidgets()[0].editor; expect(inlineEditor).toBe(null); }); }); it("should close first popover message before opening another one", function () { var editor, openFile = "test1.html"; runs(function () { waitsForDone(SpecRunnerUtils.openProjectFiles([openFile]), "FILE_OPEN timeout", 1000); }); runs(function () { editor = EditorManager.getCurrentFullEditor(); // attempt to open inline editor at specified offset index var inlineEditorResult = SpecRunnerUtils.toggleQuickEditAtOffset( editor, infos[openFile].offsets[3] ); waitsForFail(inlineEditorResult, "inline editor not opened", 1000); }); runs(function () { // attempt to open another inline editor at a different offset index var inlineEditorResult = SpecRunnerUtils.toggleQuickEditAtOffset( editor, infos[openFile].offsets[12] ); waitsForFail(inlineEditorResult, "inline editor not opened", 1000); }); runs(function () { // verify only 1 popover message var $popover = testWindow.$(".popover-message"); expect($popover.length).toEqual(1); }); }); it("should close popover message on selection change", function () { var editor, openFile = "test1.html"; runs(function () { waitsForDone(SpecRunnerUtils.openProjectFiles([openFile]), "FILE_OPEN timeout", 1000); }); runs(function () { editor = EditorManager.getCurrentFullEditor(); // attempt to open inline editor at specified offset index var inlineEditorResult = SpecRunnerUtils.toggleQuickEditAtOffset( editor, infos[openFile].offsets[3] ); waitsForFail(inlineEditorResult, "inline editor not opened", 1000); }); runs(function () { // change selection var offset = infos[openFile].offsets[12]; editor.setCursorPos(offset.line, offset.ch); // verify no popover message var $popover = testWindow.$(".popover-message"); expect($popover.length).toEqual(0); }); }); it("should position message popover inside left edge of window", function () { var $popover; initInlineTest("test1.html", 11, false); runs(function () { $popover = testWindow.$(".popover-message"); expect($popover.length).toEqual(1); }); runs(function () { expect(boundsInsideWindow($popover)).toBeTruthy(); }); }); it("should position message popover inside top edge of window", function () { var $popover; initInlineTest("test1.html", 3, false); runs(function () { $popover = testWindow.$(".popover-message"); expect($popover.length).toEqual(1); }); runs(function () { expect(boundsInsideWindow($popover)).toBeTruthy(); }); }); it("should scroll cursor into view and position message popover inside right edge of window", function () { var $popover, editor, openFile = "test1.html"; runs(function () { // Turn off word wrap for next tests toggleOption(Commands.TOGGLE_WORD_WRAP, "Toggle word-wrap"); }); runs(function () { waitsForDone(SpecRunnerUtils.openProjectFiles([openFile]), "FILE_OPEN timeout", 1000); }); runs(function () { editor = EditorManager.getCurrentFullEditor(); expect(editor.getScrollPos().x).toEqual(0); // attempt to open inline editor at specified offset index var inlineEditorResult = SpecRunnerUtils.toggleQuickEditAtOffset( editor, infos[openFile].offsets[13] ); waitsForFail(inlineEditorResult, "inline editor not opened", 1000); }); runs(function () { $popover = testWindow.$(".popover-message"); expect($popover.length).toEqual(1); }); runs(function () { // Popover should be inside right edge expect(boundsInsideWindow($popover)).toBeTruthy(); // verify that page scrolled left expect(editor.getScrollPos().x).toBeGreaterThan(0); // restore word wrap toggleOption(Commands.TOGGLE_WORD_WRAP, "Toggle word-wrap"); }); }); it("should increase size based on content", function () { initInlineTest("test1.html", 1); var inlineEditor, widgetHeight; runs(function () { inlineEditor = EditorManager.getCurrentFullEditor().getInlineWidgets()[0].editor; widgetHeight = inlineEditor.totalHeight(); // verify original line count expect(inlineEditor.lineCount()).toBe(25); // change inline editor content var newLines = ".bar {\ncolor: #f00;\n}\n.cat {\ncolor: #f00;\n}"; // insert new lines at current cursor position // can't mutate document directly at this point inlineEditor._codeMirror.replaceRange( newLines, inlineEditor.getCursorPos() ); // verify widget resizes when contents is changed expect(inlineEditor.lineCount()).toBe(30); expect(inlineEditor.totalHeight()).toBeGreaterThan(widgetHeight); inlineEditor = null; }); }); it("should decrease size based on content", function () { initInlineTest("test1.html", 1); var inlineEditor, widgetHeight; runs(function () { inlineEditor = EditorManager.getCurrentFullEditor().getInlineWidgets()[0].editor; widgetHeight = inlineEditor.totalHeight(); // verify original line count expect(inlineEditor.lineCount()).toBe(25); // replace the entire .foo rule with an empty string // set text on the editor, can't mutate document directly at this point inlineEditor._codeMirror.replaceRange( "", inlineEditor.getCursorPos(), infos["test1.css"].offsets[3] ); // verify widget resizes when contents is changed expect(inlineEditor.lineCount()).toBe(23); expect(inlineEditor.totalHeight()).toBeLessThan(widgetHeight); inlineEditor = null; }); }); it("should save changes in the inline editor", function () { initInlineTest("test1.html", 1); var err = false, hostEditor, inlineEditor, newText = "\n/* jasmine was here */", savedText; this.after(function () { // rewrite the project after saving changes runs(function () { waitsForDone(rewriteProject(), "rewriteProject timeout", 1000); }); }); runs(function () { hostEditor = EditorManager.getCurrentFullEditor(); inlineEditor = hostEditor.getInlineWidgets()[0].editor; // insert text at the inline editor's cursor position // can't mutate document directly at this point inlineEditor._codeMirror.replaceRange(newText, inlineEditor.getCursorPos()); // we're going to compare this to disk later, so pass true to get non-normalized line endings newText = inlineEditor.document.getText(true); // verify isDirty flag expect(inlineEditor.document.isDirty).toBeTruthy(); expect(hostEditor.document.isDirty).toBeFalsy(); // verify that the dirty dot is visible in the UI expect(testWindow.$(".dirty-indicator", hostEditor.getInlineWidgets()[0].$htmlContent).width()).not.toEqual(0); // verify focus is in inline editor expect(inlineEditor.hasFocus()).toBeTruthy(); // execute file save command waitsForDone(testWindow.executeCommand(Commands.FILE_SAVE), "save timeout", 1000); }); runs(function () { // verify focus is still in inline editor expect(inlineEditor.hasFocus()).toBeTruthy(); // read saved file contents FileUtils.readAsText(inlineEditor.document.file).done(function (text) { savedText = text; }).fail(function () { err = true; }); }); waitsFor(function () { return savedText !== undefined; }, "readAsText timeout", 1000); runs(function () { expect(savedText).toEqual(newText); // verify isDirty flag expect(inlineEditor.document.isDirty).toBeFalsy(); expect(hostEditor.document.isDirty).toBeFalsy(); // verify that the dirty dot is hidden in the UI expect(testWindow.$(".dirty-indicator", hostEditor.getInlineWidgets()[0].$htmlContent).width()).toEqual(0); inlineEditor = hostEditor = null; }); }); it("should not save changes in the host editor", function () { initInlineTest("test1.html", 1); var err = false, hostEditor, inlineEditor, newInlineText = "/* jasmine was inline */\n", newHostText = "/* jasmine was here */\n", savedInlineText, savedHostText; this.after(function () { // rewrite the project after saving changes runs(function () { waitsForDone(rewriteProject(), "rewriteProject timeout", 1000); }); }); runs(function () { hostEditor = EditorManager.getCurrentFullEditor(); inlineEditor = hostEditor.getInlineWidgets()[0].editor; // insert text at the host editor's cursor position hostEditor._codeMirror.replaceRange(newHostText, hostEditor.getCursorPos()); // insert text at the inline editor's cursor position // can't mutate document directly at this point inlineEditor._codeMirror.replaceRange(newInlineText, inlineEditor.getCursorPos()); // we're going to compare this to disk later, so pass true to get non-normalized line endings newInlineText = inlineEditor.document.getText(true); // verify isDirty flag expect(inlineEditor.document.isDirty).toBeTruthy(); expect(hostEditor.document.isDirty).toBeTruthy(); // verify focus is in inline editor expect(inlineEditor.hasFocus()).toBeTruthy(); // execute file save command waitsForDone(testWindow.executeCommand(Commands.FILE_SAVE), "save timeout", 1000); }); runs(function () { // read saved inline file contents FileUtils.readAsText(inlineEditor.document.file).done(function (text) { savedInlineText = text; }).fail(function () { err = true; }); // read saved host editor file contents FileUtils.readAsText(hostEditor.document.file).done(function (text) { savedHostText = text; }).fail(function () { err = true; }); }); waitsFor(function () { return savedInlineText !== undefined && savedHostText !== undefined; }, "readAsText timeout", 1000); runs(function () { expect(savedInlineText).toEqual(newInlineText); expect(savedHostText).toEqual(infos["test1.html"].text); // i.e, should be unchanged // verify isDirty flag expect(inlineEditor.document.isDirty).toBeFalsy(); expect(hostEditor.document.isDirty).toBeTruthy(); inlineEditor = hostEditor = null; }); }); describe("Inline Editor syncing from disk", function () { it("should close inline editor when file deleted on disk", function () { // Create an expendable CSS file var fileToWrite; runs(function () { // Important: must create file using test window's FS so that it sees the new file right away var promise = SpecRunnerUtils.createTextFile(tempPath + "/tempCSS.css", "#anotherDiv {}", testWindow.brackets.test.FileSystem) .done(function (entry) { fileToWrite = entry; }) .fail(function (err) { console.log(err); }); waitsForDone(promise, "writeText tempCSS.css", 1000); }); // Open inline editor for that file runs(function () { initInlineTest("test1.html", 6, true); }); // initInlineTest() inserts a waitsFor() automatically, so must end runs() block here runs(function () { var hostEditor = EditorManager.getCurrentFullEditor(); expect(hostEditor.getInlineWidgets().length).toBe(1); }); // Delete the file runs(function () { waitsForDone(SpecRunnerUtils.deletePath(fileToWrite.fullPath)); }); // Ping FileSyncManager to recognize the deletion runs(function () { FileSyncManager.syncOpenDocuments(); }); // Verify inline is now closed waitsFor(function () { var hostEditor = EditorManager.getCurrentFullEditor(); return (hostEditor.getInlineWidgets().length === 0); }, 1000); // Cleanup: initInlineTest() saves contents of all files in the project and rewrites // them back to disk at the end (to restore any stripped offset markers). But in this // case we really want the temp file to stay gone. runs(function () { delete infos["tempCSS.css"]; }); }); // FUTURE: Eventually we'll instead want it to stay open and revert to the content on disk. // Editor's syncing code can't yet handle blowing away the whole Document like that, though. it("should close inline when file is closed without saving changes", function () { initInlineTest("test1.html", 1); var newText = "\n/* jasmine was here */", hostEditor, inlineEditor, promise; runs(function () { hostEditor = EditorManager.getCurrentFullEditor(); inlineEditor = hostEditor.getInlineWidgets()[0].editor; // verify inline is open expect(hostEditor.getInlineWidgets().length).toBe(1); // insert text at the inline editor's cursor position inlineEditor._codeMirror.replaceRange(newText, inlineEditor.getCursorPos()); // verify isDirty flag expect(inlineEditor.document.isDirty).toBe(true); // close the main editor / working set entry for the inline's file promise = testWindow.executeCommand(Commands.FILE_CLOSE, {file: inlineEditor.document.file}); // synchronously click the don't save button, // asynchronously wait for the dialog to close and the Dialog's // promise to resolve. SpecRunnerUtils.clickDialogButton(Dialogs.DIALOG_BTN_DONTSAVE); }); // Wait on the command's promise also since the command performs // asynchronous tasks after the Dialog is resolved. If the command // could complete synchronously, this wait would be unnecessary. runs(function () { waitsForDone(promise); }); runs(function () { // verify inline is closed expect(hostEditor.getInlineWidgets().length).toBe(0); inlineEditor = hostEditor = null; }); }); }); describe("Bi-directional Editor Synchronizing", function () { // For these tests we *deliberately* use Editor._codeMirror instead of Document to make edits, // in order to test Editor->Document syncing (instead of Document->Editor). it("should not add an inline document to the working set without being edited", function () { initInlineTest("test1.html", 0); runs(function () { var i = MainViewManager.findInWorkingSet(MainViewManager.ACTIVE_PANE, infos["test1.css"].fileEntry.fullPath); expect(i).toEqual(-1); }); }); it("should add dirty documents to the working set", function () { initInlineTest("test1.html", 1); var inlineEditor, widgetHeight; runs(function () { inlineEditor = EditorManager.getCurrentFullEditor().getInlineWidgets()[0].editor; widgetHeight = inlineEditor.totalHeight(); // change inline editor content var newLines = ".bar {\ncolor: #f00;\n}\n.cat {\ncolor: #f00;\n}"; // insert new lines at current cursor position inlineEditor._codeMirror.replaceRange( newLines, inlineEditor.getCursorPos() ); var i = MainViewManager.findInWorkingSet(MainViewManager.ACTIVE_PANE, infos["test1.css"].fileEntry.fullPath); expect(i).toEqual(1); inlineEditor = null; }); }); it("should sync edits between full and inline editors", function () { var fullEditor, inlineEditor, newInlineText = "/* jasmine was inline */\n"; initInlineTest("test1.html", 1, true, ["test1.css"]); runs(function () { var cssPath = infos["test1.css"].fileEntry.fullPath; var cssDoc = DocumentManager.getOpenDocumentForPath(cssPath); // edit the inline editor inlineEditor = EditorManager.getCurrentFullEditor().getInlineWidgets()[0].editor; inlineEditor._codeMirror.replaceRange( newInlineText, inlineEditor.getCursorPos() ); // activate the full editor MainViewManager._edit(MainViewManager.ACTIVE_PANE, cssDoc); fullEditor = EditorManager.getCurrentFullEditor(); // sanity check expect(fullEditor).not.toBe(inlineEditor); expect(fullEditor._codeMirror).not.toBe(inlineEditor._codeMirror); // compare inline editor to full editor expectTextToBeEqual(inlineEditor, fullEditor); // make sure the text was inserted expect(fullEditor._codeMirror.getValue().indexOf(newInlineText)).toBeGreaterThan(0); // edit in the full editor and compare fullEditor._codeMirror.replaceRange( newInlineText, inlineEditor.getCursorPos() ); expectTextToBeEqual(inlineEditor, fullEditor); inlineEditor = fullEditor = null; }); }); }); describe("Inline Editor range updating", function () { var fullEditor, hostEditor, inlineEditor, newInlineText = "/* insert in full editor */\n", before, start, middle, middleOfMiddle, end, endOfEnd, after, wayafter; beforeEach(function () { initInlineTest("test1.html", 1, true, ["test1.css"]); runs(function () { var cssPath = infos["test1.css"].fileEntry.fullPath; var cssDoc = DocumentManager.getOpenDocumentForPath(cssPath); hostEditor = EditorManager.getCurrentFullEditor(); inlineEditor = hostEditor.getInlineWidgets()[0].editor; // activate the full editor MainViewManager._edit(MainViewManager.ACTIVE_PANE, cssDoc); fullEditor = EditorManager.getCurrentFullEditor(); // alias offsets to nice names before = infos["test1.css"].offsets[4]; start = infos["test1.css"].offsets[1]; middle = infos["test1.css"].offsets[5]; middleOfMiddle = {line: middle.line, ch: 3}; end = infos["test1.css"].offsets[6]; endOfEnd = infos["test1.css"].offsets[3]; after = infos["test1.css"].offsets[7]; wayafter = infos["test1.css"].offsets[2]; }); }); afterEach(function () { fullEditor = null; hostEditor = null; inlineEditor = null; }); it("should insert new line at start of range, and stay open on undo", function () { // insert new line at start of inline range--the new line should be included in // the inline fullEditor._codeMirror.replaceRange( newInlineText, start ); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line + 1)); // undo is equivalent to deleting the first line in the range: shouldn't close the editor fullEditor._codeMirror.undo(); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); }); it("should insert new line within first line of range, and stay open on undo", function () { // insert new line in middle of first line of inline range--the new line should // be included fullEditor._codeMirror.replaceRange( newInlineText, {line: start.line, ch: start.ch + 1} ); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line + 1)); // Even though the edit didn't start at the beginning of the line, the undo touches // the whole line; but deleting the first line still shouldn't close the editor fullEditor._codeMirror.undo(); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); }); it("should not close inline when undoing changes to first line in range that did not include newlines", function () { // undo is NOT deleting the entire first line in these cases, so the inline should be able to stay open // insert new text at start of inline range, without newlines--the new text should be included in // the inline fullEditor._codeMirror.replaceRange( ".bar, ", start ); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); fullEditor._codeMirror.undo(); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); // replace text at start of inline range with text without newlines fullEditor._codeMirror.replaceRange( ".bar", {line: start.line, ch: 0}, {line: start.line, ch: 4} ); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); fullEditor._codeMirror.undo(); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); // insert new text in middle of first line in range, without newlines fullEditor._codeMirror.replaceRange( "ABCD", {line: start.line, ch: 3} ); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); fullEditor._codeMirror.undo(); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); }); it("should sync deletions (and their undos) on last line in range, with or without newlines", function () { // undo is NOT deleting the entire last line in these cases, so the inline should be able to stay open // insert new line in middle of last line of inline range--the new line should // be included fullEditor._codeMirror.replaceRange( newInlineText, {line: end.line, ch: end.ch + 1} ); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line + 1)); fullEditor._codeMirror.undo(); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); // insert new line at end of last line of inline range--the new line should // be included fullEditor._codeMirror.replaceRange( newInlineText, endOfEnd ); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line + 1)); fullEditor._codeMirror.undo(); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); // replace all text on last line (without replacing trailing newline) fullEditor._codeMirror.replaceRange( "ABCD", end, endOfEnd ); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); fullEditor._codeMirror.undo(); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); }); it("should sync insertions (and their undos) in most cases without closing", function () { // insert line above inline range fullEditor._codeMirror.replaceRange( newInlineText, before ); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line + 1, end.line + 1)); fullEditor._codeMirror.undo(); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); // insert line within inline range fullEditor._codeMirror.replaceRange( newInlineText, middle ); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line + 1)); fullEditor._codeMirror.undo(); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); // insert line at end of inline range fullEditor._codeMirror.replaceRange( newInlineText, end ); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line + 1)); fullEditor._codeMirror.undo(); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); // insert line below inline range fullEditor._codeMirror.replaceRange( newInlineText, after ); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); fullEditor._codeMirror.undo(); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); }); it("should sync deletions from the full editor and update the visual range of the inline editor", function () { // delete line within inline range fullEditor._codeMirror.replaceRange("", middle, end); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line - 1)); fullEditor._codeMirror.undo(); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); // delete line below inline range fullEditor._codeMirror.replaceRange("", after, wayafter); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); fullEditor._codeMirror.undo(); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line)); }); it("should close inline when newline at beginning of range is deleted", function () { // delete all of line just above the range fullEditor._codeMirror.replaceRange("", before, start); expect(hostEditor.getInlineWidgets().length).toBe(0); }); it("should stay open when first line entirely deleted", function () { // delete all of first line in range fullEditor._codeMirror.replaceRange("", start, middle); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line - 1)); }); it("should stay open when first line and following text deleted", function () { // delete all of first line in range, plus some of the next line fullEditor._codeMirror.replaceRange("", start, middleOfMiddle); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line - 1)); }); it("should stay open when first two lines deleted", function () { // delete all of first two lines in range fullEditor._codeMirror.replaceRange("", start, end); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line - 2)); }); it("should close inline when last line entirely deleted", function () { // delete all of last line in range fullEditor._codeMirror.replaceRange("", end, after); expect(hostEditor.getInlineWidgets().length).toBe(0); }); it("should close inline when last line and preceding text deleted", function () { // delete all of last line in range, plus some of previous line fullEditor._codeMirror.replaceRange("", middleOfMiddle, after); expect(hostEditor.getInlineWidgets().length).toBe(0); }); it("should close inline when last line and more preceding text deleted", function () { // delete all of last line in range, plus some of previous two lines fullEditor._codeMirror.replaceRange("", {line: start.line, ch: 3}, after); expect(hostEditor.getInlineWidgets().length).toBe(0); }); it("should close inline when deletion spans top of range", function () { // delete a block starting before the inline and ending within it fullEditor._codeMirror.replaceRange("", {line: before.line - 1, ch: 0}, middleOfMiddle); expect(hostEditor.getInlineWidgets().length).toBe(0); }); it("should close inline when deletion spans top of range (barely)", function () { // delete a block starting before the inline and ending within it fullEditor._codeMirror.replaceRange("", before, {line: start.line, ch: 1}); expect(hostEditor.getInlineWidgets().length).toBe(0); }); it("should close inline when deletion spans bottom of range", function () { // delete a block starting within the inline and ending after it fullEditor._codeMirror.replaceRange("", middleOfMiddle, wayafter); expect(hostEditor.getInlineWidgets().length).toBe(0); }); it("should sync insertions with multiple change objects in one event", function () { // Insert two new lines: one at start of inline range, the other at the (newly // updated) end of range. The edits are batched into just one event (so the // event's changeList will have length 2); both new lines should be included in // the inline fullEditor._codeMirror.operation(function () { fullEditor._codeMirror.replaceRange( newInlineText, start ); // because the edit shifted everything down a line, 'after' is now pointing // at what 'end' used to point at (the "}" line) fullEditor._codeMirror.replaceRange( newInlineText, after ); }); expect(inlineEditor).toHaveInlineEditorRange(toRange(start.line, end.line + 2)); // TODO: can't do our usual undo + re-check range test at the end, because of // codemirror/CodeMirror bug #487 }); it("should sync multiple edits between full and inline editors", function () { var i, editor, text, newInlineText = "/* jasmine was inline */\n", newFullText = "/* full editor edit */\n"; // alternate editors while inserting new text for (i = 0; i < 10; i++) { editor = (i % 2 === 0) ? fullEditor : inlineEditor; text = (i % 2 === 0) ? newFullText : newInlineText; editor._codeMirror.replaceRange( text, editor.getCursorPos() ); expectTextToBeEqual(inlineEditor, fullEditor); } }); it("should close inline if the contents of the full editor are all deleted", function () { // verify inline is open expect(hostEditor.getInlineWidgets().length).toBe(1); // delete all text via full editor fullEditor._codeMirror.setValue(""); // verify inline is closed expect(hostEditor.getInlineWidgets().length).toBe(0); }); it("should sync after undoing and redoing an edit", function () { var originalText, editedText, newInlineText = "/* jasmine was inline */\n"; originalText = inlineEditor._codeMirror.getValue(); // edit the inline editor in the middle of the text so it doesn't // run into one of the collapsing cases inlineEditor._codeMirror.replaceRange( newInlineText, middle ); editedText = inlineEditor._codeMirror.getValue(); // compare inline editor to full editor expectTextToBeEqual(inlineEditor, fullEditor); // undo the inline editor inlineEditor._codeMirror.undo(); expectTextToBeEqual(inlineEditor, fullEditor); expectText(inlineEditor).toBe(originalText); // redo the inline editor inlineEditor._codeMirror.redo(); expectTextToBeEqual(inlineEditor, fullEditor); expectText(inlineEditor).toBe(editedText); // undo the full editor fullEditor._codeMirror.undo(); expectTextToBeEqual(inlineEditor, fullEditor); expectText(fullEditor).toBe(originalText); // redo the full editor fullEditor._codeMirror.redo(); expectTextToBeEqual(inlineEditor, fullEditor); expectText(fullEditor).toBe(editedText); }); }); describe("Multiple inline editor interaction", function () { var hostEditor, inlineEditor; beforeEach(function () { initInlineTest("test1.html", 1, true, ["test1.css"]); runs(function () { hostEditor = EditorManager.getCurrentFullEditor(); inlineEditor = hostEditor.getInlineWidgets()[0].editor; }); }); afterEach(function () { hostEditor = null; inlineEditor = null; }); it("should keep range consistent after undo/redo (bug #1031)", function () { var secondInlineOpen = false, secondInlineEditor; // open inline editor at specified offset index runs(function () { hostEditor.focus(); SpecRunnerUtils.toggleQuickEditAtOffset( hostEditor, infos["test1.html"].offsets[8] ).done(function (isOpen) { secondInlineOpen = isOpen; }); }); waitsFor(function () { return secondInlineOpen; }, "second inline open timeout", 1000); // Not sure why we have to wait in between these for the bug to occur, but we do. runs(function () { secondInlineEditor = hostEditor.getInlineWidgets()[1].editor; secondInlineEditor._codeMirror.replaceRange("\n\n\n\n\n", { line: 0, ch: 0 }); }); waits(500); runs(function () { secondInlineEditor._codeMirror.undo(); }); waits(500); runs(function () { inlineEditor._codeMirror.undo(); expect(inlineEditor).toHaveInlineEditorRange(toRange(10, 12)); secondInlineEditor = null; }); }); }); describe("Inline Editor range equal to file range", function () { var fullEditor, hostEditor, inlineEditor; beforeEach(function () { initInlineTest("test1.html", 5, true, ["testOneRuleFile.css"]); runs(function () { var cssPath = infos["testOneRuleFile.css"].fileEntry.fullPath; var cssDoc = DocumentManager.getOpenDocumentForPath(cssPath); hostEditor = EditorManager.getCurrentFullEditor(); inlineEditor = hostEditor.getInlineWidgets()[0].editor; // activate the full editor MainViewManager._edit(MainViewManager.ACTIVE_PANE, cssDoc); fullEditor = EditorManager.getCurrentFullEditor(); }); }); afterEach(function () { fullEditor = null; hostEditor = null; inlineEditor = null; }); it("should delete line at bottom and not close on undo", function () { expect(inlineEditor).toHaveInlineEditorRange(toRange(0, 2)); // delete all of last line in range fullEditor._codeMirror.replaceRange("", {line: 1, ch: 16}, {line: 2, ch: 1}); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(0, 1)); fullEditor._codeMirror.undo(); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(0, 2)); }); it("should insert new line at bottom and not close on undo", function () { expect(inlineEditor).toHaveInlineEditorRange(toRange(0, 2)); // delete all of last line in range fullEditor._codeMirror.replaceRange("\n/* New last line */", {line: 2, ch: 1}); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(0, 3)); fullEditor._codeMirror.undo(); expect(hostEditor.getInlineWidgets().length).toBe(1); expect(inlineEditor).toHaveInlineEditorRange(toRange(0, 2)); }); }); }); describe("InlineEditor provider prioritization", function () { var testWindow, testDocumentManager, testEditorManager, testMainViewManager, testDoc; function getPositiveProviderCallback(widget) { return function () { widget.called = true; return $.Deferred().resolve(widget).promise(); }; } function negativeProviderCallback() { return null; } beforeEach(function () { SpecRunnerUtils.createTestWindowAndRun(this, function (w) { var mock = SpecRunnerUtils.createMockEditor(""); testWindow = w; Commands = testWindow.brackets.test.Commands; testEditorManager = testWindow.brackets.test.EditorManager; testMainViewManager = testWindow.brackets.test.MainViewManager; testDocumentManager = testWindow.brackets.test.DocumentManager; testDoc = mock.doc; testDocumentManager.getOpenDocumentForPath = function (fullPath) { return testDoc; }; testMainViewManager._edit(testMainViewManager.ACTIVE_PANE, testDoc); }); }); afterEach(function () { SpecRunnerUtils.destroyMockEditor(testDoc); SpecRunnerUtils.closeTestWindow(); testWindow = null; Commands = null; testEditorManager = null; testDoc = null; testMainViewManager = null; testDocumentManager = null; }); it("should prefer positive higher priority providers (1)", function () { var widget0 = new InlineWidget(), widget1 = new InlineWidget(), widget2 = new InlineWidget(); testEditorManager.registerInlineEditProvider(getPositiveProviderCallback(widget0)); testEditorManager.registerInlineEditProvider(getPositiveProviderCallback(widget1), 1); testEditorManager.registerInlineEditProvider(getPositiveProviderCallback(widget2), 2); runs(function () { testWindow.executeCommand(Commands.TOGGLE_QUICK_EDIT); }); runs(function () { expect(widget0.called).toBeFalsy(); expect(widget1.called).toBeFalsy(); expect(widget2.called).toBe(true); }); }); it("should prefer positive higher priority providers (2)", function () { var widget0 = new InlineWidget(), widget1 = new InlineWidget(), widget2 = new InlineWidget(); testEditorManager.registerInlineEditProvider(getPositiveProviderCallback(widget2), 2); testEditorManager.registerInlineEditProvider(getPositiveProviderCallback(widget1), 1); testEditorManager.registerInlineEditProvider(getPositiveProviderCallback(widget0)); runs(function () { testWindow.executeCommand(Commands.TOGGLE_QUICK_EDIT); }); runs(function () { expect(widget0.called).toBeFalsy(); expect(widget1.called).toBeFalsy(); expect(widget2.called).toBe(true); }); }); it("should ignore negative higher priority providers", function () { var widget0 = new InlineWidget(), widget1 = new InlineWidget(), widget2 = new InlineWidget(); testEditorManager.registerInlineEditProvider(negativeProviderCallback, 2); testEditorManager.registerInlineEditProvider(negativeProviderCallback, 1); testEditorManager.registerInlineEditProvider(getPositiveProviderCallback(widget0)); testEditorManager.registerInlineEditProvider(getPositiveProviderCallback(widget1), -1); testEditorManager.registerInlineEditProvider(getPositiveProviderCallback(widget2), -2); runs(function () { testWindow.executeCommand(Commands.TOGGLE_QUICK_EDIT); }); runs(function () { expect(widget0.called).toBe(true); expect(widget1.called).toBeFalsy(); expect(widget2.called).toBeFalsy(); }); }); }); }); });