2014-05-21 00:04:59 +04:00
_ = require ' underscore-plus '
{ extend , flatten , toArray , last } = _
2014-04-10 22:46:58 +04:00
ReactEditorView = require ' ../src/react-editor-view '
2014-04-17 21:56:50 +04:00
nbsp = String . fromCharCode ( 160 )
2014-03-28 05:02:24 +04:00
2014-04-30 03:11:20 +04:00
describe " EditorComponent " , ->
2014-05-02 14:12:17 +04:00
[ contentNode , editor , wrapperView , component , node , verticalScrollbarNode , horizontalScrollbarNode ] = [ ]
2014-05-16 20:44:49 +04:00
[ lineHeightInPixels , charWidth , delayAnimationFrames , nextAnimationFrame , lineOverdrawMargin ] = [ ]
2014-03-28 05:02:24 +04:00
beforeEach ->
2014-05-16 20:44:49 +04:00
lineOverdrawMargin = 2
2014-04-02 20:59:57 +04:00
waitsForPromise ->
atom . packages . activatePackage ( ' language-javascript ' )
2014-04-02 03:06:59 +04:00
2014-04-02 20:59:57 +04:00
runs ->
2014-04-12 02:43:04 +04:00
spyOn ( window , " setInterval " ) . andCallFake window . fakeSetInterval
spyOn ( window , " clearInterval " ) . andCallFake window . fakeClearInterval
2014-04-07 01:47:50 +04:00
delayAnimationFrames = false
nextAnimationFrame = null
spyOn ( window , ' requestAnimationFrame ' ) . andCallFake (fn) ->
if delayAnimationFrames
nextAnimationFrame = fn
else
fn ( )
2014-03-28 05:02:24 +04:00
2014-04-25 01:14:10 +04:00
waitsForPromise ->
atom . project . open ( ' sample.js ' ) . then (o) -> editor = o
runs ->
2014-05-02 14:12:17 +04:00
contentNode = document . querySelector ( ' # jasmine-content ' )
contentNode.style.width = ' 1000px '
2014-05-16 20:44:49 +04:00
wrapperView = new ReactEditorView ( editor , { lineOverdrawMargin } )
2014-04-10 22:46:58 +04:00
wrapperView . attachToDom ( )
{ component } = wrapperView
2014-04-02 20:59:57 +04:00
component . setLineHeight ( 1.3 )
component . setFontSize ( 20 )
2014-04-14 23:48:54 +04:00
2014-05-22 04:04:44 +04:00
lineHeightInPixels = editor . getLineHeightInPixels ( )
2014-04-14 23:48:54 +04:00
charWidth = editor . getDefaultCharWidth ( )
2014-04-02 20:59:57 +04:00
node = component . getDOMNode ( )
2014-04-14 23:06:15 +04:00
verticalScrollbarNode = node . querySelector ( ' .vertical-scrollbar ' )
horizontalScrollbarNode = node . querySelector ( ' .horizontal-scrollbar ' )
2014-03-28 05:02:24 +04:00
2014-05-13 01:30:21 +04:00
node.style.height = editor . getLineCount ( ) * lineHeightInPixels + ' px '
node.style.width = ' 1000px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-05-13 01:30:21 +04:00
2014-05-02 14:12:17 +04:00
afterEach ->
contentNode.style.width = ' '
2014-04-08 03:52:48 +04:00
describe " line rendering " , ->
2014-05-16 20:44:49 +04:00
it " renders the currently-visible lines plus the overdraw margin " , ->
2014-04-04 22:58:12 +04:00
node.style.height = 4.5 * lineHeightInPixels + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-04 22:58:12 +04:00
2014-05-16 20:44:49 +04:00
linesNode = node . querySelector ( ' .lines ' )
expect ( linesNode . style [ ' -webkit-transform ' ] ) . toBe " translate3d(0px, 0px, 0px) "
expect ( node . querySelectorAll ( ' .line ' ) . length ) . toBe 6 + 2 # no margin above
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe editor . lineForScreenRow ( 0 ) . text
expect ( component . lineNodeForScreenRow ( 0 ) . offsetTop ) . toBe 0
expect ( component . lineNodeForScreenRow ( 5 ) . textContent ) . toBe editor . lineForScreenRow ( 5 ) . text
expect ( component . lineNodeForScreenRow ( 5 ) . offsetTop ) . toBe 5 * lineHeightInPixels
verticalScrollbarNode.scrollTop = 4.5 * lineHeightInPixels
2014-04-14 23:06:15 +04:00
verticalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-03-28 05:02:24 +04:00
2014-05-16 20:44:49 +04:00
expect ( linesNode . style [ ' -webkit-transform ' ] ) . toBe " translate3d(0px, #{ - 4.5 * lineHeightInPixels } px, 0px) "
expect ( node . querySelectorAll ( ' .line ' ) . length ) . toBe 6 + 4 # margin above and below
expect ( component . lineNodeForScreenRow ( 2 ) . offsetTop ) . toBe 2 * lineHeightInPixels
expect ( component . lineNodeForScreenRow ( 2 ) . textContent ) . toBe editor . lineForScreenRow ( 2 ) . text
expect ( component . lineNodeForScreenRow ( 9 ) . offsetTop ) . toBe 9 * lineHeightInPixels
expect ( component . lineNodeForScreenRow ( 9 ) . textContent ) . toBe editor . lineForScreenRow ( 9 ) . text
2014-03-28 05:02:24 +04:00
2014-05-16 20:44:49 +04:00
it " updates the top position of subsequent lines when lines are inserted or removed " , ->
2014-04-22 21:10:48 +04:00
editor . getBuffer ( ) . deleteRows ( 0 , 1 )
lineNodes = node . querySelectorAll ( ' .line ' )
2014-05-16 20:44:49 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . offsetTop ) . toBe 0
expect ( component . lineNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * lineHeightInPixels
expect ( component . lineNodeForScreenRow ( 2 ) . offsetTop ) . toBe 2 * lineHeightInPixels
2014-04-22 21:10:48 +04:00
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n \n ' )
lineNodes = node . querySelectorAll ( ' .line ' )
2014-05-16 20:44:49 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . offsetTop ) . toBe 0 * lineHeightInPixels
expect ( component . lineNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * lineHeightInPixels
expect ( component . lineNodeForScreenRow ( 2 ) . offsetTop ) . toBe 2 * lineHeightInPixels
expect ( component . lineNodeForScreenRow ( 3 ) . offsetTop ) . toBe 3 * lineHeightInPixels
expect ( component . lineNodeForScreenRow ( 4 ) . offsetTop ) . toBe 4 * lineHeightInPixels
2014-04-22 21:10:48 +04:00
2014-05-22 04:21:27 +04:00
it " updates the top position of lines when the line height changes " , ->
initialLineHeightInPixels = editor . getLineHeightInPixels ( )
component . setLineHeight ( 2 )
newLineHeightInPixels = editor . getLineHeightInPixels ( )
expect ( newLineHeightInPixels ) . not . toBe initialLineHeightInPixels
expect ( component . lineNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * newLineHeightInPixels
2014-05-22 04:21:44 +04:00
it " updates the top position of lines when the font size changes " , ->
initialLineHeightInPixels = editor . getLineHeightInPixels ( )
component . setFontSize ( 10 )
newLineHeightInPixels = editor . getLineHeightInPixels ( )
expect ( newLineHeightInPixels ) . not . toBe initialLineHeightInPixels
expect ( component . lineNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * newLineHeightInPixels
it " updates the top position of lines when the font family changes " , ->
# Can't find a font that changes the line height, but we think one might exist
2014-06-01 10:24:59 +04:00
linesComponent = component . refs . lines
2014-06-02 12:52:10 +04:00
spyOn ( linesComponent , ' measureLineHeightAndDefaultCharWidth ' ) . andCallFake -> editor . setLineHeightInPixels ( 10 )
2014-05-22 04:21:44 +04:00
initialLineHeightInPixels = editor . getLineHeightInPixels ( )
component . setFontFamily ( ' sans-serif ' )
2014-06-02 12:52:10 +04:00
expect ( linesComponent . measureLineHeightAndDefaultCharWidth ) . toHaveBeenCalled ( )
2014-05-22 04:21:44 +04:00
newLineHeightInPixels = editor . getLineHeightInPixels ( )
expect ( newLineHeightInPixels ) . not . toBe initialLineHeightInPixels
expect ( component . lineNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * newLineHeightInPixels
2014-05-22 05:07:03 +04:00
it " renders the .lines div at the full height of the editor if there aren ' t enough lines to scroll vertically " , ->
editor . setText ( ' ' )
node.style.height = ' 300px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-05-22 05:07:03 +04:00
linesNode = node . querySelector ( ' .lines ' )
expect ( linesNode . offsetHeight ) . toBe 300
2014-05-20 03:20:51 +04:00
describe " when showInvisibles is enabled " , ->
2014-05-20 03:56:53 +04:00
invisibles = null
2014-05-20 03:20:51 +04:00
beforeEach ->
2014-05-20 03:56:53 +04:00
invisibles =
eol: ' E '
space: ' S '
tab: ' T '
cr: ' C '
2014-05-20 04:10:04 +04:00
atom . config . set ( " editor.showInvisibles " , true )
atom . config . set ( " editor.invisibles " , invisibles )
2014-05-20 03:20:51 +04:00
2014-05-21 01:17:19 +04:00
it " re-renders the lines when the showInvisibles config option changes " , ->
2014-05-20 03:20:51 +04:00
editor . setText " a line with tabs \t and spaces "
2014-05-20 23:34:43 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ invisibles . space } a line with tabs #{ invisibles . tab } and spaces #{ invisibles . space } #{ invisibles . eol } "
atom . config . set ( " editor.showInvisibles " , false )
2014-05-21 00:58:22 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " a line with tabs and spaces "
2014-05-20 23:34:43 +04:00
atom . config . set ( " editor.showInvisibles " , true )
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ invisibles . space } a line with tabs #{ invisibles . tab } and spaces #{ invisibles . space } #{ invisibles . eol } "
it " displays spaces, tabs, and newlines as visible characters " , ->
2014-05-21 01:07:36 +04:00
editor . setText " a line with tabs \t and spaces "
2014-05-20 03:56:53 +04:00
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ invisibles . space } a line with tabs #{ invisibles . tab } and spaces #{ invisibles . space } #{ invisibles . eol } "
2014-05-20 03:20:51 +04:00
2014-05-21 01:17:19 +04:00
it " displays newlines as their own token outside of the other tokens ' scopes " , ->
2014-05-20 04:27:59 +04:00
editor . setText " var "
expect ( component . lineNodeForScreenRow ( 0 ) . innerHTML ) . toBe " <span class= \" source js \" ><span class= \" storage modifier js \" >var</span></span><span class= \" invisible-character \" > #{ invisibles . eol } </span> "
2014-05-20 03:20:51 +04:00
2014-05-21 01:17:19 +04:00
it " displays trailing carriage returns using a visible, non-empty value " , ->
2014-05-20 21:38:01 +04:00
editor . setText " a line that ends with a carriage return \r \n "
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " a line that ends with a carriage return #{ invisibles . cr } #{ invisibles . eol } "
2014-05-20 03:20:51 +04:00
describe " when soft wrapping is enabled " , ->
beforeEach ->
2014-05-20 22:44:55 +04:00
editor . setText " a line that wraps "
2014-05-20 03:20:51 +04:00
editor . setSoftWrap ( true )
2014-06-07 01:28:27 +04:00
node.style.width = 16 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-05-20 03:20:51 +04:00
2014-05-20 22:44:55 +04:00
it " doesn ' t show end of line invisibles at the end of wrapped lines " , ->
expect ( component . lineNodeForScreenRow ( 0 ) . textContent ) . toBe " a line that "
expect ( component . lineNodeForScreenRow ( 1 ) . textContent ) . toBe " wraps #{ invisibles . space } #{ invisibles . eol } "
2014-05-20 03:20:51 +04:00
2014-04-09 23:49:56 +04:00
describe " when indent guides are enabled " , ->
2014-04-10 03:07:04 +04:00
beforeEach ->
2014-04-09 23:49:56 +04:00
component . setShowIndentGuide ( true )
2014-04-10 03:07:04 +04:00
it " adds an ' indent-guide ' class to spans comprising the leading whitespace " , ->
2014-05-16 20:44:49 +04:00
line1LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 1 ) )
2014-04-09 23:49:56 +04:00
expect ( line1LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line1LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line1LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
2014-05-17 01:21:12 +04:00
line2LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 2 ) )
2014-04-09 23:49:56 +04:00
expect ( line2LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line2LeafNodes [ 1 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line2LeafNodes [ 2 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
2014-04-10 03:07:04 +04:00
it " renders leading whitespace spans with the ' indent-guide ' class for empty lines " , ->
editor . getBuffer ( ) . insert ( [ 1 , Infinity ] , ' \n ' )
2014-05-17 01:21:12 +04:00
line2LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 2 ) )
2014-04-10 03:07:04 +04:00
expect ( line2LeafNodes . length ) . toBe 3
expect ( line2LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line2LeafNodes [ 1 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line2LeafNodes [ 2 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 2 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
2014-04-10 03:20:22 +04:00
it " renders indent guides correctly on lines containing only whitespace " , ->
editor . getBuffer ( ) . insert ( [ 1 , Infinity ] , ' \n ' )
2014-05-17 01:21:12 +04:00
line2LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 2 ) )
2014-04-10 03:20:22 +04:00
expect ( line2LeafNodes . length ) . toBe 3
expect ( line2LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line2LeafNodes [ 1 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line2LeafNodes [ 2 ] . textContent ) . toBe ' '
expect ( line2LeafNodes [ 2 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
2014-04-16 22:37:35 +04:00
it " does not render indent guides in trailing whitespace for lines containing non whitespace characters " , ->
2014-05-17 01:21:12 +04:00
editor . getBuffer ( ) . setText " hi "
line0LeafNodes = getLeafNodes ( component . lineNodeForScreenRow ( 0 ) )
2014-04-16 22:37:35 +04:00
expect ( line0LeafNodes [ 0 ] . textContent ) . toBe ' '
expect ( line0LeafNodes [ 0 ] . classList . contains ( ' indent-guide ' ) ) . toBe true
expect ( line0LeafNodes [ 1 ] . textContent ) . toBe ' '
expect ( line0LeafNodes [ 1 ] . classList . contains ( ' indent-guide ' ) ) . toBe false
2014-04-09 23:49:56 +04:00
getLeafNodes = (node) ->
if node . children . length > 0
flatten ( toArray ( node . children ) . map ( getLeafNodes ) )
else
[ node ]
2014-06-11 17:18:52 +04:00
describe " when the buffer contains null bytes " , ->
it " excludes the null byte from character measurement " , ->
editor . setText ( " a \0 b " )
expect ( editor . pixelPositionForScreenPosition ( [ 0 , Infinity ] ) . left ) . toEqual 2 * charWidth
2014-06-14 03:52:49 +04:00
describe " gutter rendering " , ->
2014-06-13 05:11:55 +04:00
[ gutter ] = [ ]
lineNumberHasClass = (screenRow, klass) ->
component . lineNumberNodeForScreenRow ( screenRow ) . classList . contains ( klass )
2014-06-14 03:42:26 +04:00
lineNumberForBufferRowHasClass = (bufferRow, klass) ->
2014-06-13 05:11:55 +04:00
screenRow = editor . displayBuffer . screenRowForBufferRow ( bufferRow )
component . lineNumberNodeForScreenRow ( screenRow ) . classList . contains ( klass )
2014-06-10 00:56:23 +04:00
2014-06-06 01:03:55 +04:00
beforeEach ->
{ gutter } = component . refs
2014-04-08 04:13:19 +04:00
it " renders the currently-visible line numbers " , ->
node.style.height = 4.5 * lineHeightInPixels + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-08 04:13:19 +04:00
2014-05-17 00:52:24 +04:00
expect ( node . querySelectorAll ( ' .line-number ' ) . length ) . toBe 6 + 2 + 1 # line overdraw margin below + dummy line number
expect ( component . lineNumberNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ nbsp } 1 "
expect ( component . lineNumberNodeForScreenRow ( 5 ) . textContent ) . toBe " #{ nbsp } 6 "
2014-04-08 04:13:19 +04:00
2014-04-14 23:06:15 +04:00
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
verticalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-04-08 04:13:19 +04:00
2014-05-17 00:52:24 +04:00
expect ( node . querySelectorAll ( ' .line-number ' ) . length ) . toBe 6 + 4 + 1 # line overdraw margin above/below + dummy line number
2014-05-12 22:45:58 +04:00
2014-05-17 00:52:24 +04:00
expect ( component . lineNumberNodeForScreenRow ( 2 ) . textContent ) . toBe " #{ nbsp } 3 "
expect ( component . lineNumberNodeForScreenRow ( 2 ) . offsetTop ) . toBe 2 * lineHeightInPixels
return
expect ( component . lineNumberNodeForScreenRow ( 7 ) . textContent ) . toBe " #{ nbsp } 8 "
expect ( component . lineNumberNodeForScreenRow ( 7 ) . offsetTop ) . toBe 7 * lineHeightInPixels
2014-04-08 04:13:19 +04:00
2014-05-12 22:45:58 +04:00
it " updates the translation of subsequent line numbers when lines are inserted or removed " , ->
2014-04-22 21:10:48 +04:00
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n \n ' )
lineNumberNodes = node . querySelectorAll ( ' .line-number ' )
2014-05-17 00:52:24 +04:00
expect ( component . lineNumberNodeForScreenRow ( 0 ) . offsetTop ) . toBe 0
expect ( component . lineNumberNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 2 ) . offsetTop ) . toBe 2 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 3 ) . offsetTop ) . toBe 3 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 4 ) . offsetTop ) . toBe 4 * lineHeightInPixels
2014-04-22 21:10:48 +04:00
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n \n ' )
2014-05-17 00:52:24 +04:00
expect ( component . lineNumberNodeForScreenRow ( 0 ) . offsetTop ) . toBe 0
expect ( component . lineNumberNodeForScreenRow ( 1 ) . offsetTop ) . toBe 1 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 2 ) . offsetTop ) . toBe 2 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 3 ) . offsetTop ) . toBe 3 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 4 ) . offsetTop ) . toBe 4 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 5 ) . offsetTop ) . toBe 5 * lineHeightInPixels
expect ( component . lineNumberNodeForScreenRow ( 6 ) . offsetTop ) . toBe 6 * lineHeightInPixels
2014-04-22 21:10:48 +04:00
2014-04-08 05:06:39 +04:00
it " renders • characters for soft-wrapped lines " , ->
editor . setSoftWrap ( true )
node.style.height = 4.5 * lineHeightInPixels + ' px '
node.style.width = 30 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-08 05:06:39 +04:00
2014-05-17 00:52:24 +04:00
expect ( node . querySelectorAll ( ' .line-number ' ) . length ) . toBe 6 + lineOverdrawMargin + 1 # 1 dummy line node
expect ( component . lineNumberNodeForScreenRow ( 0 ) . textContent ) . toBe " #{ nbsp } 1 "
expect ( component . lineNumberNodeForScreenRow ( 1 ) . textContent ) . toBe " #{ nbsp } • "
expect ( component . lineNumberNodeForScreenRow ( 2 ) . textContent ) . toBe " #{ nbsp } 2 "
expect ( component . lineNumberNodeForScreenRow ( 3 ) . textContent ) . toBe " #{ nbsp } • "
expect ( component . lineNumberNodeForScreenRow ( 4 ) . textContent ) . toBe " #{ nbsp } 3 "
expect ( component . lineNumberNodeForScreenRow ( 5 ) . textContent ) . toBe " #{ nbsp } • "
2014-04-08 05:06:39 +04:00
2014-05-17 00:52:24 +04:00
it " pads line numbers to be right-justified based on the maximum number of line number digits " , ->
2014-04-24 21:09:51 +04:00
editor . getBuffer ( ) . setText ( [ 1 . . 10 ] . join ( ' \n ' ) )
2014-05-17 00:52:24 +04:00
for screenRow in [ 0 . . 8 ]
expect ( component . lineNumberNodeForScreenRow ( screenRow ) . textContent ) . toBe " #{ nbsp } #{ screenRow + 1 } "
expect ( component . lineNumberNodeForScreenRow ( 9 ) . textContent ) . toBe " 10 "
2014-04-24 21:09:51 +04:00
2014-05-17 01:56:18 +04:00
gutterNode = node . querySelector ( ' .gutter ' )
initialGutterWidth = gutterNode . offsetWidth
2014-04-24 21:09:51 +04:00
# Removes padding when the max number of digits goes down
editor . getBuffer ( ) . delete ( [ [ 1 , 0 ] , [ 2 , 0 ] ] )
2014-05-17 00:52:24 +04:00
for screenRow in [ 0 . . 8 ]
expect ( component . lineNumberNodeForScreenRow ( screenRow ) . textContent ) . toBe " #{ screenRow + 1 } "
2014-05-17 01:56:18 +04:00
expect ( gutterNode . offsetWidth ) . toBeLessThan initialGutterWidth
# Increases padding when the max number of digits goes up
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n \n ' )
for screenRow in [ 0 . . 8 ]
expect ( component . lineNumberNodeForScreenRow ( screenRow ) . textContent ) . toBe " #{ nbsp } #{ screenRow + 1 } "
expect ( component . lineNumberNodeForScreenRow ( 9 ) . textContent ) . toBe " 10 "
expect ( gutterNode . offsetWidth ) . toBe initialGutterWidth
2014-04-24 21:09:51 +04:00
2014-06-06 23:23:12 +04:00
describe " fold decorations " , ->
describe " rendering fold decorations " , ->
it " adds the foldable class to line numbers when the line is foldable " , ->
expect ( lineNumberHasClass ( 0 , ' foldable ' ) ) . toBe true
expect ( lineNumberHasClass ( 1 , ' foldable ' ) ) . toBe true
expect ( lineNumberHasClass ( 2 , ' foldable ' ) ) . toBe false
expect ( lineNumberHasClass ( 3 , ' foldable ' ) ) . toBe false
expect ( lineNumberHasClass ( 4 , ' foldable ' ) ) . toBe true
expect ( lineNumberHasClass ( 5 , ' foldable ' ) ) . toBe false
it " updates the foldable class on the correct line numbers when the foldable positions change " , ->
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n ' )
expect ( lineNumberHasClass ( 0 , ' foldable ' ) ) . toBe false
expect ( lineNumberHasClass ( 1 , ' foldable ' ) ) . toBe true
expect ( lineNumberHasClass ( 2 , ' foldable ' ) ) . toBe true
expect ( lineNumberHasClass ( 3 , ' foldable ' ) ) . toBe false
expect ( lineNumberHasClass ( 4 , ' foldable ' ) ) . toBe false
expect ( lineNumberHasClass ( 5 , ' foldable ' ) ) . toBe true
expect ( lineNumberHasClass ( 6 , ' foldable ' ) ) . toBe false
2014-06-10 01:03:53 +04:00
it " updates the foldable class on a line number that becomes foldable " , ->
expect ( lineNumberHasClass ( 11 , ' foldable ' ) ) . toBe false
editor . getBuffer ( ) . insert ( [ 11 , 44 ] , ' \n fold me ' )
expect ( lineNumberHasClass ( 11 , ' foldable ' ) ) . toBe true
editor . undo ( )
expect ( lineNumberHasClass ( 11 , ' foldable ' ) ) . toBe false
2014-06-06 23:23:12 +04:00
it " adds, updates and removes the folded class on the correct line number nodes " , ->
editor . foldBufferRow ( 4 )
expect ( lineNumberHasClass ( 4 , ' folded ' ) ) . toBe true
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n ' )
expect ( lineNumberHasClass ( 4 , ' folded ' ) ) . toBe false
expect ( lineNumberHasClass ( 5 , ' folded ' ) ) . toBe true
editor . unfoldBufferRow ( 5 )
expect ( lineNumberHasClass ( 5 , ' folded ' ) ) . toBe false
describe " mouse interactions with fold indicators " , ->
[ gutterNode ] = [ ]
buildClickEvent = (target) ->
2014-06-10 00:56:23 +04:00
buildMouseEvent ( ' click ' , { target } )
2014-06-06 23:23:12 +04:00
beforeEach ->
gutterNode = node . querySelector ( ' .gutter ' )
it " folds and unfolds the block represented by the fold indicator when clicked " , ->
2014-06-10 00:56:23 +04:00
expect ( lineNumberHasClass ( 1 , ' folded ' ) ) . toBe false
2014-06-06 23:23:12 +04:00
2014-06-10 00:56:23 +04:00
lineNumber = component . lineNumberNodeForScreenRow ( 1 )
2014-06-06 23:23:12 +04:00
target = lineNumber . querySelector ( ' .icon-right ' )
2014-06-10 00:56:23 +04:00
target . dispatchEvent ( buildClickEvent ( target ) )
expect ( lineNumberHasClass ( 1 , ' folded ' ) ) . toBe true
lineNumber = component . lineNumberNodeForScreenRow ( 1 )
2014-06-06 23:23:12 +04:00
target = lineNumber . querySelector ( ' .icon-right ' )
2014-06-10 00:56:23 +04:00
target . dispatchEvent ( buildClickEvent ( target ) )
expect ( lineNumberHasClass ( 1 , ' folded ' ) ) . toBe false
2014-06-06 23:23:12 +04:00
it " does not fold when the line number node is clicked " , ->
2014-06-10 01:11:41 +04:00
lineNumber = component . lineNumberNodeForScreenRow ( 1 )
lineNumber . dispatchEvent ( buildClickEvent ( lineNumber ) )
2014-06-10 00:56:23 +04:00
expect ( lineNumberHasClass ( 1 , ' folded ' ) ) . toBe false
2014-06-05 03:36:41 +04:00
2014-06-10 02:18:12 +04:00
describe " cursor-line decorations " , ->
cursor = null
beforeEach ->
cursor = editor . getCursor ( )
it " modifies the cursor-line decoration when the cursor moves " , ->
cursor . setScreenPosition ( [ 0 , 0 ] )
expect ( lineNumberHasClass ( 0 , ' cursor-line ' ) ) . toBe true
cursor . setScreenPosition ( [ 1 , 0 ] )
expect ( lineNumberHasClass ( 0 , ' cursor-line ' ) ) . toBe false
expect ( lineNumberHasClass ( 1 , ' cursor-line ' ) ) . toBe true
it " updates cursor-line decorations for multiple cursors " , ->
cursor . setScreenPosition ( [ 2 , 0 ] )
cursor2 = editor . addCursorAtScreenPosition ( [ 8 , 0 ] )
cursor3 = editor . addCursorAtScreenPosition ( [ 10 , 0 ] )
expect ( lineNumberHasClass ( 2 , ' cursor-line ' ) ) . toBe true
expect ( lineNumberHasClass ( 8 , ' cursor-line ' ) ) . toBe true
expect ( lineNumberHasClass ( 10 , ' cursor-line ' ) ) . toBe true
cursor2 . destroy ( )
expect ( lineNumberHasClass ( 2 , ' cursor-line ' ) ) . toBe true
expect ( lineNumberHasClass ( 8 , ' cursor-line ' ) ) . toBe false
expect ( lineNumberHasClass ( 10 , ' cursor-line ' ) ) . toBe true
cursor3 . destroy ( )
expect ( lineNumberHasClass ( 2 , ' cursor-line ' ) ) . toBe true
expect ( lineNumberHasClass ( 8 , ' cursor-line ' ) ) . toBe false
expect ( lineNumberHasClass ( 10 , ' cursor-line ' ) ) . toBe false
it " adds cursor-line decorations to multiple lines when a selection is performed " , ->
cursor . setScreenPosition ( [ 1 , 0 ] )
editor . selectDown ( 2 )
expect ( lineNumberHasClass ( 0 , ' cursor-line ' ) ) . toBe false
expect ( lineNumberHasClass ( 1 , ' cursor-line ' ) ) . toBe true
expect ( lineNumberHasClass ( 2 , ' cursor-line ' ) ) . toBe true
expect ( lineNumberHasClass ( 3 , ' cursor-line ' ) ) . toBe true
expect ( lineNumberHasClass ( 4 , ' cursor-line ' ) ) . toBe false
2014-06-17 01:09:08 +04:00
describe " when decorations are applied to markers " , ->
{ marker , decoration } = { }
beforeEach ->
marker = editor . displayBuffer . markBufferRange ( [ [ 2 , 13 ] , [ 3 , 15 ] ] , class : ' my-marker ' , invalidate: ' inside ' )
decoration = { type: ' gutter ' , class : ' someclass ' }
editor . addDecorationForMarker ( marker , decoration )
waitsFor -> not component . decorationChangedImmediate ?
2014-06-13 04:45:45 +04:00
2014-06-17 01:09:08 +04:00
it " initially renders off screen lines with line number classes based on the decorations on their buffer row " , ->
node.style.height = 4.5 * lineHeightInPixels + ' px '
component . measureScrollView ( )
expect ( component . lineNumberNodeForScreenRow ( 9 ) ) . not . toBeDefined ( )
2014-06-04 20:53:22 +04:00
2014-06-17 01:09:08 +04:00
marker = editor . displayBuffer . markBufferRange ( [ [ 9 , 0 ] , [ 9 , 0 ] ] , invalidate: ' inside ' )
editor . addDecorationForMarker ( marker , type: ' gutter ' , class : ' fancy-class ' )
editor . addDecorationForMarker ( marker , type: ' someother-type ' , class : ' nope-class ' )
2014-06-04 20:53:22 +04:00
2014-06-17 01:09:08 +04:00
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
verticalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-06-04 20:53:22 +04:00
2014-06-17 01:09:08 +04:00
expect ( lineNumberHasClass ( 9 , ' fancy-class ' ) ) . toBe true
expect ( lineNumberHasClass ( 9 , ' nope-class ' ) ) . toBe false
it " initially renders off screen lines with line number classes based on the decorations on their buffer row " , ->
marker = editor . displayBuffer . markBufferRange ( [ [ 9 , 0 ] , [ 9 , 0 ] ] , invalidate: ' inside ' )
editor . addDecorationForMarker ( marker , decoration )
waitsFor -> not component . decorationChangedImmediate ?
runs ->
expect ( lineNumberForBufferRowHasClass ( 9 , ' someclass ' ) ) . toBe true
editor . foldBufferRow ( 5 )
editor . removeDecorationForMarker ( marker , decoration )
2014-06-04 20:53:22 +04:00
2014-06-17 01:09:08 +04:00
waitsFor -> not component . decorationChangedImmediate ?
runs ->
expect ( lineNumberForBufferRowHasClass ( 9 , ' someclass ' ) ) . toBe false
2014-06-04 20:53:22 +04:00
2014-06-17 01:09:08 +04:00
it " updates line number classes when the marker moves " , ->
expect ( lineNumberHasClass ( 1 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 2 , ' someclass ' ) ) . toBe true
expect ( lineNumberHasClass ( 3 , ' someclass ' ) ) . toBe true
expect ( lineNumberHasClass ( 4 , ' someclass ' ) ) . toBe false
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n ' )
waitsFor -> not component . decorationChangedImmediate ?
runs ->
expect ( lineNumberHasClass ( 2 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 3 , ' someclass ' ) ) . toBe true
expect ( lineNumberHasClass ( 4 , ' someclass ' ) ) . toBe true
expect ( lineNumberHasClass ( 5 , ' someclass ' ) ) . toBe false
editor . getBuffer ( ) . deleteRows ( 0 , 1 )
waitsFor -> not component . decorationChangedImmediate ?
runs ->
expect ( lineNumberHasClass ( 0 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 1 , ' someclass ' ) ) . toBe true
expect ( lineNumberHasClass ( 2 , ' someclass ' ) ) . toBe true
expect ( lineNumberHasClass ( 3 , ' someclass ' ) ) . toBe false
2014-06-13 05:11:55 +04:00
2014-06-17 01:09:08 +04:00
it " removes line number classes when a decoration ' s marker is invalidated " , ->
editor . getBuffer ( ) . insert ( [ 3 , 2 ] , ' n ' )
2014-06-13 05:11:55 +04:00
2014-06-17 01:09:08 +04:00
waitsFor -> not component . decorationChangedImmediate ?
runs ->
2014-06-13 05:11:55 +04:00
2014-06-17 01:09:08 +04:00
expect ( marker . isValid ( ) ) . toBe false
expect ( lineNumberHasClass ( 1 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 2 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 3 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 4 , ' someclass ' ) ) . toBe false
editor . getBuffer ( ) . undo ( )
waitsFor -> not component . decorationChangedImmediate ?
runs ->
expect ( marker . isValid ( ) ) . toBe true
2014-06-05 21:50:57 +04:00
expect ( lineNumberHasClass ( 1 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 2 , ' someclass ' ) ) . toBe true
expect ( lineNumberHasClass ( 3 , ' someclass ' ) ) . toBe true
expect ( lineNumberHasClass ( 4 , ' someclass ' ) ) . toBe false
2014-06-05 03:36:41 +04:00
2014-06-17 01:09:08 +04:00
it " removes the classes and unsubscribes from the marker when decoration is removed " , ->
editor . removeDecorationForMarker ( marker , decoration )
2014-06-05 03:36:41 +04:00
2014-06-17 01:09:08 +04:00
waitsFor -> not component . decorationChangedImmediate ?
runs ->
expect ( lineNumberHasClass ( 1 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 2 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 3 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 4 , ' someclass ' ) ) . toBe false
2014-06-05 03:36:41 +04:00
2014-06-17 01:09:08 +04:00
editor . getBuffer ( ) . insert ( [ 0 , 0 ] , ' \n ' )
2014-06-05 23:32:45 +04:00
2014-06-17 01:09:08 +04:00
waitsFor -> not component . decorationChangedImmediate ?
runs ->
expect ( lineNumberHasClass ( 2 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 3 , ' someclass ' ) ) . toBe false
2014-06-05 03:36:41 +04:00
2014-06-17 01:09:08 +04:00
it " removes the line number classes when the decoration ' s marker is destroyed " , ->
marker . destroy ( )
2014-06-05 03:36:41 +04:00
2014-06-17 01:09:08 +04:00
waitsFor -> not component . decorationChangedImmediate ?
runs ->
expect ( lineNumberHasClass ( 1 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 2 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 3 , ' someclass ' ) ) . toBe false
expect ( lineNumberHasClass ( 4 , ' someclass ' ) ) . toBe false
2014-06-04 20:53:22 +04:00
2014-04-04 03:04:19 +04:00
describe " cursor rendering " , ->
2014-05-13 01:13:56 +04:00
it " renders the currently visible cursors, translated relative to the scroll position " , ->
2014-04-04 03:04:19 +04:00
cursor1 = editor . getCursor ( )
cursor1 . setScreenPosition ( [ 0 , 5 ] )
node.style.height = 4.5 * lineHeightInPixels + ' px '
2014-05-13 01:13:56 +04:00
node.style.width = 20 * lineHeightInPixels + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-04 03:04:19 +04:00
cursorNodes = node . querySelectorAll ( ' .cursor ' )
expect ( cursorNodes . length ) . toBe 1
expect ( cursorNodes [ 0 ] . offsetHeight ) . toBe lineHeightInPixels
expect ( cursorNodes [ 0 ] . offsetWidth ) . toBe charWidth
2014-05-13 01:13:56 +04:00
expect ( cursorNodes [ 0 ] . style [ ' -webkit-transform ' ] ) . toBe " translate3d( #{ 5 * charWidth } px, #{ 0 * lineHeightInPixels } px, 0px) "
2014-04-04 03:04:19 +04:00
2014-06-02 06:09:29 +04:00
cursor2 = editor . addCursorAtScreenPosition ( [ 8 , 11 ] )
2014-04-04 03:04:19 +04:00
cursor3 = editor . addCursorAtScreenPosition ( [ 4 , 10 ] )
cursorNodes = node . querySelectorAll ( ' .cursor ' )
expect ( cursorNodes . length ) . toBe 2
expect ( cursorNodes [ 0 ] . offsetTop ) . toBe 0
2014-05-13 01:13:56 +04:00
expect ( cursorNodes [ 0 ] . style [ ' -webkit-transform ' ] ) . toBe " translate3d( #{ 5 * charWidth } px, #{ 0 * lineHeightInPixels } px, 0px) "
expect ( cursorNodes [ 1 ] . style [ ' -webkit-transform ' ] ) . toBe " translate3d( #{ 10 * charWidth } px, #{ 4 * lineHeightInPixels } px, 0px) "
2014-04-04 03:04:19 +04:00
2014-06-02 06:09:29 +04:00
verticalScrollbarNode.scrollTop = 4.5 * lineHeightInPixels
2014-04-14 23:06:15 +04:00
verticalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-05-13 01:13:56 +04:00
horizontalScrollbarNode.scrollLeft = 3.5 * charWidth
horizontalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-04-04 03:04:19 +04:00
cursorNodes = node . querySelectorAll ( ' .cursor ' )
expect ( cursorNodes . length ) . toBe 2
2014-06-02 06:09:29 +04:00
expect ( cursorNodes [ 0 ] . style [ ' -webkit-transform ' ] ) . toBe " translate3d( #{ ( 11 - 3.5 ) * charWidth } px, #{ ( 8 - 4.5 ) * lineHeightInPixels } px, 0px) "
expect ( cursorNodes [ 1 ] . style [ ' -webkit-transform ' ] ) . toBe " translate3d( #{ ( 10 - 3.5 ) * charWidth } px, #{ ( 4 - 4.5 ) * lineHeightInPixels } px, 0px) "
2014-04-04 03:04:19 +04:00
cursor3 . destroy ( )
cursorNodes = node . querySelectorAll ( ' .cursor ' )
expect ( cursorNodes . length ) . toBe 1
2014-05-13 01:13:56 +04:00
expect ( cursorNodes [ 0 ] . style [ ' -webkit-transform ' ] ) . toBe " translate3d( #{ ( 11 - 3.5 ) * charWidth } px, #{ ( 6 - 2.5 ) * lineHeightInPixels } px, 0px) "
2014-04-04 03:04:19 +04:00
it " accounts for character widths when positioning cursors " , ->
atom . config . set ( ' editor.fontFamily ' , ' sans-serif ' )
editor . setCursorScreenPosition ( [ 0 , 16 ] )
cursor = node . querySelector ( ' .cursor ' )
cursorRect = cursor . getBoundingClientRect ( )
2014-05-16 21:00:05 +04:00
cursorLocationTextNode = component . lineNodeForScreenRow ( 0 ) . querySelector ( ' .storage.type.function.js ' ) . firstChild
2014-04-04 03:04:19 +04:00
range = document . createRange ( )
range . setStart ( cursorLocationTextNode , 0 )
range . setEnd ( cursorLocationTextNode , 1 )
rangeRect = range . getBoundingClientRect ( )
expect ( cursorRect . left ) . toBe rangeRect . left
expect ( cursorRect . width ) . toBe rangeRect . width
2014-06-11 21:53:51 +04:00
it " sets the cursor to the default character width at the end of a line " , ->
editor . setCursorScreenPosition ( [ 0 , Infinity ] )
cursorNode = node . querySelector ( ' .cursor ' )
expect ( cursorNode . offsetWidth ) . toBe charWidth
2014-06-11 21:40:57 +04:00
it " gives the cursor a non-zero width even if it ' s inside atomic tokens " , ->
editor . setCursorScreenPosition ( [ 1 , 0 ] )
cursorNode = node . querySelector ( ' .cursor ' )
expect ( cursorNode . offsetWidth ) . toBe charWidth
2014-04-08 00:28:44 +04:00
it " blinks cursors when they aren ' t moving " , ->
2014-05-21 00:04:59 +04:00
spyOn ( _ . _ , ' now ' ) . andCallFake -> window . now # Ensure _.debounce is based on our fake spec timeline
2014-05-13 22:12:04 +04:00
cursorsNode = node . querySelector ( ' .cursors ' )
2014-05-21 00:04:59 +04:00
expect ( cursorsNode . classList . contains ( ' blink-off ' ) ) . toBe false
advanceClock ( component . props . cursorBlinkPeriod / 2 )
expect ( cursorsNode . classList . contains ( ' blink-off ' ) ) . toBe true
2014-06-02 06:09:29 +04:00
2014-05-21 00:04:59 +04:00
advanceClock ( component . props . cursorBlinkPeriod / 2 )
expect ( cursorsNode . classList . contains ( ' blink-off ' ) ) . toBe false
2014-04-07 23:51:25 +04:00
2014-05-13 22:12:04 +04:00
# Stop blinking after moving the cursor
2014-04-08 00:28:44 +04:00
editor . moveCursorRight ( )
2014-05-21 00:04:59 +04:00
expect ( cursorsNode . classList . contains ( ' blink-off ' ) ) . toBe false
2014-04-08 00:28:44 +04:00
2014-05-21 00:04:59 +04:00
advanceClock ( component . props . cursorBlinkResumeDelay )
advanceClock ( component . props . cursorBlinkPeriod / 2 )
expect ( cursorsNode . classList . contains ( ' blink-off ' ) ) . toBe true
2014-04-08 00:28:44 +04:00
2014-04-18 00:19:40 +04:00
it " does not render cursors that are associated with non-empty selections " , ->
editor . setSelectedScreenRange ( [ [ 0 , 4 ] , [ 4 , 6 ] ] )
editor . addCursorAtScreenPosition ( [ 6 , 8 ] )
cursorNodes = node . querySelectorAll ( ' .cursor ' )
expect ( cursorNodes . length ) . toBe 1
2014-05-13 01:13:56 +04:00
expect ( cursorNodes [ 0 ] . style [ ' -webkit-transform ' ] ) . toBe " translate3d( #{ 8 * charWidth } px, #{ 6 * lineHeightInPixels } px, 0px) "
2014-04-18 00:19:40 +04:00
2014-06-02 06:33:16 +04:00
it " updates cursor positions when the line height changes " , ->
editor . setCursorBufferPosition ( [ 1 , 10 ] )
component . setLineHeight ( 2 )
2014-06-02 12:52:10 +04:00
cursorNode = node . querySelector ( ' .cursor ' )
expect ( cursorNode . style [ ' -webkit-transform ' ] ) . toBe " translate3d( #{ 10 * editor . getDefaultCharWidth ( ) } px, #{ editor . getLineHeightInPixels ( ) } px, 0px) "
2014-06-02 12:52:31 +04:00
it " updates cursor positions when the font size changes " , ->
editor . setCursorBufferPosition ( [ 1 , 10 ] )
component . setFontSize ( 10 )
cursorNode = node . querySelector ( ' .cursor ' )
2014-06-02 06:33:16 +04:00
expect ( cursorNode . style [ ' -webkit-transform ' ] ) . toBe " translate3d( #{ 10 * editor . getDefaultCharWidth ( ) } px, #{ editor . getLineHeightInPixels ( ) } px, 0px) "
2014-06-02 12:55:14 +04:00
it " updates cursor positions when the font family changes " , ->
editor . setCursorBufferPosition ( [ 1 , 10 ] )
component . setFontFamily ( ' sans-serif ' )
cursorNode = node . querySelector ( ' .cursor ' )
{ left } = editor . pixelPositionForScreenPosition ( [ 1 , 10 ] )
expect ( cursorNode . style [ ' -webkit-transform ' ] ) . toBe " translate3d( #{ left } px, #{ editor . getLineHeightInPixels ( ) } px, 0px) "
2014-04-04 03:04:19 +04:00
describe " selection rendering " , ->
2014-05-13 20:32:20 +04:00
[ scrollViewNode , scrollViewClientLeft ] = [ ]
2014-04-08 04:13:19 +04:00
beforeEach ->
2014-05-13 20:32:20 +04:00
scrollViewNode = node . querySelector ( ' .scroll-view ' )
2014-04-08 04:13:19 +04:00
scrollViewClientLeft = node . querySelector ( ' .scroll-view ' ) . getBoundingClientRect ( ) . left
2014-05-16 21:10:39 +04:00
it " renders 1 region for 1-line selections " , ->
# 1-line selection
editor . setSelectedScreenRange ( [ [ 1 , 6 ] , [ 1 , 10 ] ] )
regions = node . querySelectorAll ( ' .selection .region ' )
expect ( regions . length ) . toBe 1
regionRect = regions [ 0 ] . getBoundingClientRect ( )
expect ( regionRect . top ) . toBe 1 * lineHeightInPixels
expect ( regionRect . height ) . toBe 1 * lineHeightInPixels
expect ( regionRect . left ) . toBe scrollViewClientLeft + 6 * charWidth
expect ( regionRect . width ) . toBe 4 * charWidth
it " renders 2 regions for 2-line selections " , ->
editor . setSelectedScreenRange ( [ [ 1 , 6 ] , [ 2 , 10 ] ] )
regions = node . querySelectorAll ( ' .selection .region ' )
expect ( regions . length ) . toBe 2
region1Rect = regions [ 0 ] . getBoundingClientRect ( )
expect ( region1Rect . top ) . toBe 1 * lineHeightInPixels
expect ( region1Rect . height ) . toBe 1 * lineHeightInPixels
expect ( region1Rect . left ) . toBe scrollViewClientLeft + 6 * charWidth
expect ( region1Rect . right ) . toBe scrollViewNode . getBoundingClientRect ( ) . right
region2Rect = regions [ 1 ] . getBoundingClientRect ( )
expect ( region2Rect . top ) . toBe 2 * lineHeightInPixels
expect ( region2Rect . height ) . toBe 1 * lineHeightInPixels
expect ( region2Rect . left ) . toBe scrollViewClientLeft + 0
expect ( region2Rect . width ) . toBe 10 * charWidth
it " renders 3 regions for selections with more than 2 lines " , ->
editor . setSelectedScreenRange ( [ [ 1 , 6 ] , [ 5 , 10 ] ] )
regions = node . querySelectorAll ( ' .selection .region ' )
expect ( regions . length ) . toBe 3
region1Rect = regions [ 0 ] . getBoundingClientRect ( )
expect ( region1Rect . top ) . toBe 1 * lineHeightInPixels
expect ( region1Rect . height ) . toBe 1 * lineHeightInPixels
expect ( region1Rect . left ) . toBe scrollViewClientLeft + 6 * charWidth
expect ( region1Rect . right ) . toBe scrollViewNode . getBoundingClientRect ( ) . right
region2Rect = regions [ 1 ] . getBoundingClientRect ( )
expect ( region2Rect . top ) . toBe 2 * lineHeightInPixels
expect ( region2Rect . height ) . toBe 3 * lineHeightInPixels
expect ( region2Rect . left ) . toBe scrollViewClientLeft + 0
expect ( region2Rect . right ) . toBe scrollViewNode . getBoundingClientRect ( ) . right
region3Rect = regions [ 2 ] . getBoundingClientRect ( )
expect ( region3Rect . top ) . toBe 5 * lineHeightInPixels
expect ( region3Rect . height ) . toBe 1 * lineHeightInPixels
expect ( region3Rect . left ) . toBe scrollViewClientLeft + 0
expect ( region3Rect . width ) . toBe 10 * charWidth
it " does not render empty selections unless they are the first selection (to prevent a Chromium rendering artifact caused by removing it) " , ->
editor . addSelectionForBufferRange ( [ [ 2 , 2 ] , [ 2 , 2 ] ] )
expect ( editor . getSelection ( 0 ) . isEmpty ( ) ) . toBe true
expect ( editor . getSelection ( 1 ) . isEmpty ( ) ) . toBe true
expect ( node . querySelectorAll ( ' .selection ' ) . length ) . toBe 1
2014-04-18 00:19:52 +04:00
2014-06-02 07:48:19 +04:00
it " updates selections when the line height changes " , ->
editor . setSelectedBufferRange ( [ [ 1 , 6 ] , [ 1 , 10 ] ] )
component . setLineHeight ( 2 )
selectionNode = node . querySelector ( ' .region ' )
expect ( selectionNode . offsetTop ) . toBe editor . getLineHeightInPixels ( )
2014-06-02 13:37:21 +04:00
it " updates selections when the font size changes " , ->
editor . setSelectedBufferRange ( [ [ 1 , 6 ] , [ 1 , 10 ] ] )
component . setFontSize ( 10 )
selectionNode = node . querySelector ( ' .region ' )
expect ( selectionNode . offsetTop ) . toBe editor . getLineHeightInPixels ( )
expect ( selectionNode . offsetLeft ) . toBe 6 * editor . getDefaultCharWidth ( )
it " updates selections when the font family changes " , ->
editor . setSelectedBufferRange ( [ [ 1 , 6 ] , [ 1 , 10 ] ] )
component . setFontFamily ( ' sans-serif ' )
selectionNode = node . querySelector ( ' .region ' )
expect ( selectionNode . offsetTop ) . toBe editor . getLineHeightInPixels ( )
expect ( selectionNode . offsetLeft ) . toBe editor . pixelPositionForScreenPosition ( [ 1 , 6 ] ) . left
2014-05-23 05:54:57 +04:00
describe " hidden input field " , ->
it " renders the hidden input field at the position of the last cursor if the cursor is on screen and the editor is focused " , ->
editor . setVerticalScrollMargin ( 0 )
editor . setHorizontalScrollMargin ( 0 )
inputNode = node . querySelector ( ' .hidden-input ' )
node.style.height = 5 * lineHeightInPixels + ' px '
node.style.width = 10 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-05-23 05:54:57 +04:00
expect ( editor . getCursorScreenPosition ( ) ) . toEqual [ 0 , 0 ]
editor . setScrollTop ( 3 * lineHeightInPixels )
editor . setScrollLeft ( 3 * charWidth )
expect ( inputNode . offsetTop ) . toBe 0
expect ( inputNode . offsetLeft ) . toBe 0
# In bounds, not focused
editor . setCursorBufferPosition ( [ 5 , 4 ] )
expect ( inputNode . offsetTop ) . toBe 0
expect ( inputNode . offsetLeft ) . toBe 0
# In bounds and focused
inputNode . focus ( )
expect ( inputNode . offsetTop ) . toBe ( 5 * lineHeightInPixels ) - editor . getScrollTop ( )
expect ( inputNode . offsetLeft ) . toBe ( 4 * charWidth ) - editor . getScrollLeft ( )
# In bounds, not focused
inputNode . blur ( )
expect ( inputNode . offsetTop ) . toBe 0
expect ( inputNode . offsetLeft ) . toBe 0
# Out of bounds, not focused
editor . setCursorBufferPosition ( [ 1 , 2 ] )
expect ( inputNode . offsetTop ) . toBe 0
expect ( inputNode . offsetLeft ) . toBe 0
# Out of bounds, focused
inputNode . focus ( )
expect ( inputNode . offsetTop ) . toBe 0
expect ( inputNode . offsetLeft ) . toBe 0
2014-04-06 23:34:50 +04:00
describe " mouse interactions " , ->
2014-04-07 01:47:08 +04:00
linesNode = null
beforeEach ->
delayAnimationFrames = true
linesNode = node . querySelector ( ' .lines ' )
2014-04-06 23:56:38 +04:00
describe " when a non-folded line is single-clicked " , ->
describe " when no modifier keys are held down " , ->
2014-04-07 00:01:48 +04:00
it " moves the cursor to the nearest screen position " , ->
2014-04-06 23:56:38 +04:00
node.style.height = 4.5 * lineHeightInPixels + ' px '
2014-04-10 22:28:58 +04:00
node.style.width = 10 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-06 23:56:38 +04:00
editor . setScrollTop ( 3.5 * lineHeightInPixels )
2014-04-10 22:28:58 +04:00
editor . setScrollLeft ( 2 * charWidth )
2014-04-06 23:56:38 +04:00
2014-04-07 01:47:08 +04:00
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 4 , 8 ] ) ) )
2014-04-06 23:56:38 +04:00
expect ( editor . getCursorScreenPosition ( ) ) . toEqual [ 4 , 8 ]
describe " when the shift key is held down " , ->
2014-04-07 00:01:48 +04:00
it " selects to the nearest screen position " , ->
2014-04-06 23:56:38 +04:00
editor . setCursorScreenPosition ( [ 3 , 4 ] )
2014-04-07 01:47:08 +04:00
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 6 ] ) , shiftKey: true ) )
2014-04-06 23:56:38 +04:00
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 3 , 4 ] , [ 5 , 6 ] ]
2014-04-07 00:01:19 +04:00
describe " when the command key is held down " , ->
2014-04-07 00:01:48 +04:00
it " adds a cursor at the nearest screen position " , ->
2014-04-07 00:01:19 +04:00
editor . setCursorScreenPosition ( [ 3 , 4 ] )
2014-04-07 01:47:08 +04:00
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 6 ] ) , metaKey: true ) )
2014-04-07 00:01:19 +04:00
expect ( editor . getSelectedScreenRanges ( ) ) . toEqual [ [ [ 3 , 4 ] , [ 3 , 4 ] ] , [ [ 5 , 6 ] , [ 5 , 6 ] ] ]
2014-04-07 21:10:14 +04:00
describe " when a non-folded line is double-clicked " , ->
it " selects the word containing the nearest screen position " , ->
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 10 ] ) , detail: 2 ) )
2014-04-07 21:57:53 +04:00
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
2014-04-07 21:10:14 +04:00
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 5 , 6 ] , [ 5 , 13 ] ]
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 6 , 6 ] ) , detail: 1 ) )
2014-04-07 21:57:53 +04:00
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
2014-04-07 21:10:14 +04:00
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 6 , 6 ] , [ 6 , 6 ] ]
2014-04-07 21:57:53 +04:00
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 8 , 8 ] ) , detail: 1 , shiftKey: true ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 6 , 6 ] , [ 8 , 8 ] ]
2014-04-07 22:04:59 +04:00
describe " when a non-folded line is triple-clicked " , ->
it " selects the line containing the nearest screen position " , ->
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 5 , 10 ] ) , detail: 3 ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 5 , 0 ] , [ 6 , 0 ] ]
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 6 , 6 ] ) , detail: 1 , shiftKey: true ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 5 , 0 ] , [ 7 , 0 ] ]
2014-04-08 03:46:27 +04:00
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 7 , 5 ] ) , detail: 1 ) )
2014-04-07 22:04:59 +04:00
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 8 , 8 ] ) , detail: 1 , shiftKey: true ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
2014-04-08 03:46:27 +04:00
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 7 , 5 ] , [ 8 , 8 ] ]
2014-04-07 22:04:59 +04:00
2014-04-07 01:47:50 +04:00
describe " when the mouse is clicked and dragged " , ->
it " selects to the nearest screen position until the mouse button is released " , ->
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 2 , 4 ] ) , which: 1 ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 6 , 8 ] ) , which: 1 ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 4 ] , [ 6 , 8 ] ]
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 10 , 0 ] ) , which: 1 ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 4 ] , [ 10 , 0 ] ]
linesNode . dispatchEvent ( buildMouseEvent ( ' mouseup ' ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 12 , 0 ] ) , which: 1 ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 4 ] , [ 10 , 0 ] ]
it " stops selecting if the mouse is dragged into the dev tools " , ->
linesNode . dispatchEvent ( buildMouseEvent ( ' mousedown ' , clientCoordinatesForScreenPosition ( [ 2 , 4 ] ) , which: 1 ) )
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 6 , 8 ] ) , which: 1 ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 4 ] , [ 6 , 8 ] ]
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 10 , 0 ] ) , which: 0 ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 4 ] , [ 6 , 8 ] ]
linesNode . dispatchEvent ( buildMouseEvent ( ' mousemove ' , clientCoordinatesForScreenPosition ( [ 8 , 0 ] ) , which: 1 ) )
nextAnimationFrame ( )
expect ( editor . getSelectedScreenRange ( ) ) . toEqual [ [ 2 , 4 ] , [ 6 , 8 ] ]
2014-04-06 23:34:50 +04:00
clientCoordinatesForScreenPosition = (screenPosition) ->
positionOffset = editor . pixelPositionForScreenPosition ( screenPosition )
2014-04-08 04:13:19 +04:00
scrollViewClientRect = node . querySelector ( ' .scroll-view ' ) . getBoundingClientRect ( )
2014-04-10 22:28:58 +04:00
clientX = scrollViewClientRect . left + positionOffset . left - editor . getScrollLeft ( )
2014-04-08 04:13:19 +04:00
clientY = scrollViewClientRect . top + positionOffset . top - editor . getScrollTop ( )
2014-04-06 23:34:50 +04:00
{ clientX , clientY }
2014-04-07 23:06:40 +04:00
describe " focus handling " , ->
inputNode = null
beforeEach ->
inputNode = node . querySelector ( ' .hidden-input ' )
it " transfers focus to the hidden input " , ->
expect ( document . activeElement ) . toBe document . body
node . focus ( )
expect ( document . activeElement ) . toBe inputNode
it " adds the ' is-focused ' class to the editor when the hidden input is focused " , ->
expect ( document . activeElement ) . toBe document . body
inputNode . focus ( )
expect ( node . classList . contains ( ' is-focused ' ) ) . toBe true
2014-06-11 18:37:16 +04:00
expect ( wrapperView . hasClass ( ' is-focused ' ) ) . toBe true
2014-04-07 23:06:40 +04:00
inputNode . blur ( )
expect ( node . classList . contains ( ' is-focused ' ) ) . toBe false
2014-06-11 18:37:16 +04:00
expect ( wrapperView . hasClass ( ' is-focused ' ) ) . toBe false
2014-04-08 03:52:48 +04:00
2014-06-10 02:45:32 +04:00
describe " selection handling " , ->
cursor = null
beforeEach ->
cursor = editor . getCursor ( )
cursor . setScreenPosition ( [ 0 , 0 ] )
it " adds the ' has-selection ' class to the editor when there is a selection " , ->
expect ( node . classList . contains ( ' has-selection ' ) ) . toBe false
editor . selectDown ( )
expect ( node . classList . contains ( ' has-selection ' ) ) . toBe true
cursor . moveDown ( )
expect ( node . classList . contains ( ' has-selection ' ) ) . toBe false
2014-04-08 22:04:27 +04:00
describe " scrolling " , ->
it " updates the vertical scrollbar when the scrollTop is changed in the model " , ->
node.style.height = 4.5 * lineHeightInPixels + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-08 22:04:27 +04:00
2014-04-14 23:06:15 +04:00
expect ( verticalScrollbarNode . scrollTop ) . toBe 0
2014-04-08 03:52:48 +04:00
2014-04-08 22:04:27 +04:00
editor . setScrollTop ( 10 )
2014-04-14 23:06:15 +04:00
expect ( verticalScrollbarNode . scrollTop ) . toBe 10
2014-04-08 22:04:27 +04:00
2014-05-13 01:18:28 +04:00
it " updates the horizontal scrollbar and the x transform of the lines based on the scrollLeft of the model " , ->
2014-04-08 22:04:27 +04:00
node.style.width = 30 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-08 03:52:48 +04:00
2014-05-16 20:44:49 +04:00
linesNode = node . querySelector ( ' .lines ' )
expect ( linesNode . style [ ' -webkit-transform ' ] ) . toBe " translate3d(0px, 0px, 0px) "
2014-04-09 00:12:41 +04:00
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 0
2014-04-08 22:04:27 +04:00
editor . setScrollLeft ( 100 )
2014-05-16 20:44:49 +04:00
expect ( linesNode . style [ ' -webkit-transform ' ] ) . toBe " translate3d(-100px, 0px, 0px) "
2014-04-09 00:12:41 +04:00
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 100
2014-04-09 00:20:19 +04:00
it " updates the scrollLeft of the model when the scrollLeft of the horizontal scrollbar changes " , ->
node.style.width = 30 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-09 00:20:19 +04:00
expect ( editor . getScrollLeft ( ) ) . toBe 0
2014-04-14 23:06:15 +04:00
horizontalScrollbarNode.scrollLeft = 100
horizontalScrollbarNode . dispatchEvent ( new UIEvent ( ' scroll ' ) )
2014-04-09 00:20:19 +04:00
expect ( editor . getScrollLeft ( ) ) . toBe 100
2014-04-09 00:38:06 +04:00
2014-04-24 20:18:41 +04:00
it " does not obscure the last line with the horizontal scrollbar " , ->
node.style.height = 4.5 * lineHeightInPixels + ' px '
node.style.width = 10 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-24 20:18:41 +04:00
editor . setScrollBottom ( editor . getScrollHeight ( ) )
2014-05-16 20:44:49 +04:00
lastLineNode = component . lineNodeForScreenRow ( editor . getLastScreenRow ( ) )
2014-04-24 20:18:41 +04:00
bottomOfLastLine = lastLineNode . getBoundingClientRect ( ) . bottom
topOfHorizontalScrollbar = horizontalScrollbarNode . getBoundingClientRect ( ) . top
expect ( bottomOfLastLine ) . toBe topOfHorizontalScrollbar
2014-04-24 00:13:19 +04:00
# Scroll so there's no space below the last line when the horizontal scrollbar disappears
2014-04-24 20:18:41 +04:00
node.style.width = 100 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-24 20:18:41 +04:00
bottomOfLastLine = lastLineNode . getBoundingClientRect ( ) . bottom
bottomOfEditor = node . getBoundingClientRect ( ) . bottom
expect ( bottomOfLastLine ) . toBe bottomOfEditor
2014-04-25 07:20:25 +04:00
it " does not obscure the last character of the longest line with the vertical scrollbar " , ->
node.style.height = 7 * lineHeightInPixels + ' px '
node.style.width = 10 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-25 07:20:25 +04:00
editor . setScrollLeft ( Infinity )
2014-05-16 20:44:49 +04:00
rightOfLongestLine = component . lineNodeForScreenRow ( 6 ) . getBoundingClientRect ( ) . right
2014-04-25 07:20:25 +04:00
leftOfVerticalScrollbar = verticalScrollbarNode . getBoundingClientRect ( ) . left
2014-05-16 20:44:49 +04:00
expect ( Math . round ( rightOfLongestLine ) ) . toBe leftOfVerticalScrollbar - 1 # Leave 1 px so the cursor is visible on the end of the line
2014-04-25 07:20:25 +04:00
2014-05-02 15:11:14 +04:00
it " only displays dummy scrollbars when scrollable in that direction " , ->
expect ( verticalScrollbarNode . style . display ) . toBe ' none '
expect ( horizontalScrollbarNode . style . display ) . toBe ' none '
node.style.height = 4.5 * lineHeightInPixels + ' px '
node.style.width = ' 1000px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-05-02 15:11:14 +04:00
expect ( verticalScrollbarNode . style . display ) . toBe ' '
expect ( horizontalScrollbarNode . style . display ) . toBe ' none '
node.style.width = 10 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-05-02 15:11:14 +04:00
expect ( verticalScrollbarNode . style . display ) . toBe ' '
expect ( horizontalScrollbarNode . style . display ) . toBe ' '
node.style.height = 20 * lineHeightInPixels + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-05-02 15:11:14 +04:00
expect ( verticalScrollbarNode . style . display ) . toBe ' none '
expect ( horizontalScrollbarNode . style . display ) . toBe ' '
2014-05-02 23:32:03 +04:00
it " makes the dummy scrollbar divs only as tall/wide as the actual scrollbars " , ->
2014-05-03 00:28:29 +04:00
node.style.height = 4 * lineHeightInPixels + ' px '
node.style.width = 10 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-05-03 00:28:29 +04:00
2014-05-02 23:32:03 +04:00
atom . themes . applyStylesheet " test " , """
: : - webkit - scrollbar {
width: 8 px ;
height: 8 px ;
}
"""
2014-05-03 00:28:29 +04:00
scrollbarCornerNode = node . querySelector ( ' .scrollbar-corner ' )
2014-05-02 23:32:03 +04:00
expect ( verticalScrollbarNode . offsetWidth ) . toBe 8
expect ( horizontalScrollbarNode . offsetHeight ) . toBe 8
2014-05-03 00:28:29 +04:00
expect ( scrollbarCornerNode . offsetWidth ) . toBe 8
expect ( scrollbarCornerNode . offsetHeight ) . toBe 8
2014-05-02 23:32:03 +04:00
2014-04-25 07:20:25 +04:00
it " assigns the bottom/right of the scrollbars to the width of the opposite scrollbar if it is visible " , ->
2014-05-02 14:15:42 +04:00
scrollbarCornerNode = node . querySelector ( ' .scrollbar-corner ' )
2014-04-25 07:20:25 +04:00
expect ( verticalScrollbarNode . style . bottom ) . toBe ' '
expect ( horizontalScrollbarNode . style . right ) . toBe ' '
2014-04-24 04:10:26 +04:00
node.style.height = 4.5 * lineHeightInPixels + ' px '
2014-04-25 07:20:25 +04:00
node.style.width = ' 1000px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-25 07:20:25 +04:00
expect ( verticalScrollbarNode . style . bottom ) . toBe ' '
expect ( horizontalScrollbarNode . style . right ) . toBe verticalScrollbarNode . offsetWidth + ' px '
2014-05-02 14:15:42 +04:00
expect ( scrollbarCornerNode . style . display ) . toBe ' none '
2014-04-24 04:10:26 +04:00
node.style.width = 10 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-25 07:20:25 +04:00
expect ( verticalScrollbarNode . style . bottom ) . toBe horizontalScrollbarNode . offsetHeight + ' px '
expect ( horizontalScrollbarNode . style . right ) . toBe verticalScrollbarNode . offsetWidth + ' px '
2014-05-02 14:15:42 +04:00
expect ( scrollbarCornerNode . style . display ) . toBe ' '
2014-04-24 04:10:26 +04:00
node.style.height = 20 * lineHeightInPixels + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-25 07:20:25 +04:00
expect ( verticalScrollbarNode . style . bottom ) . toBe horizontalScrollbarNode . offsetHeight + ' px '
expect ( horizontalScrollbarNode . style . right ) . toBe ' '
2014-05-02 14:15:42 +04:00
expect ( scrollbarCornerNode . style . display ) . toBe ' none '
2014-04-24 04:10:26 +04:00
2014-04-25 05:07:17 +04:00
it " accounts for the width of the gutter in the scrollWidth of the horizontal scrollbar " , ->
gutterNode = node . querySelector ( ' .gutter ' )
node.style.width = 10 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-04-25 05:07:17 +04:00
expect ( horizontalScrollbarNode . scrollWidth ) . toBe gutterNode . offsetWidth + editor . getScrollWidth ( )
2014-05-31 13:20:27 +04:00
describe " mousewheel events " , ->
2014-06-10 05:05:54 +04:00
beforeEach ->
atom . config . set ( ' editor.scrollSensitivity ' , 100 )
2014-04-09 00:38:06 +04:00
2014-06-10 05:05:54 +04:00
describe " updating scrollTop and scrollLeft " , ->
beforeEach ->
node.style.height = 4.5 * lineHeightInPixels + ' px '
node.style.width = 20 * charWidth + ' px '
component . measureScrollView ( )
2014-04-10 22:41:36 +04:00
2014-06-10 05:05:54 +04:00
it " updates the scrollLeft or scrollTop on mousewheel events depending on which delta is greater (x or y) " , ->
expect ( verticalScrollbarNode . scrollTop ) . toBe 0
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 0
node . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: - 5 , wheelDeltaY: - 10 ) )
expect ( verticalScrollbarNode . scrollTop ) . toBe 10
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 0
node . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: - 15 , wheelDeltaY: - 5 ) )
expect ( verticalScrollbarNode . scrollTop ) . toBe 10
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 15
it " updates the scrollLeft or scrollTop according to the scroll sensitivity " , ->
atom . config . set ( ' editor.scrollSensitivity ' , 50 )
node . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: - 5 , wheelDeltaY: - 10 ) )
expect ( verticalScrollbarNode . scrollTop ) . toBe 5
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 0
node . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: - 15 , wheelDeltaY: - 5 ) )
expect ( verticalScrollbarNode . scrollTop ) . toBe 5
expect ( horizontalScrollbarNode . scrollLeft ) . toBe 7
it " uses the previous scrollSensitivity when the value is not an int " , ->
atom . config . set ( ' editor.scrollSensitivity ' , ' nope ' )
node . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: - 10 ) )
expect ( verticalScrollbarNode . scrollTop ) . toBe 10
it " parses negative scrollSensitivity values as positive " , ->
atom . config . set ( ' editor.scrollSensitivity ' , - 50 )
node . dispatchEvent ( new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: - 10 ) )
expect ( verticalScrollbarNode . scrollTop ) . toBe 5
2014-05-31 13:20:27 +04:00
describe " when the mousewheel event ' s target is a line " , ->
it " keeps the line on the DOM if it is scrolled off-screen " , ->
node.style.height = 4.5 * lineHeightInPixels + ' px '
node.style.width = 20 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-05-20 00:33:17 +04:00
2014-05-31 13:20:27 +04:00
lineNode = node . querySelector ( ' .line ' )
wheelEvent = new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: - 500 )
Object . defineProperty ( wheelEvent , ' target ' , get: -> lineNode )
node . dispatchEvent ( wheelEvent )
2014-05-20 00:33:17 +04:00
2014-05-31 13:20:27 +04:00
expect ( node . contains ( lineNode ) ) . toBe true
2014-05-20 00:33:17 +04:00
2014-05-31 13:20:27 +04:00
it " does not set the mouseWheelScreenRow if scrolling horizontally " , ->
node.style.height = 4.5 * lineHeightInPixels + ' px '
node.style.width = 20 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-05-30 01:49:34 +04:00
2014-05-31 13:20:27 +04:00
lineNode = node . querySelector ( ' .line ' )
wheelEvent = new WheelEvent ( ' mousewheel ' , wheelDeltaX: 10 , wheelDeltaY: 0 )
Object . defineProperty ( wheelEvent , ' target ' , get: -> lineNode )
node . dispatchEvent ( wheelEvent )
2014-05-30 01:49:34 +04:00
2014-05-31 13:20:27 +04:00
expect ( component . mouseWheelScreenRow ) . toBe null
2014-05-30 01:49:34 +04:00
2014-05-31 13:36:59 +04:00
it " clears the mouseWheelScreenRow after a delay even if the event does not cause scrolling " , ->
spyOn ( _ . _ , ' now ' ) . andCallFake -> window . now # Ensure _.debounce is based on our fake spec timeline
expect ( editor . getScrollTop ( ) ) . toBe 0
lineNode = node . querySelector ( ' .line ' )
wheelEvent = new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: 10 )
Object . defineProperty ( wheelEvent , ' target ' , get: -> lineNode )
node . dispatchEvent ( wheelEvent )
expect ( editor . getScrollTop ( ) ) . toBe 0
expect ( component . mouseWheelScreenRow ) . toBe 0
advanceClock ( component . mouseWheelScreenRowClearDelay )
expect ( component . mouseWheelScreenRow ) . toBe null
2014-05-31 13:20:27 +04:00
it " does not preserve the line if it is on screen " , ->
expect ( node . querySelectorAll ( ' .line-number ' ) . length ) . toBe 14 # dummy line
lineNodes = node . querySelectorAll ( ' .line ' )
expect ( lineNodes . length ) . toBe 13
lineNode = lineNodes [ 0 ]
2014-05-30 06:54:29 +04:00
2014-05-31 13:20:27 +04:00
wheelEvent = new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: 100 ) # goes nowhere, we're already at scrollTop 0
Object . defineProperty ( wheelEvent , ' target ' , get: -> lineNode )
node . dispatchEvent ( wheelEvent )
2014-05-30 06:54:29 +04:00
2014-05-31 13:20:27 +04:00
expect ( component . mouseWheelScreenRow ) . toBe 0
editor . insertText ( " hello " )
expect ( node . querySelectorAll ( ' .line-number ' ) . length ) . toBe 14 # dummy line
expect ( node . querySelectorAll ( ' .line ' ) . length ) . toBe 13
2014-05-30 06:54:29 +04:00
2014-05-31 13:20:27 +04:00
describe " when the mousewheel event ' s target is a line number " , ->
it " keeps the line number on the DOM if it is scrolled off-screen " , ->
node.style.height = 4.5 * lineHeightInPixels + ' px '
node.style.width = 20 * charWidth + ' px '
2014-06-01 10:24:59 +04:00
component . measureScrollView ( )
2014-05-20 00:33:17 +04:00
2014-05-31 13:20:27 +04:00
lineNumberNode = node . querySelectorAll ( ' .line-number ' ) [ 1 ]
wheelEvent = new WheelEvent ( ' mousewheel ' , wheelDeltaX: 0 , wheelDeltaY: - 500 )
Object . defineProperty ( wheelEvent , ' target ' , get: -> lineNumberNode )
node . dispatchEvent ( wheelEvent )
2014-05-20 00:33:17 +04:00
2014-05-31 13:20:27 +04:00
expect ( node . contains ( lineNumberNode ) ) . toBe true
2014-05-20 00:33:17 +04:00
2014-04-10 22:41:36 +04:00
describe " input events " , ->
2014-04-14 23:06:15 +04:00
inputNode = null
beforeEach ->
inputNode = node . querySelector ( ' .hidden-input ' )
it " inserts the newest character in the input ' s value into the buffer " , ->
inputNode.value = ' x '
inputNode . dispatchEvent ( new Event ( ' input ' ) )
2014-04-10 22:41:36 +04:00
expect ( editor . lineForBufferRow ( 0 ) ) . toBe ' xvar quicksort = function () { '
2014-04-14 23:06:15 +04:00
inputNode.value = ' xy '
inputNode . dispatchEvent ( new Event ( ' input ' ) )
expect ( editor . lineForBufferRow ( 0 ) ) . toBe ' xyvar quicksort = function () { '
2014-04-15 00:10:56 +04:00
it " replaces the last character if the length of the input ' s value doesn ' t increase, as occurs with the accented character menu " , ->
2014-04-14 23:06:15 +04:00
inputNode.value = ' u '
inputNode . dispatchEvent ( new Event ( ' input ' ) )
expect ( editor . lineForBufferRow ( 0 ) ) . toBe ' uvar quicksort = function () { '
inputNode.value = ' ü '
inputNode . dispatchEvent ( new Event ( ' input ' ) )
2014-04-10 22:41:36 +04:00
expect ( editor . lineForBufferRow ( 0 ) ) . toBe ' üvar quicksort = function () { '
2014-04-17 22:43:13 +04:00
2014-06-11 16:07:41 +04:00
it " does not handle input events when input is disabled " , ->
component . setInputEnabled ( false )
inputNode.value = ' x '
inputNode . dispatchEvent ( new Event ( ' input ' ) )
expect ( editor . lineForBufferRow ( 0 ) ) . toBe ' var quicksort = function () { '
2014-04-17 22:43:13 +04:00
describe " commands " , ->
describe " editor:consolidate-selections " , ->
it " consolidates selections on the editor model, aborting the key binding if there is only one selection " , ->
spyOn ( editor , ' consolidateSelections ' ) . andCallThrough ( )
event = new CustomEvent ( ' editor:consolidate-selections ' , bubbles: true , cancelable: true )
event.abortKeyBinding = jasmine . createSpy ( " event.abortKeyBinding " )
node . dispatchEvent ( event )
expect ( editor . consolidateSelections ) . toHaveBeenCalled ( )
expect ( event . abortKeyBinding ) . toHaveBeenCalled ( )
2014-05-22 03:15:47 +04:00
describe " hiding and showing the editor " , ->
describe " when fontSize, fontFamily, or lineHeight changes while the editor is hidden " , ->
it " does not attempt to measure the lineHeight and defaultCharWidth until the editor becomes visible again " , ->
wrapperView . hide ( )
2014-05-22 04:04:44 +04:00
initialLineHeightInPixels = editor . getLineHeightInPixels ( )
2014-05-22 03:15:47 +04:00
initialCharWidth = editor . getDefaultCharWidth ( )
component . setLineHeight ( 2 )
2014-05-22 04:04:44 +04:00
expect ( editor . getLineHeightInPixels ( ) ) . toBe initialLineHeightInPixels
2014-05-22 03:15:47 +04:00
expect ( editor . getDefaultCharWidth ( ) ) . toBe initialCharWidth
component . setFontSize ( 22 )
2014-05-22 04:04:44 +04:00
expect ( editor . getLineHeightInPixels ( ) ) . toBe initialLineHeightInPixels
2014-05-22 03:15:47 +04:00
expect ( editor . getDefaultCharWidth ( ) ) . toBe initialCharWidth
component . setFontFamily ( ' monospace ' )
2014-05-22 04:04:44 +04:00
expect ( editor . getLineHeightInPixels ( ) ) . toBe initialLineHeightInPixels
2014-05-22 03:15:47 +04:00
expect ( editor . getDefaultCharWidth ( ) ) . toBe initialCharWidth
wrapperView . show ( )
2014-05-22 04:04:44 +04:00
expect ( editor . getLineHeightInPixels ( ) ) . not . toBe initialLineHeightInPixels
2014-05-22 03:15:47 +04:00
expect ( editor . getDefaultCharWidth ( ) ) . not . toBe initialCharWidth
2014-05-24 01:59:31 +04:00
describe " when lines are changed while the editor is hidden " , ->
it " does not measure new characters until the editor is shown again " , ->
editor . setText ( ' ' )
wrapperView . hide ( )
editor . setText ( ' var z = 1 ' )
editor . setCursorBufferPosition ( [ 0 , Infinity ] )
wrapperView . show ( )
expect ( node . querySelector ( ' .cursor ' ) . style [ ' -webkit-transform ' ] ) . toBe " translate3d( #{ 9 * charWidth } px, 0px, 0px) "
2014-06-10 00:56:23 +04:00
buildMouseEvent = (type, properties...) ->
properties = extend ( { bubbles: true , cancelable: true } , properties . . . )
event = new MouseEvent ( type , properties )
Object . defineProperty ( event , ' which ' , get: -> properties . which ) if properties . which ?
if properties . target ?
Object . defineProperty ( event , ' target ' , get: -> properties . target )
Object . defineProperty ( event , ' srcObject ' , get: -> properties . target )
event