       clientY = editorClientRect.top + positionOffset.top - editor.getScrollTop()
       {clientX, clientY}
 
+    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?
+      event
+
   it "transfers focus to the hidden input", ->
     expect(document.activeElement).toBe document.body
     node.focus()

      expect(region3Rect.width).toBe 10 * charWidth

  describe "mouse interactions", ->
    describe "when a non-folded line is single-clicked", ->
      describe "when no modifier keys are held down", ->
        it "moves the cursor to the nearest screen position", ->

          component.updateAllDimensions()
          editor.setScrollTop(3.5 * lineHeightInPixels)

          linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([4, 8])))
          expect(editor.getCursorScreenPosition()).toEqual [4, 8]

      describe "when the shift key is held down", ->
        it "selects to the nearest screen position", ->
          editor.setCursorScreenPosition([3, 4])
          linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 6]), shiftKey: true))
          expect(editor.getSelectedScreenRange()).toEqual [[3, 4], [5, 6]]

      describe "when the command key is held down", ->
        it "adds a cursor at the nearest screen position", ->
          editor.setCursorScreenPosition([3, 4])
          linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 6]), metaKey: true))
          expect(editor.getSelectedScreenRanges()).toEqual [[[3, 4], [3, 4]], [[5, 6], [5, 6]]]

    clientCoordinatesForScreenPosition = (screenPosition) ->
