overleaf/services/web/test/frontend/features/source-editor/extensions/line-duplication-command.test.ts

170 lines
5.3 KiB
TypeScript
Raw Normal View History

import { foldEffect, foldState } from '@codemirror/language'
import { EditorSelection, EditorState } from '@codemirror/state'
import { DecorationSet, EditorView } from '@codemirror/view'
import { expect } from 'chai'
import { duplicateSelection } from '../../../../../frontend/js/features/source-editor/commands/ranges'
type Position = {
from: number
to: number
}
function folds(foldRanges: DecorationSet) {
const ranges: Position[] = []
foldRanges.between(Number.MIN_VALUE, Number.MAX_VALUE, (from, to) => {
ranges.push({ from, to })
})
return ranges
}
describe('Line duplication command', function () {
describe('For single selections', function () {
describe('For cursor selection', function () {
it('Cursor selection duplicates line downwards', function () {
const view = new EditorView({
doc: 'line1\nline2',
selection: EditorSelection.cursor(0),
})
duplicateSelection(view)
expect(view.state.doc.toString()).to.equal('line1\nline1\nline2')
expect(view.state.selection.ranges.length).to.equal(1)
expect(view.state.selection.ranges[0].eq(EditorSelection.cursor(0))).to
.be.true
})
it('Preserves folded ranges', function () {
const view = new EditorView({
doc: '\\begin{itemize}\n\t\\item test\n\\end{itemize}',
extensions: foldState,
})
view.dispatch(
view.state.update({
selection: EditorSelection.cursor(0),
// Fold to \begin{itemize}...\end{itemize}
effects: [foldEffect.of({ from: 15, to: 28 })],
})
)
duplicateSelection(view)
expect(folds(view.state.field(foldState))).to.deep.equal([
{ from: 15, to: 28 },
{ from: 57, to: 70 },
])
expect(view.state.selection.ranges.length).to.equal(1)
expect(view.state.selection.ranges[0].eq(EditorSelection.cursor(0))).to
.be.true
})
})
describe('For range selections', function () {
it('Duplicates line with a cursor downwards', function () {
const view = new EditorView({
doc: 'line1\nline2',
selection: EditorSelection.cursor(0),
})
duplicateSelection(view)
expect(view.state.doc.toString()).to.equal('line1\nline1\nline2')
})
it('Duplicates range forwards', function () {
const view = new EditorView({
doc: 'line1\nline2',
selection: EditorSelection.range(0, 5),
})
duplicateSelection(view)
expect(view.state.doc.toString()).to.equal('line1line1\nline2')
expect(view.state.selection.ranges.length).to.equal(1)
expect(view.state.selection.ranges[0].eq(EditorSelection.range(5, 10)))
.to.be.true
})
it('Duplicates range backwards', function () {
const view = new EditorView({
doc: 'line1\nline2',
selection: EditorSelection.range(5, 0),
})
duplicateSelection(view)
expect(view.state.doc.toString()).to.equal('line1line1\nline2')
expect(view.state.selection.ranges.length).to.equal(1)
expect(view.state.selection.ranges[0].eq(EditorSelection.range(5, 0)))
.to.be.true
})
})
})
describe('For multiple selections', function () {
it('Preserves folded ranges', function () {
const doc =
'\\begin{itemize}\n\t\\item line1\n\\end{itemize}\n\\begin{itemize}\n\t\\item line2\n\\end{itemize}'
const view = new EditorView({
doc,
extensions: [foldState, EditorState.allowMultipleSelections.of(true)],
})
view.dispatch(
view.state.update({
selection: EditorSelection.create([
EditorSelection.cursor(0),
EditorSelection.cursor(43),
]),
effects: [
foldEffect.of({ from: 15, to: 29 }),
foldEffect.of({ from: 58, to: 72 }),
],
})
)
duplicateSelection(view)
expect(view.state.doc.toString()).to.equal(
'\\begin{itemize}\n\t\\item line1\n\\end{itemize}\n\\begin{itemize}\n\t\\item line1\n\\end{itemize}\n\\begin{itemize}\n\t\\item line2\n\\end{itemize}\n\\begin{itemize}\n\t\\item line2\n\\end{itemize}'
)
expect(folds(view.state.field(foldState))).to.deep.equal([
{ from: 15, to: 29 },
{ from: 58, to: 72 },
{ from: 101, to: 115 },
{ from: 144, to: 158 },
])
expect(view.state.selection.ranges.length).to.equal(2)
expect(view.state.selection.ranges[0].eq(EditorSelection.cursor(0))).to.be
.true
expect(view.state.selection.ranges[1].eq(EditorSelection.cursor(86))).to
.be.true
})
it('Duplicates all selections', function () {
const view = new EditorView({
doc: 'line1\nline2',
extensions: [EditorState.allowMultipleSelections.of(true)],
selection: EditorSelection.create([
EditorSelection.cursor(1),
EditorSelection.range(7, 9),
]),
})
duplicateSelection(view)
expect(view.state.doc.toString()).to.equal('line1\nline1\nlinine2')
expect(view.state.selection.ranges.length).to.equal(2)
expect(view.state.selection.ranges[0].eq(EditorSelection.cursor(1))).to.be
.true
expect(view.state.selection.ranges[1].eq(EditorSelection.range(15, 17)))
.to.be.true
})
})
})