From e34dfc636c6728fafbeeb4ae030e6882b30c2f8e Mon Sep 17 00:00:00 2001 From: Jess Lin Date: Wed, 13 May 2015 16:31:20 -0700 Subject: [PATCH] [Gutter] Fix custom decorations tests in TextEditorPresenter specs --- spec/text-editor-presenter-spec.coffee | 468 ++++++++++++------------- 1 file changed, 233 insertions(+), 235 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 601539894..8a991e34c 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -2156,7 +2156,7 @@ describe "TextEditorPresenter", -> gutter = description.gutter return description if gutter.name is gutterName - describe "the array itself", -> + describe "the array itself, an array of gutter descriptions", -> it "updates when gutters are added to the editor model, and keeps the gutters sorted by priority", -> presenter = buildPresenter() gutter1 = editor.addGutter({name: 'test-gutter-1', priority: -100, visible: true}) @@ -2180,8 +2180,238 @@ describe "TextEditorPresenter", -> gutter.destroy() expect(getStateForGutterWithName(presenter, 'test-gutter')).toBeUndefined() - describe "when gutter description corresponds to a custom gutter", -> - # TODO + describe "for a gutter description that corresponds to a custom gutter", -> + describe ".content", -> + getContentForGutterWithName = (presenter, gutterName) -> + fullState = getStateForGutterWithName(presenter, gutterName) + return fullState.content if fullState + + [presenter, gutter, decorationItem, decorationParams] = [] + [marker1, decoration1, marker2, decoration2, marker3, decoration3] = [] + + # Set the scrollTop to 0 to show the very top of the file. + # Set the explicitHeight to make 10 lines visible. + scrollTop = 0 + lineHeight = 10 + explicitHeight = lineHeight * 10 + lineOverdrawMargin = 1 + + beforeEach -> + # At the beginning of each test, decoration1 and decoration2 are in visible range, + # but not decoration3. + presenter = buildPresenter({explicitHeight, scrollTop, lineHeight, lineOverdrawMargin}) + gutter = editor.addGutter({name: 'test-gutter', visible: true}) + decorationItem = document.createElement('div') + decorationItem.class = 'decoration-item' + decorationParams = + type: 'gutter' + gutterName: 'test-gutter' + class: 'test-class' + item: decorationItem + marker1 = editor.markBufferRange([[0,0],[1,0]]) + decoration1 = editor.decorateMarker(marker1, decorationParams) + marker2 = editor.markBufferRange([[9,0],[12,0]]) + decoration2 = editor.decorateMarker(marker2, decorationParams) + marker3 = editor.markBufferRange([[13,0],[14,0]]) + decoration3 = editor.decorateMarker(marker3, decorationParams) + + # Clear any batched state updates. + presenter.getState() + + it "contains all decorations within the visible buffer range", -> + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBe lineHeight * marker1.getScreenRange().start.row + expect(decorationState[decoration1.id].height).toBe lineHeight * marker1.getScreenRange().getRowCount() + expect(decorationState[decoration1.id].item).toBe decorationItem + expect(decorationState[decoration1.id].class).toBe 'test-class' + + expect(decorationState[decoration2.id].top).toBe lineHeight * marker2.getScreenRange().start.row + expect(decorationState[decoration2.id].height).toBe lineHeight * marker2.getScreenRange().getRowCount() + expect(decorationState[decoration2.id].item).toBe decorationItem + expect(decorationState[decoration2.id].class).toBe 'test-class' + + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates when ::scrollTop changes", -> + # This update will scroll decoration1 out of view, and decoration3 into view. + expectStateUpdate presenter, -> presenter.setScrollTop(scrollTop + lineHeight * 5) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id]).toBeUndefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id].top).toBeDefined() + + it "updates when ::explicitHeight changes", -> + # This update will make all three decorations visible. + expectStateUpdate presenter, -> presenter.setExplicitHeight(explicitHeight + lineHeight * 5) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBeDefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id].top).toBeDefined() + + it "updates when ::lineHeight changes", -> + # This update will make all three decorations visible. + expectStateUpdate presenter, -> presenter.setLineHeight(Math.ceil(1.0 * explicitHeight / marker3.getBufferRange().end.row)) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBeDefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id].top).toBeDefined() + + it "updates when the editor's content changes", -> + # This update will add enough lines to push decoration2 out of view. + expectStateUpdate presenter, -> editor.setTextInBufferRange([[8,0],[9,0]],'\n\n\n\n\n') + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBeDefined() + expect(decorationState[decoration2.id]).toBeUndefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates when a decoration's marker is modified", -> + # This update will move decoration1 out of view. + expectStateUpdate presenter, -> + newRange = new Range([13,0],[14,0]) + marker1.setBufferRange(newRange) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id]).toBeUndefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + describe "when a decoration's properties are modified", -> + it "updates the item applied to the decoration, if the decoration item is changed", -> + # This changes the decoration class. The visibility of the decoration should not be affected. + newItem = document.createElement('div') + newItem.class = 'new-decoration-item' + newDecorationParams = + type: 'gutter' + gutterName: 'test-gutter' + class: 'test-class' + item: newItem + expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].item).toBe newItem + expect(decorationState[decoration2.id].item).toBe decorationItem + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates the class applied to the decoration, if the decoration class is changed", -> + # This changes the decoration item. The visibility of the decoration should not be affected. + newDecorationParams = + type: 'gutter' + gutterName: 'test-gutter' + class: 'new-test-class' + item: decorationItem + expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].class).toBe 'new-test-class' + expect(decorationState[decoration2.id].class).toBe 'test-class' + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates the type of the decoration, if the decoration type is changed", -> + # This changes the type of the decoration. This should remove the decoration from the gutter. + newDecorationParams = + type: 'line' + gutterName: 'test-gutter' # This is an invalid/meaningless option here, but it shouldn't matter. + class: 'test-class' + item: decorationItem + expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id]).toBeUndefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates the gutter the decoration targets, if the decoration gutterName is changed", -> + # This changes which gutter this decoration applies to. Since this gutter does not exist, + # the decoration should not appear in the customDecorations state. + newDecorationParams = + type: 'gutter' + gutterName: 'test-gutter-2' + class: 'new-test-class' + item: decorationItem + expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id]).toBeUndefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + # After adding the targeted gutter, the decoration will appear in the state for that gutter, + # since it should be visible. + expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'}) + newGutterDecorationState = getContentForGutterWithName(presenter, 'test-gutter-2') + expect(newGutterDecorationState[decoration1.id].top).toBeDefined() + expect(newGutterDecorationState[decoration2.id]).toBeUndefined() + expect(newGutterDecorationState[decoration3.id]).toBeUndefined() + oldGutterDecorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(oldGutterDecorationState[decoration1.id]).toBeUndefined() + expect(oldGutterDecorationState[decoration2.id].top).toBeDefined() + expect(oldGutterDecorationState[decoration3.id]).toBeUndefined() + + it "updates when the editor's mini state changes, and is cleared when the editor is mini", -> + expectStateUpdate presenter, -> editor.setMini(true) + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState).toBeUndefined() + + # The decorations should return to the original state. + expectStateUpdate presenter, -> editor.setMini(false) + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBeDefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates when a gutter's visibility changes, and is cleared when the gutter is not visible", -> + expectStateUpdate presenter, -> gutter.hide() + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id]).toBeUndefined() + expect(decorationState[decoration2.id]).toBeUndefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + # The decorations should return to the original state. + expectStateUpdate presenter, -> gutter.show() + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBeDefined() + expect(decorationState[decoration2.id].top).toBeDefined() + expect(decorationState[decoration3.id]).toBeUndefined() + + it "updates when a gutter is added to the editor", -> + decorationParams = + type: 'gutter' + gutterName: 'test-gutter-2' + class: 'test-class' + marker4 = editor.markBufferRange([[0,0],[1,0]]) + decoration4 = editor.decorateMarker(marker4, decorationParams) + expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'}) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter-2') + expect(decorationState[decoration1.id]).toBeUndefined() + expect(decorationState[decoration2.id]).toBeUndefined() + expect(decorationState[decoration3.id]).toBeUndefined() + expect(decorationState[decoration4.id].top).toBeDefined() + + it "updates when editor lines are folded", -> + oldDimensionsForDecoration1 = + top: lineHeight * marker1.getScreenRange().start.row + height: lineHeight * marker1.getScreenRange().getRowCount() + oldDimensionsForDecoration2 = + top: lineHeight * marker2.getScreenRange().start.row + height: lineHeight * marker2.getScreenRange().getRowCount() + + # Based on the contents of sample.js, this should affect all but the top + # part of decoration1. + expectStateUpdate presenter, -> editor.foldBufferRow(0) + + decorationState = getContentForGutterWithName(presenter, 'test-gutter') + expect(decorationState[decoration1.id].top).toBe oldDimensionsForDecoration1.top + expect(decorationState[decoration1.id].height).not.toBe oldDimensionsForDecoration1.height + # Due to the issue described here: https://github.com/atom/atom/issues/6454, decoration2 + # will be bumped up to the row that was folded and still made visible, instead of being + # entirely collapsed. (The same thing will happen to decoration3.) + expect(decorationState[decoration2.id].top).not.toBe oldDimensionsForDecoration2.top + expect(decorationState[decoration2.id].height).not.toBe oldDimensionsForDecoration2.height describe "regardless of what kind of gutter a gutter description corresponds to", -> [customGutter] = [] @@ -2310,238 +2540,6 @@ describe "TextEditorPresenter", -> expect(getStylesForGutterWithName(presenter, 'line-number').backgroundColor).toBe "rgba(0, 0, 255, 0)" expect(getStylesForGutterWithName(presenter, 'test-gutter').backgroundColor).toBe "rgba(0, 0, 255, 0)" - # TODO - describe ".customDecorations", -> - [presenter, gutter, decorationItem, decorationParams] = [] - [marker1, decoration1, marker2, decoration2, marker3, decoration3] = [] - - # Set the scrollTop to 0 to show the very top of the file. - # Set the explicitHeight to make 10 lines visible. - scrollTop = 0 - lineHeight = 10 - explicitHeight = lineHeight * 10 - lineOverdrawMargin = 1 - - decorationStateForGutterName = (presenter, gutterName) -> - presenter.getState().gutters.customDecorations[gutterName] - - beforeEach -> - # At the beginning of each test, decoration1 and decoration2 are in visible range, - # but not decoration3. - presenter = buildPresenter({explicitHeight, scrollTop, lineHeight, lineOverdrawMargin}) - gutter = editor.addGutter({name: 'test-gutter', visible: true}) - decorationItem = document.createElement('div') - decorationItem.class = 'decoration-item' - decorationParams = - type: 'gutter' - gutterName: 'test-gutter' - class: 'test-class' - item: decorationItem - marker1 = editor.markBufferRange([[0,0],[1,0]]) - decoration1 = editor.decorateMarker(marker1, decorationParams) - marker2 = editor.markBufferRange([[9,0],[12,0]]) - decoration2 = editor.decorateMarker(marker2, decorationParams) - marker3 = editor.markBufferRange([[13,0],[14,0]]) - decoration3 = editor.decorateMarker(marker3, decorationParams) - - # Clear any batched state updates. - presenter.getState() - - it "contains all decorations within the visible buffer range", -> - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBe lineHeight * marker1.getScreenRange().start.row - expect(decorationState[decoration1.id].height).toBe lineHeight * marker1.getScreenRange().getRowCount() - expect(decorationState[decoration1.id].item).toBe decorationItem - expect(decorationState[decoration1.id].class).toBe 'test-class' - - expect(decorationState[decoration2.id].top).toBe lineHeight * marker2.getScreenRange().start.row - expect(decorationState[decoration2.id].height).toBe lineHeight * marker2.getScreenRange().getRowCount() - expect(decorationState[decoration2.id].item).toBe decorationItem - expect(decorationState[decoration2.id].class).toBe 'test-class' - - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates when ::scrollTop changes", -> - # This update will scroll decoration1 out of view, and decoration3 into view. - expectStateUpdate presenter, -> presenter.setScrollTop(scrollTop + lineHeight * 5) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id]).toBeUndefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id].top).toBeDefined() - - it "updates when ::explicitHeight changes", -> - # This update will make all three decorations visible. - expectStateUpdate presenter, -> presenter.setExplicitHeight(explicitHeight + lineHeight * 5) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBeDefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id].top).toBeDefined() - - it "updates when ::lineHeight changes", -> - # This update will make all three decorations visible. - expectStateUpdate presenter, -> presenter.setLineHeight(Math.ceil(1.0 * explicitHeight / marker3.getBufferRange().end.row)) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBeDefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id].top).toBeDefined() - - it "updates when the editor's content changes", -> - # This update will add enough lines to push decoration2 out of view. - expectStateUpdate presenter, -> editor.setTextInBufferRange([[8,0],[9,0]],'\n\n\n\n\n') - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBeDefined() - expect(decorationState[decoration2.id]).toBeUndefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates when a decoration's marker is modified", -> - # This update will move decoration1 out of view. - expectStateUpdate presenter, -> - newRange = new Range([13,0],[14,0]) - marker1.setBufferRange(newRange) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id]).toBeUndefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - describe "when a decoration's properties are modified", -> - it "updates the item applied to the decoration, if the decoration item is changed", -> - # This changes the decoration class. The visibility of the decoration should not be affected. - newItem = document.createElement('div') - newItem.class = 'new-decoration-item' - newDecorationParams = - type: 'gutter' - gutterName: 'test-gutter' - class: 'test-class' - item: newItem - expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].item).toBe newItem - expect(decorationState[decoration2.id].item).toBe decorationItem - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates the class applied to the decoration, if the decoration class is changed", -> - # This changes the decoration item. The visibility of the decoration should not be affected. - newDecorationParams = - type: 'gutter' - gutterName: 'test-gutter' - class: 'new-test-class' - item: decorationItem - expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].class).toBe 'new-test-class' - expect(decorationState[decoration2.id].class).toBe 'test-class' - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates the type of the decoration, if the decoration type is changed", -> - # This changes the type of the decoration. This should remove the decoration from the gutter. - newDecorationParams = - type: 'line' - gutterName: 'test-gutter' # This is an invalid/meaningless option here, but it shouldn't matter. - class: 'test-class' - item: decorationItem - expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id]).toBeUndefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates the gutter the decoration targets, if the decoration gutterName is changed", -> - # This changes which gutter this decoration applies to. Since this gutter does not exist, - # the decoration should not appear in the customDecorations state. - newDecorationParams = - type: 'gutter' - gutterName: 'test-gutter-2' - class: 'new-test-class' - item: decorationItem - expectStateUpdate presenter, -> decoration1.setProperties(newDecorationParams) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id]).toBeUndefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - # After adding the targeted gutter, the decoration will appear in the state for that gutter, - # since it should be visible. - expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'}) - newGutterDecorationState = decorationStateForGutterName(presenter, 'test-gutter-2') - expect(newGutterDecorationState[decoration1.id].top).toBeDefined() - expect(newGutterDecorationState[decoration2.id]).toBeUndefined() - expect(newGutterDecorationState[decoration3.id]).toBeUndefined() - oldGutterDecorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(oldGutterDecorationState[decoration1.id]).toBeUndefined() - expect(oldGutterDecorationState[decoration2.id].top).toBeDefined() - expect(oldGutterDecorationState[decoration3.id]).toBeUndefined() - - it "updates when the editor's mini state changes, and is cleared when the editor is mini", -> - expectStateUpdate presenter, -> editor.setMini(true) - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState).toBeUndefined() - - # The decorations should return to the original state. - expectStateUpdate presenter, -> editor.setMini(false) - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBeDefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates when a gutter's visibility changes, and is cleared when the gutter is not visible", -> - expectStateUpdate presenter, -> gutter.hide() - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id]).toBeUndefined() - expect(decorationState[decoration2.id]).toBeUndefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - # The decorations should return to the original state. - expectStateUpdate presenter, -> gutter.show() - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBeDefined() - expect(decorationState[decoration2.id].top).toBeDefined() - expect(decorationState[decoration3.id]).toBeUndefined() - - it "updates when a gutter is added to the editor", -> - decorationParams = - type: 'gutter' - gutterName: 'test-gutter-2' - class: 'test-class' - marker4 = editor.markBufferRange([[0,0],[1,0]]) - decoration4 = editor.decorateMarker(marker4, decorationParams) - expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'}) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter-2') - expect(decorationState[decoration1.id]).toBeUndefined() - expect(decorationState[decoration2.id]).toBeUndefined() - expect(decorationState[decoration3.id]).toBeUndefined() - expect(decorationState[decoration4.id].top).toBeDefined() - - it "updates when editor lines are folded", -> - oldDimensionsForDecoration1 = - top: lineHeight * marker1.getScreenRange().start.row - height: lineHeight * marker1.getScreenRange().getRowCount() - oldDimensionsForDecoration2 = - top: lineHeight * marker2.getScreenRange().start.row - height: lineHeight * marker2.getScreenRange().getRowCount() - - # Based on the contents of sample.js, this should affect all but the top - # part of decoration1. - expectStateUpdate presenter, -> editor.foldBufferRow(0) - - decorationState = decorationStateForGutterName(presenter, 'test-gutter') - expect(decorationState[decoration1.id].top).toBe oldDimensionsForDecoration1.top - expect(decorationState[decoration1.id].height).not.toBe oldDimensionsForDecoration1.height - # Due to the issue described here: https://github.com/atom/atom/issues/6454, decoration2 - # will be bumped up to the row that was folded and still made visible, instead of being - # entirely collapsed. (The same thing will happen to decoration3.) - expect(decorationState[decoration2.id].top).not.toBe oldDimensionsForDecoration2.top - expect(decorationState[decoration2.id].height).not.toBe oldDimensionsForDecoration2.height - # disabled until we fix an issue with display buffer markers not updating when # they are moved on screen but not in the buffer xdescribe "when the model and view measurements are mutated randomly", ->