Files
atom/spec/text-editor-element-spec.coffee
Nathan Sobo 106621e161 Deprecate automatic assignment of autoHeight for text editors
Previously, we attempted to automatically determine whether the editor’s
height should be based on the editor’s content or the height of its
container. Unfortunately, DOM APIs are insufficient to make this
determination in a complete way, leading to unpredictable behavior.

This PR deprecates the automatic determination of this behavior. By
default, editors base their height on their content. If an editor has
an explicit height assigned via its style or is positioned absolute with
an explicit top and bottom, we disable the content-based autoHeight and
log a deprecation warning telling the user to assign autoHeight
explicitly.

This paves the way to add an autoWidth setting, which will default to
false.
2016-08-17 15:52:51 -06:00

294 lines
10 KiB
CoffeeScript

TextEditorElement = require '../src/text-editor-element'
TextEditor = require '../src/text-editor'
{Disposable} = require 'event-kit'
# The rest of text-editor-component-spec will be moved to this file when React
# is eliminated. This covers only concerns related to the wrapper element for now
describe "TextEditorElement", ->
jasmineContent = null
beforeEach ->
jasmineContent = document.body.querySelector('#jasmine-content')
describe "instantiation", ->
it "honors the 'mini' attribute", ->
jasmineContent.innerHTML = "<atom-text-editor mini>"
element = jasmineContent.firstChild
expect(element.getModel().isMini()).toBe true
it "honors the 'placeholder-text' attribute", ->
jasmineContent.innerHTML = "<atom-text-editor placeholder-text='testing'>"
element = jasmineContent.firstChild
expect(element.getModel().getPlaceholderText()).toBe 'testing'
it "honors the 'gutter-hidden' attribute", ->
jasmineContent.innerHTML = "<atom-text-editor gutter-hidden>"
element = jasmineContent.firstChild
expect(element.getModel().isLineNumberGutterVisible()).toBe false
it "honors the text content", ->
jasmineContent.innerHTML = "<atom-text-editor>testing</atom-text-editor>"
element = jasmineContent.firstChild
expect(element.getModel().getText()).toBe 'testing'
describe "when the model is assigned", ->
it "adds the 'mini' attribute if .isMini() returns true on the model", ->
element = new TextEditorElement
model = atom.workspace.buildTextEditor(mini: true)
element.setModel(model)
expect(element.hasAttribute('mini')).toBe true
describe "when the editor is attached to the DOM", ->
it "mounts the component and unmounts when removed from the dom", ->
element = new TextEditorElement
jasmine.attachToDOM(element)
component = element.component
expect(component.mounted).toBe true
element.remove()
expect(component.mounted).toBe false
jasmine.attachToDOM(element)
expect(element.component.mounted).toBe true
describe "when the editor is detached from the DOM and then reattached", ->
it "does not render duplicate line numbers", ->
editor = atom.workspace.buildTextEditor()
editor.setText('1\n2\n3')
element = atom.views.getView(editor)
jasmine.attachToDOM(element)
initialCount = element.shadowRoot.querySelectorAll('.line-number').length
element.remove()
jasmine.attachToDOM(element)
expect(element.shadowRoot.querySelectorAll('.line-number').length).toBe initialCount
it "does not render duplicate decorations in custom gutters", ->
editor = atom.workspace.buildTextEditor()
editor.setText('1\n2\n3')
editor.addGutter({name: 'test-gutter'})
marker = editor.markBufferRange([[0, 0], [2, 0]])
editor.decorateMarker(marker, {type: 'gutter', gutterName: 'test-gutter'})
element = atom.views.getView(editor)
jasmine.attachToDOM(element)
initialDecorationCount = element.shadowRoot.querySelectorAll('.decoration').length
element.remove()
jasmine.attachToDOM(element)
expect(element.shadowRoot.querySelectorAll('.decoration').length).toBe initialDecorationCount
describe "focus and blur handling", ->
it "proxies focus/blur events to/from the hidden input inside the shadow root", ->
element = new TextEditorElement
jasmineContent.appendChild(element)
blurCalled = false
element.addEventListener 'blur', -> blurCalled = true
element.focus()
expect(blurCalled).toBe false
expect(element.hasFocus()).toBe true
expect(document.activeElement).toBe element
expect(element.shadowRoot.activeElement).toBe element.shadowRoot.querySelector('input')
document.body.focus()
expect(blurCalled).toBe true
describe "when focused while a parent node is being attached to the DOM", ->
class ElementThatFocusesChild extends HTMLDivElement
attachedCallback: ->
@firstChild.focus()
document.registerElement("element-that-focuses-child",
prototype: ElementThatFocusesChild.prototype
)
it "proxies the focus event to the hidden input", ->
element = new TextEditorElement
parentElement = document.createElement("element-that-focuses-child")
parentElement.appendChild(element)
jasmineContent.appendChild(parentElement)
expect(element.shadowRoot.activeElement).toBe element.shadowRoot.querySelector('input')
describe "when the themes finish loading", ->
[themeReloadCallback, initialThemeLoadComplete, element] = []
beforeEach ->
themeReloadCallback = null
initialThemeLoadComplete = false
spyOn(atom.themes, 'isInitialLoadComplete').andCallFake ->
initialThemeLoadComplete
spyOn(atom.themes, 'onDidChangeActiveThemes').andCallFake (fn) ->
themeReloadCallback = fn
new Disposable
element = new TextEditorElement()
element.style.height = '200px'
element.getModel().update({autoHeight: false})
element.getModel().setText [0..20].join("\n")
it "re-renders the scrollbar", ->
jasmineContent.appendChild(element)
atom.styles.addStyleSheet("""
::-webkit-scrollbar {
width: 8px;
}
""", context: 'atom-text-editor')
initialThemeLoadComplete = true
themeReloadCallback()
verticalScrollbarNode = element.shadowRoot.querySelector(".vertical-scrollbar")
scrollbarWidth = verticalScrollbarNode.offsetWidth - verticalScrollbarNode.clientWidth
expect(scrollbarWidth).toEqual(8)
describe "::onDidAttach and ::onDidDetach", ->
it "invokes callbacks when the element is attached and detached", ->
element = new TextEditorElement
attachedCallback = jasmine.createSpy("attachedCallback")
detachedCallback = jasmine.createSpy("detachedCallback")
element.onDidAttach(attachedCallback)
element.onDidDetach(detachedCallback)
jasmine.attachToDOM(element)
expect(attachedCallback).toHaveBeenCalled()
expect(detachedCallback).not.toHaveBeenCalled()
attachedCallback.reset()
element.remove()
expect(attachedCallback).not.toHaveBeenCalled()
expect(detachedCallback).toHaveBeenCalled()
describe "::setUpdatedSynchronously", ->
it "controls whether the text editor is updated synchronously", ->
spyOn(window, 'requestAnimationFrame').andCallFake (fn) -> fn()
element = new TextEditorElement
jasmine.attachToDOM(element)
element.setUpdatedSynchronously(false)
expect(element.isUpdatedSynchronously()).toBe false
element.getModel().setText("hello")
expect(window.requestAnimationFrame).toHaveBeenCalled()
expect(element.shadowRoot.textContent).toContain "hello"
window.requestAnimationFrame.reset()
element.setUpdatedSynchronously(true)
element.getModel().setText("goodbye")
expect(window.requestAnimationFrame).not.toHaveBeenCalled()
expect(element.shadowRoot.textContent).toContain "goodbye"
describe "::getDefaultCharacterWidth", ->
it "returns null before the element is attached", ->
element = new TextEditorElement
expect(element.getDefaultCharacterWidth()).toBeNull()
it "returns the width of a character in the root scope", ->
element = new TextEditorElement
jasmine.attachToDOM(element)
expect(element.getDefaultCharacterWidth()).toBeGreaterThan(0)
describe "::getMaxScrollTop", ->
it "returns the maximum scroll top that can be applied to the element", ->
editor = atom.workspace.buildTextEditor()
editor.setText('1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16')
element = atom.views.getView(editor)
element.style.lineHeight = "10px"
element.style.width = "200px"
jasmine.attachToDOM(element)
expect(element.getMaxScrollTop()).toBe(0)
element.style.height = '100px'
editor.update({autoHeight: false})
element.component.measureDimensions()
expect(element.getMaxScrollTop()).toBe(60)
element.style.height = '120px'
element.component.measureDimensions()
expect(element.getMaxScrollTop()).toBe(40)
element.style.height = '200px'
element.component.measureDimensions()
expect(element.getMaxScrollTop()).toBe(0)
describe "on TextEditor::setMini", ->
it "changes the element's 'mini' attribute", ->
element = new TextEditorElement
jasmine.attachToDOM(element)
expect(element.hasAttribute('mini')).toBe false
element.getModel().setMini(true)
expect(element.hasAttribute('mini')).toBe true
element.getModel().setMini(false)
expect(element.hasAttribute('mini')).toBe false
describe "events", ->
element = null
beforeEach ->
element = new TextEditorElement
element.getModel().setText("lorem\nipsum\ndolor\nsit\namet")
element.setUpdatedSynchronously(true)
element.setHeight(20)
element.setWidth(20)
element.getModel().update({autoHeight: false})
describe "::onDidChangeScrollTop(callback)", ->
it "triggers even when subscribing before attaching the element", ->
positions = []
subscription1 = element.onDidChangeScrollTop (p) -> positions.push(p)
jasmine.attachToDOM(element)
subscription2 = element.onDidChangeScrollTop (p) -> positions.push(p)
positions.length = 0
element.setScrollTop(10)
expect(positions).toEqual([10, 10])
element.remove()
jasmine.attachToDOM(element)
positions.length = 0
element.setScrollTop(20)
expect(positions).toEqual([20, 20])
subscription1.dispose()
positions.length = 0
element.setScrollTop(30)
expect(positions).toEqual([30])
describe "::onDidChangeScrollLeft(callback)", ->
it "triggers even when subscribing before attaching the element", ->
positions = []
subscription1 = element.onDidChangeScrollLeft (p) -> positions.push(p)
jasmine.attachToDOM(element)
subscription2 = element.onDidChangeScrollLeft (p) -> positions.push(p)
positions.length = 0
element.setScrollLeft(10)
expect(positions).toEqual([10, 10])
element.remove()
jasmine.attachToDOM(element)
positions.length = 0
element.setScrollLeft(20)
expect(positions).toEqual([20, 20])
subscription1.dispose()
positions.length = 0
element.setScrollLeft(30)
expect(positions).toEqual([30])