203 lines
6.0 KiB
Dart
203 lines
6.0 KiB
Dart
// 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,
|
||
]));
|
||
});
|
||
});
|
||
}
|