/
githubmirror
/
brackets
Обзор
Документация
Войти
/
githubmirror
/
brackets
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/spec/Document-test.js
600 строк
30 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 jasmine, describe, beforeFirst, afterLast, beforeEach, afterEach, it, runs, expect, waitsForDone */ define(function (require, exports, module) { 'use strict'; // Load dependent modules var CommandManager, // loaded from brackets.test Commands, // loaded from brackets.test EditorManager, // loaded from brackets.test DocumentModule, // loaded from brackets.test DocumentManager, // loaded from brackets.test MainViewManager, // loaded from brackets.test SpecRunnerUtils = require("spec/SpecRunnerUtils"); describe("Document", function () { describe("doMultipleEdits", function () { // Even though these are Document unit tests, we need to create an editor in order to // be able to test actual edit ops. var myEditor, myDocument, initialContentLines; function makeDummyLines(num) { var content = [], i; for (i = 0; i < num; i++) { content.push("this is line " + i); } return content; } beforeEach(function () { // Each line from 0-9 is 14 chars long, each line from 10-19 is 15 chars long initialContentLines = makeDummyLines(20); var mocks = SpecRunnerUtils.createMockEditor(initialContentLines.join("\n"), "unknown"); myDocument = mocks.doc; myEditor = mocks.editor; }); afterEach(function () { if (myEditor) { SpecRunnerUtils.destroyMockEditor(myDocument); myEditor = null; myDocument = null; } }); it("should do a single edit, tracking a beforeEdit selection and preserving reversed flag", function () { var result = myDocument.doMultipleEdits([{edit: {text: "new content", start: {line: 2, ch: 0}, end: {line: 2, ch: 14}}, selection: {start: {line: 2, ch: 4}, end: {line: 2, ch: 4}, reversed: true, isBeforeEdit: true}}]); initialContentLines[2] = "new content"; expect(myDocument.getText()).toEqual(initialContentLines.join("\n")); expect(result.length).toBe(1); expect(result[0].start.line).toEqual(2); expect(result[0].start.ch).toEqual(11); // end of "new content" expect(result[0].end.line).toEqual(2); expect(result[0].end.ch).toEqual(11); expect(result[0].reversed).toBe(true); }); it("should do a single edit, leaving a non-beforeEdit selection untouched and preserving reversed flag", function () { var result = myDocument.doMultipleEdits([{edit: {text: "new content", start: {line: 2, ch: 0}, end: {line: 2, ch: 14}}, selection: {start: {line: 2, ch: 4}, end: {line: 2, ch: 4}, reversed: true}}]); initialContentLines[2] = "new content"; expect(myDocument.getText()).toEqual(initialContentLines.join("\n")); expect(result.length).toBe(1); expect(result[0].start.line).toEqual(2); expect(result[0].start.ch).toEqual(4); expect(result[0].end.line).toEqual(2); expect(result[0].end.ch).toEqual(4); expect(result[0].reversed).toBe(true); }); it("should do multiple edits, fixing up isBeforeEdit selections with respect to both edits and preserving other selection attributes", function () { var result = myDocument.doMultipleEdits([ {edit: {text: "modified line 2\n", start: {line: 2, ch: 0}, end: {line: 2, ch: 14}}, selection: {start: {line: 2, ch: 4}, end: {line: 2, ch: 4}, isBeforeEdit: true, primary: true}}, {edit: {text: "modified line 4\n", start: {line: 4, ch: 0}, end: {line: 4, ch: 14}}, selection: {start: {line: 4, ch: 4}, end: {line: 4, ch: 4}, isBeforeEdit: true, reversed: true}} ]); initialContentLines[2] = "modified line 2"; initialContentLines[4] = "modified line 4"; initialContentLines.splice(5, 0, ""); initialContentLines.splice(3, 0, ""); expect(myDocument.getText()).toEqual(initialContentLines.join("\n")); expect(result.length).toBe(2); expect(result[0].start.line).toEqual(3); expect(result[0].start.ch).toEqual(0); // pushed to end of modified text expect(result[0].end.line).toEqual(3); expect(result[0].end.ch).toEqual(0); expect(result[0].primary).toBe(true); expect(result[1].start.line).toEqual(6); expect(result[1].start.ch).toEqual(0); // pushed to end of modified text and updated for both edits expect(result[1].end.line).toEqual(6); expect(result[1].end.ch).toEqual(0); expect(result[1].reversed).toBe(true); }); it("should do multiple edits, fixing up non-isBeforeEdit selections only with respect to other edits", function () { var result = myDocument.doMultipleEdits([ {edit: {text: "modified line 2\n", start: {line: 2, ch: 0}, end: {line: 2, ch: 14}}, selection: {start: {line: 2, ch: 4}, end: {line: 2, ch: 4}, primary: true}}, {edit: {text: "modified line 4\n", start: {line: 4, ch: 0}, end: {line: 4, ch: 14}}, selection: {start: {line: 4, ch: 4}, end: {line: 4, ch: 4}, reversed: true}} ]); initialContentLines[2] = "modified line 2"; initialContentLines[4] = "modified line 4"; initialContentLines.splice(5, 0, ""); initialContentLines.splice(3, 0, ""); expect(myDocument.getText()).toEqual(initialContentLines.join("\n")); expect(result.length).toBe(2); expect(result[0].start.line).toEqual(2); expect(result[0].start.ch).toEqual(4); // not modified since it's above the other edit expect(result[0].end.line).toEqual(2); expect(result[0].end.ch).toEqual(4); expect(result[0].primary).toBe(true); expect(result[1].start.line).toEqual(5); expect(result[1].start.ch).toEqual(4); // not pushed to end of modified text, but updated for previous edit expect(result[1].end.line).toEqual(5); expect(result[1].end.ch).toEqual(4); expect(result[1].reversed).toBe(true); }); it("should perform multiple changes/track multiple selections within a single edit, selections specified as isBeforeEdit", function () { var result = myDocument.doMultipleEdits([ {edit: [{text: "modified line 1", start: {line: 1, ch: 0}, end: {line: 1, ch: 14}}, {text: "modified line 2\n", start: {line: 2, ch: 0}, end: {line: 2, ch: 14}}], selection: [{start: {line: 1, ch: 4}, end: {line: 1, ch: 4}, isBeforeEdit: true}, {start: {line: 2, ch: 4}, end: {line: 2, ch: 4}, isBeforeEdit: true, primary: true}]}, {edit: {text: "modified line 4\n", start: {line: 4, ch: 0}, end: {line: 4, ch: 14}}, selection: {start: {line: 4, ch: 4}, end: {line: 4, ch: 4}, isBeforeEdit: true, reversed: true}} ]); initialContentLines[1] = "modified line 1"; // no extra newline inserted here initialContentLines[2] = "modified line 2"; initialContentLines[4] = "modified line 4"; initialContentLines.splice(5, 0, ""); initialContentLines.splice(3, 0, ""); expect(myDocument.getText()).toEqual(initialContentLines.join("\n")); expect(result.length).toBe(3); expect(result[0].start.line).toEqual(1); expect(result[0].start.ch).toEqual(15); // pushed to end of first modified text expect(result[0].end.line).toEqual(1); expect(result[0].end.ch).toEqual(15); expect(result[0].primary).toBeFalsy(); expect(result[1].start.line).toEqual(3); expect(result[1].start.ch).toEqual(0); // pushed to end of second modified text expect(result[1].end.line).toEqual(3); expect(result[1].end.ch).toEqual(0); expect(result[1].primary).toBe(true); expect(result[2].start.line).toEqual(6); expect(result[2].start.ch).toEqual(0); // pushed to end of third modified text and updated for both edits expect(result[2].end.line).toEqual(6); expect(result[2].end.ch).toEqual(0); expect(result[2].reversed).toBe(true); }); it("should perform multiple changes/track multiple selections within a single edit, selections not specified as isBeforeEdit", function () { var result = myDocument.doMultipleEdits([ {edit: [{text: "modified line 1", start: {line: 1, ch: 0}, end: {line: 1, ch: 14}}, {text: "modified line 2\n", start: {line: 2, ch: 0}, end: {line: 2, ch: 14}}], selection: [{start: {line: 1, ch: 4}, end: {line: 1, ch: 4}}, {start: {line: 2, ch: 4}, end: {line: 2, ch: 4}, primary: true}]}, {edit: {text: "modified line 4\n", start: {line: 4, ch: 0}, end: {line: 4, ch: 14}}, selection: {start: {line: 4, ch: 4}, end: {line: 4, ch: 4}, reversed: true}} ]); initialContentLines[1] = "modified line 1"; // no extra newline inserted here initialContentLines[2] = "modified line 2"; initialContentLines[4] = "modified line 4"; initialContentLines.splice(5, 0, ""); initialContentLines.splice(3, 0, ""); expect(myDocument.getText()).toEqual(initialContentLines.join("\n")); expect(result.length).toBe(3); expect(result[0].start.line).toEqual(1); expect(result[0].start.ch).toEqual(4); // not fixed up expect(result[0].end.line).toEqual(1); expect(result[0].end.ch).toEqual(4); expect(result[0].primary).toBeFalsy(); expect(result[1].start.line).toEqual(2); expect(result[1].start.ch).toEqual(4); // not fixed up, no need to adjust for first edit expect(result[1].end.line).toEqual(2); expect(result[1].end.ch).toEqual(4); expect(result[1].primary).toBe(true); expect(result[2].start.line).toEqual(5); expect(result[2].start.ch).toEqual(4); // not pushed to end of modified text, but updated for previous edit expect(result[2].end.line).toEqual(5); expect(result[2].end.ch).toEqual(4); expect(result[2].reversed).toBe(true); }); it("should throw an error if edits overlap", function () { function shouldDie() { myDocument.doMultipleEdits([ {edit: {text: "modified line 3", start: {line: 3, ch: 0}, end: {line: 3, ch: 5}}}, {edit: {text: "modified line 3 again", start: {line: 3, ch: 3}, end: {line: 3, ch: 8}}} ]); } expect(shouldDie).toThrow(); }); it("should throw an error if multiple edits in one group surround an edit in another group, even if they don't directly overlap", function () { function shouldDie() { myDocument.doMultipleEdits([ {edit: [{text: "modified line 2", start: {line: 2, ch: 0}, end: {line: 2, ch: 0}}, {text: "modified line 4", start: {line: 4, ch: 0}, end: {line: 4, ch: 0}}]}, {edit: {text: "modified line 3", start: {line: 3, ch: 0}, end: {line: 3, ch: 0}}} ]); } expect(shouldDie).toThrow(); }); }); }); describe("Document Integration", function () { this.category = "integration"; var testPath = SpecRunnerUtils.getTestPath("/spec/Document-test-files"), testWindow, $; beforeFirst(function () { SpecRunnerUtils.createTestWindowAndRun(this, function (w) { testWindow = w; $ = testWindow.$; // Load module instances from brackets.test CommandManager = testWindow.brackets.test.CommandManager; Commands = testWindow.brackets.test.Commands; EditorManager = testWindow.brackets.test.EditorManager; DocumentModule = testWindow.brackets.test.DocumentModule; DocumentManager = testWindow.brackets.test.DocumentManager; MainViewManager = testWindow.brackets.test.MainViewManager; SpecRunnerUtils.loadProjectInTestWindow(testPath); }); }); afterLast(function () { testWindow = null; CommandManager = null; Commands = null; EditorManager = null; DocumentModule = null; DocumentManager = null; MainViewManager = null; SpecRunnerUtils.closeTestWindow(); testWindow = null; }); afterEach(function () { testWindow.closeAllFiles(); runs(function () { expect(DocumentManager.getAllOpenDocuments().length).toBe(0); DocumentModule.off(".docTest"); }); }); var JS_FILE = testPath + "/test.js", CSS_FILE = testPath + "/test.css", HTML_FILE = testPath + "/test.html"; describe("Dirty flag and undo", function () { var promise; it("should not fire dirtyFlagChange when created", function () { var dirtyFlagListener = jasmine.createSpy(); runs(function () { DocumentManager.on("dirtyFlagChange", dirtyFlagListener); promise = DocumentManager.getDocumentForPath(JS_FILE); waitsForDone(promise, "Create Document"); }); runs(function () { expect(dirtyFlagListener.callCount).toBe(0); DocumentManager.off("dirtyFlagChange", dirtyFlagListener); }); }); it("should clear dirty flag, preserve undo when marked saved", function () { var dirtyFlagListener = jasmine.createSpy(); runs(function () { DocumentManager.on("dirtyFlagChange", dirtyFlagListener); promise = CommandManager.execute(Commands.FILE_OPEN, {fullPath: JS_FILE}); waitsForDone(promise, "Open file"); }); runs(function () { var doc = DocumentManager.getOpenDocumentForPath(JS_FILE); expect(doc.isDirty).toBe(false); expect(doc._masterEditor._codeMirror.historySize().undo).toBe(0); // Make an edit (make dirty) doc.replaceRange("Foo", {line: 0, ch: 0}); expect(doc.isDirty).toBe(true); expect(doc._masterEditor._codeMirror.historySize().undo).toBe(1); expect(dirtyFlagListener.callCount).toBe(1); // Mark saved (e.g. called by Save command) doc.notifySaved(); expect(doc.isDirty).toBe(false); expect(doc._masterEditor._codeMirror.historySize().undo).toBe(1); // still has undo history expect(dirtyFlagListener.callCount).toBe(2); DocumentManager.off("dirtyFlagChange", dirtyFlagListener); }); }); it("should clear dirty flag AND undo when text reset", function () { var dirtyFlagListener = jasmine.createSpy(), changeListener = jasmine.createSpy(); runs(function () { DocumentManager.on("dirtyFlagChange", dirtyFlagListener); promise = CommandManager.execute(Commands.FILE_OPEN, {fullPath: JS_FILE}); waitsForDone(promise, "Open file"); }); runs(function () { var doc = DocumentManager.getOpenDocumentForPath(JS_FILE); doc.on("change", changeListener); expect(doc.isDirty).toBe(false); expect(doc._masterEditor._codeMirror.historySize().undo).toBe(0); // Make an edit (make dirty) doc.replaceRange("Foo", {line: 0, ch: 0}); expect(doc.isDirty).toBe(true); expect(doc._masterEditor._codeMirror.historySize().undo).toBe(1); expect(dirtyFlagListener.callCount).toBe(1); expect(changeListener.callCount).toBe(1); // Reset text (e.g. called by Revert command, or syncing external changes) doc.refreshText("New content", Date.now()); expect(doc.isDirty).toBe(false); expect(doc._masterEditor._codeMirror.historySize().undo).toBe(0); // undo history GONE expect(dirtyFlagListener.callCount).toBe(2); expect(changeListener.callCount).toBe(2); doc.off("change", changeListener); DocumentManager.off("dirtyFlagChange", dirtyFlagListener); }); }); it("should fire change but not dirtyFlagChange when clean text reset, with editor", function () { // bug #502 var dirtyFlagListener = jasmine.createSpy(), changeListener = jasmine.createSpy(); runs(function () { DocumentManager.on("dirtyFlagChange", dirtyFlagListener); promise = CommandManager.execute(Commands.FILE_OPEN, {fullPath: JS_FILE}); waitsForDone(promise, "Open file"); }); runs(function () { var doc = DocumentManager.getOpenDocumentForPath(JS_FILE); doc.on("change", changeListener); expect(doc.isDirty).toBe(false); expect(doc._masterEditor._codeMirror.historySize().undo).toBe(0); doc.refreshText("New content", Date.now()); // e.g. syncing external changes expect(doc.isDirty).toBe(false); expect(doc._masterEditor._codeMirror.historySize().undo).toBe(0); // still no undo history expect(dirtyFlagListener.callCount).toBe(0); // isDirty hasn't changed expect(changeListener.callCount).toBe(1); // but still counts as a content change doc.off("change", changeListener); DocumentManager.off("dirtyFlagChange", dirtyFlagListener); }); }); it("should fire change but not dirtyFlagChange when clean text reset, without editor", function () { var dirtyFlagListener = jasmine.createSpy(), changeListener = jasmine.createSpy(), doc; runs(function () { DocumentManager.on("dirtyFlagChange", dirtyFlagListener); promise = DocumentManager.getDocumentForPath(JS_FILE) .done(function (result) { doc = result; }); waitsForDone(promise, "Create Document"); }); runs(function () { doc.on("change", changeListener); expect(doc._masterEditor).toBeFalsy(); expect(doc.isDirty).toBe(false); doc.refreshText("New content", Date.now()); // e.g. syncing external changes expect(doc.isDirty).toBe(false); expect(dirtyFlagListener.callCount).toBe(0); expect(changeListener.callCount).toBe(1); // resetting text is still a content change doc.off("change", changeListener); DocumentManager.off("dirtyFlagChange", dirtyFlagListener); doc = null; }); }); it("should not clean history when reset is called with the same text as in the editor", function () { runs(function () { promise = CommandManager.execute(Commands.FILE_OPEN, {fullPath: JS_FILE}); waitsForDone(promise, "Open file"); }); runs(function () { var doc = DocumentManager.getOpenDocumentForPath(JS_FILE); // Put some text into editor doc.setText("Foo"); expect(doc._masterEditor._codeMirror.historySize().undo).toBe(1); // Reset text with the same value, expect history not to change doc.refreshText("Foo", Date.now()); expect(doc._masterEditor._codeMirror.historySize().undo).toBe(1); }); }); it("should not clean history when reset is called with the same text with different line-endings", function () { runs(function () { promise = CommandManager.execute(Commands.FILE_OPEN, {fullPath: JS_FILE}); waitsForDone(promise, "Open file"); }); runs(function () { var doc = DocumentManager.getOpenDocumentForPath(JS_FILE); var crlf = "a\r\nb\r\nc"; var lf = "a\nb\nc"; // Put some text into editor doc.setText(crlf); expect(doc._masterEditor._codeMirror.historySize().undo).toBe(1); // Reset text with the same value, expect history not to change doc.refreshText(lf, Date.now()); expect(doc._masterEditor._codeMirror.historySize().undo).toBe(1); // Reset text with the same value, expect history not to change doc.refreshText(crlf, Date.now()); expect(doc._masterEditor._codeMirror.historySize().undo).toBe(1); }); }); }); describe("Refresh and change events", function () { var promise, changeListener, docChangeListener, doc; beforeEach(function () { changeListener = jasmine.createSpy(); docChangeListener = jasmine.createSpy(); }); afterEach(function () { promise = null; changeListener = null; docChangeListener = null; doc = null; }); it("should fire both change and documentChange when text is refreshed if doc does not have masterEditor", function () { runs(function () { promise = DocumentManager.getDocumentForPath(JS_FILE) .done(function (result) { doc = result; }); waitsForDone(promise, "Create Document"); }); runs(function () { DocumentModule.on("documentChange.docTest", docChangeListener); doc.on("change", changeListener); expect(doc._masterEditor).toBeFalsy(); doc.refreshText("New content", Date.now()); expect(doc._masterEditor).toBeFalsy(); expect(docChangeListener.callCount).toBe(1); expect(changeListener.callCount).toBe(1); }); }); it("should fire both change and documentChange when text is refreshed if doc has masterEditor", function () { runs(function () { promise = DocumentManager.getDocumentForPath(JS_FILE) .done(function (result) { doc = result; }); waitsForDone(promise, "Create Document"); }); runs(function () { expect(doc._masterEditor).toBeFalsy(); doc.setText("first edit"); expect(doc._masterEditor).toBeTruthy(); DocumentModule.on("documentChange.docTest", docChangeListener); doc.on("change", changeListener); doc.refreshText("New content", Date.now()); expect(docChangeListener.callCount).toBe(1); expect(changeListener.callCount).toBe(1); }); }); it("should *not* fire documentChange when a document is first created", function () { runs(function () { DocumentModule.on("documentChange.docTest", docChangeListener); waitsForDone(DocumentManager.getDocumentForPath(JS_FILE)); }); runs(function () { expect(docChangeListener.callCount).toBe(0); }); }); }); describe("Ref counting", function () { // TODO: additional, simpler ref counting test cases such as Live Development, open/close inline editor (refs from // both editor & rule list TextRanges), navigate files w/o adding to working set, etc. it("should clean up (later) a master Editor auto-created by calling read-only Document API, if Editor not used by UI", function () { var promise, cssDoc, cssMasterEditor; runs(function () { promise = CommandManager.execute(Commands.CMD_ADD_TO_WORKINGSET_AND_OPEN, {fullPath: HTML_FILE}); waitsForDone(promise, "Open into working set"); }); runs(function () { // Open inline editor onto test.css's ".testClass" rule promise = SpecRunnerUtils.toggleQuickEditAtOffset(EditorManager.getCurrentFullEditor(), {line: 8, ch: 4}); waitsForDone(promise, "Open inline editor"); }); runs(function () { expect(MainViewManager.findInWorkingSet(MainViewManager.ACTIVE_PANE, CSS_FILE)).toBe(-1); expect(DocumentManager.getOpenDocumentForPath(CSS_FILE)).toBeTruthy(); // Force creation of master editor for CSS file cssDoc = DocumentManager.getOpenDocumentForPath(CSS_FILE); expect(cssDoc._masterEditor).toBeFalsy(); DocumentManager.getOpenDocumentForPath(CSS_FILE).getLine(0); expect(cssDoc._masterEditor).toBeTruthy(); // Close inline editor var hostEditor = EditorManager.getCurrentFullEditor(); var inlineWidget = hostEditor.getInlineWidgets()[0]; waitsForDone(EditorManager.closeInlineWidget(hostEditor, inlineWidget), "close inline editor"); }); runs(function () { // Now there are no parts of Brackets that need to keep the CSS Document alive (its only ref is its own master // Editor and that Editor isn't accessible in the UI anywhere). It's ready to get "GCed" by DocumentManager as // soon as it hits a trigger point for doing so. expect(DocumentManager.getOpenDocumentForPath(CSS_FILE)).toBeTruthy(); expect(cssDoc._refCount).toBe(1); expect(cssDoc._masterEditor).toBeTruthy(); expect(testWindow.$(".CodeMirror").length).toBe(2); // HTML editor (current) & CSS editor (dangling) // Switch to a third file - trigger point for cleanup promise = CommandManager.execute(Commands.FILE_OPEN, {fullPath: JS_FILE}); waitsForDone(promise, "Switch to other file"); }); runs(function () { // Creation of that third file's Document should have triggered cleanup of CSS Document and its master Editor expect(DocumentManager.getOpenDocumentForPath(CSS_FILE)).toBeFalsy(); expect(cssDoc._refCount).toBe(0); expect(cssDoc._masterEditor).toBeFalsy(); expect(testWindow.$(".CodeMirror").length).toBe(2); // HTML editor (working set) & JS editor (current) }); cssDoc = cssMasterEditor = null; }); }); }); });