mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
test: operation.dart
This commit is contained in:
parent
19bf8e3b7a
commit
b5e9bf6ee3
@ -4,8 +4,8 @@ import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy_editor/src/core/document/attributes.dart';
|
||||
import 'package:appflowy_editor/src/core/document/path.dart';
|
||||
import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart';
|
||||
import 'package:appflowy_editor/src/core/document/text_delta.dart';
|
||||
import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart';
|
||||
|
||||
class Node extends ChangeNotifier with LinkedListEntry<Node> {
|
||||
Node({
|
||||
@ -276,3 +276,26 @@ class TextNode extends Node {
|
||||
|
||||
String toPlainText() => _delta.toPlainText();
|
||||
}
|
||||
|
||||
extension NodeEquality on Iterable<Node> {
|
||||
bool equals(Iterable<Node> other) {
|
||||
if (length != other.length) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (!_nodeEquals(elementAt(i), other.elementAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _nodeEquals<T, U>(T base, U other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return base is Node &&
|
||||
other is Node &&
|
||||
other.type == base.type &&
|
||||
other.children.equals(base.children);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:appflowy_editor/src/core/document/attributes.dart';
|
||||
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||
import 'package:appflowy_editor/src/core/document/path.dart';
|
||||
@ -33,7 +35,9 @@ class InsertOperation extends Operation {
|
||||
|
||||
factory InsertOperation.fromJson(Map<String, dynamic> json) {
|
||||
final path = json['path'] as Path;
|
||||
final nodes = (json['nodes'] as List).map((n) => Node.fromJson(n));
|
||||
final nodes = (json['nodes'] as List)
|
||||
.map((n) => Node.fromJson(n))
|
||||
.toList(growable: false);
|
||||
return InsertOperation(path, nodes);
|
||||
}
|
||||
|
||||
@ -47,7 +51,7 @@ class InsertOperation extends Operation {
|
||||
return {
|
||||
'op': 'insert',
|
||||
'path': path,
|
||||
'nodes': nodes.map((n) => n.toJson()),
|
||||
'nodes': nodes.map((n) => n.toJson()).toList(growable: false),
|
||||
};
|
||||
}
|
||||
|
||||
@ -55,6 +59,18 @@ class InsertOperation extends Operation {
|
||||
Operation copyWith({Path? path}) {
|
||||
return InsertOperation(path ?? this.path, nodes);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is InsertOperation &&
|
||||
other.path.equals(path) &&
|
||||
other.nodes.equals(nodes);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => path.hashCode ^ Object.hashAll(nodes);
|
||||
}
|
||||
|
||||
/// [DeleteOperation] represents a delete operation.
|
||||
@ -66,7 +82,9 @@ class DeleteOperation extends Operation {
|
||||
|
||||
factory DeleteOperation.fromJson(Map<String, dynamic> json) {
|
||||
final path = json['path'] as Path;
|
||||
final nodes = (json['nodes'] as List).map((n) => Node.fromJson(n));
|
||||
final nodes = (json['nodes'] as List)
|
||||
.map((n) => Node.fromJson(n))
|
||||
.toList(growable: false);
|
||||
return DeleteOperation(path, nodes);
|
||||
}
|
||||
|
||||
@ -80,7 +98,7 @@ class DeleteOperation extends Operation {
|
||||
return {
|
||||
'op': 'delete',
|
||||
'path': path,
|
||||
'nodes': nodes.map((n) => n.toJson()),
|
||||
'nodes': nodes.map((n) => n.toJson()).toList(growable: false),
|
||||
};
|
||||
}
|
||||
|
||||
@ -88,6 +106,18 @@ class DeleteOperation extends Operation {
|
||||
Operation copyWith({Path? path}) {
|
||||
return DeleteOperation(path ?? this.path, nodes);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is DeleteOperation &&
|
||||
other.path.equals(path) &&
|
||||
other.nodes.equals(nodes);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => path.hashCode ^ Object.hashAll(nodes);
|
||||
}
|
||||
|
||||
/// [UpdateOperation] represents an attributes update operation.
|
||||
@ -137,6 +167,20 @@ class UpdateOperation extends Operation {
|
||||
{...oldAttributes},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is UpdateOperation &&
|
||||
other.path.equals(path) &&
|
||||
mapEquals(other.attributes, attributes) &&
|
||||
mapEquals(other.oldAttributes, oldAttributes);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
path.hashCode ^ attributes.hashCode ^ oldAttributes.hashCode;
|
||||
}
|
||||
|
||||
/// [UpdateTextOperation] represents a text update operation.
|
||||
@ -150,7 +194,7 @@ class UpdateTextOperation extends Operation {
|
||||
factory UpdateTextOperation.fromJson(Map<String, dynamic> json) {
|
||||
final path = json['path'] as Path;
|
||||
final delta = Delta.fromJson(json['delta']);
|
||||
final inverted = Delta.fromJson(json['invert']);
|
||||
final inverted = Delta.fromJson(json['inverted']);
|
||||
return UpdateTextOperation(path, delta, inverted);
|
||||
}
|
||||
|
||||
@ -174,6 +218,19 @@ class UpdateTextOperation extends Operation {
|
||||
Operation copyWith({Path? path}) {
|
||||
return UpdateTextOperation(path ?? this.path, delta, inverted);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is UpdateTextOperation &&
|
||||
other.path.equals(path) &&
|
||||
other.delta == delta &&
|
||||
other.inverted == inverted;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => delta.hashCode ^ inverted.hashCode;
|
||||
}
|
||||
|
||||
// TODO(Lucas.Xu): refactor this part
|
||||
|
@ -0,0 +1,79 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() async {
|
||||
group('operation.dart', () {
|
||||
test('test insert operation', () {
|
||||
final node = Node(type: 'example');
|
||||
final op = InsertOperation([0], [node]);
|
||||
final json = op.toJson();
|
||||
expect(json, {
|
||||
'op': 'insert',
|
||||
'path': [0],
|
||||
'nodes': [
|
||||
{
|
||||
'type': 'example',
|
||||
}
|
||||
]
|
||||
});
|
||||
expect(InsertOperation.fromJson(json), op);
|
||||
expect(op.invert().invert(), op);
|
||||
expect(op.copyWith(), op);
|
||||
});
|
||||
|
||||
test('test update operation', () {
|
||||
final op = UpdateOperation([0], {'a': 1}, {'a': 0});
|
||||
final json = op.toJson();
|
||||
expect(json, {
|
||||
'op': 'update',
|
||||
'path': [0],
|
||||
'attributes': {'a': 1},
|
||||
'oldAttributes': {'a': 0}
|
||||
});
|
||||
expect(UpdateOperation.fromJson(json), op);
|
||||
expect(op.invert().invert(), op);
|
||||
expect(op.copyWith(), op);
|
||||
});
|
||||
|
||||
test('test delete operation', () {
|
||||
final node = Node(type: 'example');
|
||||
final op = DeleteOperation([0], [node]);
|
||||
final json = op.toJson();
|
||||
expect(json, {
|
||||
'op': 'delete',
|
||||
'path': [0],
|
||||
'nodes': [
|
||||
{
|
||||
'type': 'example',
|
||||
}
|
||||
]
|
||||
});
|
||||
expect(DeleteOperation.fromJson(json), op);
|
||||
expect(op.invert().invert(), op);
|
||||
expect(op.copyWith(), op);
|
||||
});
|
||||
|
||||
test('test update text operation', () {
|
||||
final app = Delta()..insert('App');
|
||||
final appflowy = Delta()
|
||||
..retain(3)
|
||||
..insert('Flowy');
|
||||
final op = UpdateTextOperation([0], app, appflowy.invert(app));
|
||||
final json = op.toJson();
|
||||
expect(json, {
|
||||
'op': 'update_text',
|
||||
'path': [0],
|
||||
'delta': [
|
||||
{'insert': 'App'}
|
||||
],
|
||||
'inverted': [
|
||||
{'retain': 3},
|
||||
{'delete': 5}
|
||||
]
|
||||
});
|
||||
expect(UpdateTextOperation.fromJson(json), op);
|
||||
expect(op.invert().invert(), op);
|
||||
expect(op.copyWith(), op);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user