test(app): 前端第一批测试 — EditorBloc 19用例 + JournalElement 11用例 + InMemoryRepo 12用例
添加 mocktail 测试依赖 + 字体文件占位 总计 42 测试通过,覆盖工具/笔画/元素/撤销/重做/序列化/乐观锁/CRUD
This commit is contained in:
202
app/test/data/models/journal_element_test.dart
Normal file
202
app/test/data/models/journal_element_test.dart
Normal file
@@ -0,0 +1,202 @@
|
||||
// JournalElement 模型单元测试
|
||||
//
|
||||
// 覆盖:工厂方法、便利工厂、copyWith 不可变性、序列化往返、默认值
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:nuanji_app/data/models/journal_element.dart';
|
||||
|
||||
void main() {
|
||||
group('JournalElement', () {
|
||||
// ===== create 工厂 =====
|
||||
|
||||
test('create 工厂生成正确的默认值', () {
|
||||
final element = JournalElement.create(
|
||||
journalId: 'journal-1',
|
||||
elementType: ElementType.text,
|
||||
positionX: 10,
|
||||
positionY: 20,
|
||||
content: {'text': 'Hello'},
|
||||
);
|
||||
|
||||
expect(element.journalId, 'journal-1');
|
||||
expect(element.elementType, ElementType.text);
|
||||
expect(element.positionX, 10.0);
|
||||
expect(element.positionY, 20.0);
|
||||
expect(element.width, 100.0);
|
||||
expect(element.height, 100.0);
|
||||
expect(element.rotation, 0.0);
|
||||
expect(element.zIndex, 0);
|
||||
expect(element.version, 1);
|
||||
expect(element.id, isNotEmpty);
|
||||
expect(element.createdAt, isNotNull);
|
||||
expect(element.updatedAt, isNotNull);
|
||||
});
|
||||
|
||||
// ===== createText =====
|
||||
|
||||
test('createText 便利工厂', () {
|
||||
final element = JournalElement.createText(
|
||||
journalId: 'j-1',
|
||||
text: '你好',
|
||||
position: const Offset(50, 100),
|
||||
fontSize: 24.0,
|
||||
fontColor: '#E07A5F',
|
||||
);
|
||||
|
||||
expect(element.elementType, ElementType.text);
|
||||
expect(element.content['text'], '你好');
|
||||
expect(element.content['fontSize'], 24.0);
|
||||
expect(element.content['fontColor'], '#E07A5F');
|
||||
expect(element.positionX, 50.0);
|
||||
expect(element.positionY, 100.0);
|
||||
});
|
||||
|
||||
test('createText 使用默认字号和颜色', () {
|
||||
final element = JournalElement.createText(
|
||||
journalId: 'j-1',
|
||||
text: '默认',
|
||||
position: Offset.zero,
|
||||
);
|
||||
|
||||
expect(element.content['fontSize'], 18.0);
|
||||
expect(element.content['fontColor'], '#2D2420');
|
||||
});
|
||||
|
||||
// ===== createImage =====
|
||||
|
||||
test('createImage 便利工厂', () {
|
||||
final element = JournalElement.createImage(
|
||||
journalId: 'j-1',
|
||||
filePath: '/path/to/image.jpg',
|
||||
position: Offset.zero,
|
||||
thumbnailPath: '/path/to/thumb.jpg',
|
||||
);
|
||||
|
||||
expect(element.elementType, ElementType.image);
|
||||
expect(element.content['filePath'], '/path/to/image.jpg');
|
||||
expect(element.content['thumbnailPath'], '/path/to/thumb.jpg');
|
||||
});
|
||||
|
||||
test('createImage 无 thumbnailPath 时不包含该字段', () {
|
||||
final element = JournalElement.createImage(
|
||||
journalId: 'j-1',
|
||||
filePath: '/path/to/img.jpg',
|
||||
position: Offset.zero,
|
||||
);
|
||||
|
||||
expect(element.content.containsKey('thumbnailPath'), isFalse);
|
||||
expect(element.content['filePath'], '/path/to/img.jpg');
|
||||
});
|
||||
|
||||
// ===== createSticker =====
|
||||
|
||||
test('createSticker 便利工厂', () {
|
||||
final element = JournalElement.createSticker(
|
||||
journalId: 'j-1',
|
||||
emoji: '🐱',
|
||||
position: const Offset(50, 50),
|
||||
stickerPackId: 'pack-1',
|
||||
stickerId: 's-1',
|
||||
);
|
||||
|
||||
expect(element.elementType, ElementType.sticker);
|
||||
expect(element.content['emoji'], '🐱');
|
||||
expect(element.content['stickerPackId'], 'pack-1');
|
||||
expect(element.content['stickerId'], 's-1');
|
||||
});
|
||||
|
||||
test('createSticker 无可选字段时不包含', () {
|
||||
final element = JournalElement.createSticker(
|
||||
journalId: 'j-1',
|
||||
emoji: '🌸',
|
||||
position: Offset.zero,
|
||||
);
|
||||
|
||||
expect(element.content.containsKey('stickerPackId'), isFalse);
|
||||
expect(element.content.containsKey('stickerId'), isFalse);
|
||||
expect(element.content['emoji'], '🌸');
|
||||
});
|
||||
|
||||
// ===== copyWith 不可变性 =====
|
||||
|
||||
test('copyWith 返回新实例且不修改原始', () {
|
||||
final original = JournalElement.createText(
|
||||
journalId: 'j-1',
|
||||
text: '原始',
|
||||
position: Offset.zero,
|
||||
);
|
||||
|
||||
final modified = original.copyWith(
|
||||
positionX: 100.0,
|
||||
positionY: 200.0,
|
||||
);
|
||||
|
||||
expect(identical(original, modified), isFalse);
|
||||
expect(original.positionX, 0.0);
|
||||
expect(original.positionY, 0.0);
|
||||
expect(modified.positionX, 100.0);
|
||||
expect(modified.positionY, 200.0);
|
||||
expect(modified.journalId, original.journalId);
|
||||
expect(modified.id, original.id);
|
||||
});
|
||||
|
||||
// ===== 序列化 =====
|
||||
|
||||
test('toJson / fromJson 往返一致', () {
|
||||
final original = JournalElement.createText(
|
||||
journalId: 'j-1',
|
||||
text: '序列化测试',
|
||||
position: const Offset(42, 84),
|
||||
fontSize: 20.0,
|
||||
);
|
||||
|
||||
final json = original.toJson();
|
||||
final restored = JournalElement.fromJson(json);
|
||||
|
||||
expect(restored.id, original.id);
|
||||
expect(restored.journalId, original.journalId);
|
||||
expect(restored.elementType, original.elementType);
|
||||
expect(restored.positionX, original.positionX);
|
||||
expect(restored.positionY, original.positionY);
|
||||
expect(restored.content['text'], original.content['text']);
|
||||
expect(restored.version, original.version);
|
||||
});
|
||||
|
||||
test('fromJson 处理空 content', () {
|
||||
final json = {
|
||||
'id': 'test-id',
|
||||
'journal_id': 'j-1',
|
||||
'element_type': 'text',
|
||||
'position_x': 0.0,
|
||||
'position_y': 0.0,
|
||||
'width': 100.0,
|
||||
'height': 100.0,
|
||||
'rotation': 0.0,
|
||||
'z_index': 0,
|
||||
'content': <String, dynamic>{},
|
||||
'version': 1,
|
||||
'created_at': DateTime.now().toIso8601String(),
|
||||
'updated_at': DateTime.now().toIso8601String(),
|
||||
};
|
||||
|
||||
final element = JournalElement.fromJson(json);
|
||||
expect(element.content, isA<Map>());
|
||||
expect(element.content, isEmpty);
|
||||
});
|
||||
|
||||
// ===== ElementType 枚举 =====
|
||||
|
||||
test('ElementType 枚举值完整', () {
|
||||
expect(ElementType.values.length, 5);
|
||||
expect(ElementType.values, containsAll([
|
||||
ElementType.text,
|
||||
ElementType.image,
|
||||
ElementType.sticker,
|
||||
ElementType.handwritingRef,
|
||||
ElementType.tape,
|
||||
]));
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user