Rich text editor for Flutter
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

306 lines
10 KiB

import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill/quill_delta.dart';
import 'package:test/test.dart';
void main() {
const testDocumentContents = 'data';
late QuillController controller;
setUp(() {
controller = QuillController.basic()
..compose(Delta()..insert(testDocumentContents),
const TextSelection.collapsed(offset: 0), ChangeSource.local);
});
group('controller', () {
test('set document', () {
const replacementContents = 'replacement\n';
final newDocument =
Document.fromDelta(Delta()..insert(replacementContents));
var listenerCalled = false;
controller
..addListener(() {
listenerCalled = true;
})
..document = newDocument;
expect(listenerCalled, isTrue);
expect(controller.document.toPlainText(), replacementContents);
});
test('getSelectionStyle', () {
controller
..formatText(0, 5, Attribute.h1)
..updateSelection(const TextSelection(baseOffset: 0, extentOffset: 4),
ChangeSource.local);
expect(controller.getSelectionStyle().values, [Attribute.h1]);
});
test('indentSelection with single line document', () {
var listenerCalled = false;
// With selection range
controller
..updateSelection(const TextSelection(baseOffset: 0, extentOffset: 4),
ChangeSource.local)
..addListener(() {
listenerCalled = true;
})
..indentSelection(true);
expect(listenerCalled, isTrue);
expect(controller.getSelectionStyle().values, [Attribute.indentL1]);
controller.indentSelection(true);
expect(controller.getSelectionStyle().values, [Attribute.indentL2]);
controller.indentSelection(false);
expect(controller.getSelectionStyle().values, [Attribute.indentL1]);
controller.indentSelection(false);
expect(controller.getSelectionStyle().values, []);
// With collapsed selection
controller
..updateSelection(
const TextSelection.collapsed(offset: 0), ChangeSource.local)
..indentSelection(true);
expect(controller.getSelectionStyle().values, [Attribute.indentL1]);
controller
..updateSelection(
const TextSelection.collapsed(offset: 0), ChangeSource.local)
..indentSelection(true);
expect(controller.getSelectionStyle().values, [Attribute.indentL2]);
controller.indentSelection(false);
expect(controller.getSelectionStyle().values, [Attribute.indentL1]);
controller.indentSelection(false);
expect(controller.getSelectionStyle().values, []);
});
test('indentSelection with multiline document', () {
controller
..compose(Delta()..insert('line1\nline2\nline3\n'),
const TextSelection.collapsed(offset: 0), ChangeSource.local)
// Indent first line
..updateSelection(
const TextSelection.collapsed(offset: 0), ChangeSource.local)
..indentSelection(true);
expect(controller.getSelectionStyle().values, [Attribute.indentL1]);
// Indent first two lines
controller
..updateSelection(const TextSelection(baseOffset: 0, extentOffset: 11),
ChangeSource.local)
..indentSelection(true);
// Should have both L1 and L2 indent attributes in selection.
expect(
controller.getAllSelectionStyles(),
contains(
const Style().put(Attribute.indentL1).put(Attribute.indentL2),
),
);
// Remaining lines should have no attributes.
controller.updateSelection(
TextSelection(
baseOffset: 12,
extentOffset: controller.document.toPlainText().length - 1),
ChangeSource.local);
expect(controller.getAllSelectionStyles(), everyElement(const Style()));
});
test('getAllIndividualSelectionStylesAndEmbed', () {
controller
..formatText(0, 2, Attribute.bold)
..replaceText(2, 2, BlockEmbed.image('/test'), null)
..updateSelection(const TextSelection(baseOffset: 0, extentOffset: 4),
ChangeSource.remote);
final result = controller.getAllIndividualSelectionStylesAndEmbed();
expect(result.length, 2);
expect(result[0].offset, 0);
expect(result[0].value, const Style().put(Attribute.bold));
expect((result[1].value as Embeddable).type, BlockEmbed.imageType);
});
test('getPlainText', () {
controller.updateSelection(
const TextSelection(baseOffset: 0, extentOffset: 4),
ChangeSource.local);
expect(controller.getPlainText(), testDocumentContents);
});
test('getAllSelectionStyles', () {
controller.formatText(0, 2, Attribute.bold);
expect(controller.getAllSelectionStyles(),
contains(const Style().put(Attribute.bold)));
});
test('undo', () {
var listenerCalled = false;
controller.updateSelection(
const TextSelection.collapsed(offset: 4), ChangeSource.local);
expect(
controller.document.toDelta(),
Delta()..insert('data\n'),
);
controller
..addListener(() {
listenerCalled = true;
})
..undo();
expect(listenerCalled, isTrue);
expect(controller.document.toDelta(), Delta()..insert('\n'));
});
test('redo', () {
var listenerCalled = false;
controller.updateSelection(
const TextSelection.collapsed(offset: 4), ChangeSource.local);
expect(controller.document.toDelta(), Delta()..insert('data\n'));
controller.undo();
expect(controller.document.toDelta(), Delta()..insert('\n'));
controller
..addListener(() {
listenerCalled = true;
})
..redo();
expect(listenerCalled, isTrue);
expect(controller.document.toDelta(), Delta()..insert('data\n'));
});
test('clear', () {
var listenerCalled = false;
controller
..addListener(() {
listenerCalled = true;
})
..clear();
expect(listenerCalled, isTrue);
expect(controller.document.toDelta(), Delta()..insert('\n'));
});
test('replaceText', () {
var listenerCalled = false;
controller
..addListener(() {
listenerCalled = true;
})
..replaceText(1, 2, '11', const TextSelection.collapsed(offset: 0));
expect(listenerCalled, isTrue);
expect(controller.document.toDelta(), Delta()..insert('d11a\n'));
});
test('formatTextStyle', () {
var listenerCalled = false;
final style = const Style().put(Attribute.bold).put(Attribute.italic);
controller
..addListener(() {
listenerCalled = true;
})
..formatTextStyle(0, 2, style);
expect(listenerCalled, isTrue);
expect(controller.document.collectAllStyles(0, 2), contains(style));
expect(controller.document.collectAllStyles(2, 4),
everyElement(const Style()));
});
test('formatText', () {
var listenerCalled = false;
controller
..addListener(() {
listenerCalled = true;
})
..formatText(0, 2, Attribute.bold);
expect(listenerCalled, isTrue);
expect(controller.document.collectAllStyles(0, 2),
contains(const Style().put(Attribute.bold)));
expect(controller.document.collectAllStyles(2, 4),
everyElement(const Style()));
});
test('formatSelection', () {
var listenerCalled = false;
controller
..updateSelection(const TextSelection(baseOffset: 0, extentOffset: 2),
ChangeSource.local)
..addListener(() {
listenerCalled = true;
})
..formatSelection(Attribute.bold);
expect(listenerCalled, isTrue);
expect(controller.document.collectAllStyles(0, 2),
contains(const Style().put(Attribute.bold)));
expect(controller.document.collectAllStyles(2, 4),
everyElement(const Style()));
});
test('moveCursorToStart', () {
var listenerCalled = false;
controller
..updateSelection(
const TextSelection.collapsed(offset: 4), ChangeSource.local)
..addListener(() {
listenerCalled = true;
});
expect(controller.selection, const TextSelection.collapsed(offset: 4));
controller.moveCursorToStart();
expect(listenerCalled, isTrue);
expect(controller.selection, const TextSelection.collapsed(offset: 0));
});
test('moveCursorToPosition', () {
var listenerCalled = false;
controller.addListener(() {
listenerCalled = true;
});
expect(controller.selection, const TextSelection.collapsed(offset: 0));
controller.moveCursorToPosition(2);
expect(listenerCalled, isTrue);
expect(controller.selection, const TextSelection.collapsed(offset: 2));
});
test('moveCursorToEnd', () {
var listenerCalled = false;
controller.addListener(() {
listenerCalled = true;
});
expect(controller.selection, const TextSelection.collapsed(offset: 0));
controller.moveCursorToEnd();
expect(listenerCalled, isTrue);
expect(controller.selection,
TextSelection.collapsed(offset: controller.document.length - 1));
});
test('updateSelection', () {
var listenerCalled = false;
const selection = TextSelection.collapsed(offset: 0);
controller
..addListener(() {
listenerCalled = true;
})
..updateSelection(selection, ChangeSource.local);
expect(listenerCalled, isTrue);
expect(controller.selection, selection);
});
test('compose', () {
var listenerCalled = false;
final originalContents = controller.document.toPlainText();
controller
..addListener(() {
listenerCalled = true;
})
..compose(Delta()..insert('test '),
const TextSelection.collapsed(offset: 0), ChangeSource.local);
expect(listenerCalled, isTrue);
expect(controller.document.toDelta(),
Delta()..insert('test $originalContents'));
});
});
}