Merge pull request #1229 from enzoftware/main

test: Improve Code Coverage for extensions files
This commit is contained in:
Lucas.Xu 2022-10-08 10:50:36 +08:00 committed by GitHub
commit 06f1d52cc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 445 additions and 11 deletions

View File

@ -17,9 +17,9 @@ extension NodeAttributesExtensions on Attributes {
return containsKey(BuiltInAttributeKey.quote);
}
int? get number {
num? get number {
if (containsKey(BuiltInAttributeKey.number) &&
this[BuiltInAttributeKey.number] is int) {
this[BuiltInAttributeKey.number] is num) {
return this[BuiltInAttributeKey.number];
}
return null;
@ -27,7 +27,7 @@ extension NodeAttributesExtensions on Attributes {
bool get code {
if (containsKey(BuiltInAttributeKey.code) &&
this[BuiltInAttributeKey.code] == true) {
this[BuiltInAttributeKey.code] is bool) {
return this[BuiltInAttributeKey.code];
}
return false;
@ -63,11 +63,14 @@ extension DeltaAttributesExtensions on Attributes {
this[BuiltInAttributeKey.strikethrough] == true);
}
static const whiteInt = 0XFFFFFFFF;
Color? get color {
if (containsKey(BuiltInAttributeKey.color) &&
this[BuiltInAttributeKey.color] is String) {
return Color(
int.parse(this[BuiltInAttributeKey.color]),
// If the parse fails returns white by default
int.tryParse(this[BuiltInAttributeKey.color]) ?? whiteInt,
);
}
return null;
@ -77,8 +80,7 @@ extension DeltaAttributesExtensions on Attributes {
if (containsKey(BuiltInAttributeKey.backgroundColor) &&
this[BuiltInAttributeKey.backgroundColor] is String) {
return Color(
int.parse(this[BuiltInAttributeKey.backgroundColor]),
);
int.tryParse(this[BuiltInAttributeKey.backgroundColor]) ?? whiteInt);
}
return null;
}

View File

@ -6,7 +6,7 @@ import 'package:appflowy_editor/src/document/text_delta.dart';
import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart';
extension TextNodeExtension on TextNode {
dynamic getAttributeInSelection(Selection selection, String styleKey) {
T? getAttributeInSelection<T>(Selection selection, String styleKey) {
final ops = delta.whereType<TextInsert>();
final startOffset =
selection.isBackward ? selection.start.offset : selection.end.offset;
@ -19,8 +19,9 @@ extension TextNodeExtension on TextNode {
}
final length = op.length;
if (start < endOffset && start + length > startOffset) {
if (op.attributes?.containsKey(styleKey) == true) {
return op.attributes![styleKey];
final attributes = op.attributes;
if (attributes != null && attributes[styleKey] is T?) {
return attributes[styleKey];
}
}
start += length;

View File

@ -26,6 +26,7 @@ import 'messages_hu-HU.dart' as messages_hu_hu;
import 'messages_id-ID.dart' as messages_id_id;
import 'messages_it-IT.dart' as messages_it_it;
import 'messages_ja-JP.dart' as messages_ja_jp;
import 'messages_ml_IN.dart' as messages_ml_in;
import 'messages_nl-NL.dart' as messages_nl_nl;
import 'messages_pl-PL.dart' as messages_pl_pl;
import 'messages_pt-BR.dart' as messages_pt_br;
@ -48,6 +49,7 @@ Map<String, LibraryLoader> _deferredLibraries = {
'id_ID': () => new Future.value(null),
'it_IT': () => new Future.value(null),
'ja_JP': () => new Future.value(null),
'ml_IN': () => new Future.value(null),
'nl_NL': () => new Future.value(null),
'pl_PL': () => new Future.value(null),
'pt_BR': () => new Future.value(null),
@ -82,6 +84,8 @@ MessageLookupByLibrary? _findExact(String localeName) {
return messages_it_it.messages;
case 'ja_JP':
return messages_ja_jp.messages;
case 'ml_IN':
return messages_ml_in.messages;
case 'nl_NL':
return messages_nl_nl.messages;
case 'pl_PL':

View File

@ -0,0 +1,45 @@
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
// This is a library that provides messages for a ml_IN locale. All the
// messages from the main program should be duplicated here with the same
// function name.
// Ignore issues from commonly used lints in this file.
// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
final messages = new MessageLookup();
typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'ml_IN';
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
"bold": MessageLookupByLibrary.simpleMessage("ബോൾഡ്"),
"bulletedList":
MessageLookupByLibrary.simpleMessage("ബുള്ളറ്റഡ് പട്ടിക"),
"checkbox": MessageLookupByLibrary.simpleMessage("ചെക്ക്ബോക്സ്"),
"embedCode": MessageLookupByLibrary.simpleMessage("എംബെഡഡ് കോഡ്"),
"heading1": MessageLookupByLibrary.simpleMessage("തലക്കെട്ട് 1"),
"heading2": MessageLookupByLibrary.simpleMessage("തലക്കെട്ട് 2"),
"heading3": MessageLookupByLibrary.simpleMessage("തലക്കെട്ട് 3"),
"highlight":
MessageLookupByLibrary.simpleMessage("പ്രമുഖമാക്കിക്കാട്ടുക"),
"image": MessageLookupByLibrary.simpleMessage("ചിത്രം"),
"italic": MessageLookupByLibrary.simpleMessage("ഇറ്റാലിക്"),
"link": MessageLookupByLibrary.simpleMessage("ലിങ്ക്"),
"numberedList":
MessageLookupByLibrary.simpleMessage("അക്കമിട്ട പട്ടിക"),
"quote": MessageLookupByLibrary.simpleMessage("ഉദ്ധരണി"),
"strikethrough": MessageLookupByLibrary.simpleMessage("സ്ട്രൈക്ക്ത്രൂ"),
"text": MessageLookupByLibrary.simpleMessage("വചനം"),
"underline": MessageLookupByLibrary.simpleMessage("അടിവരയിടുക")
};
}

View File

@ -229,6 +229,7 @@ class AppLocalizationDelegate
Locale.fromSubtags(languageCode: 'id', countryCode: 'ID'),
Locale.fromSubtags(languageCode: 'it', countryCode: 'IT'),
Locale.fromSubtags(languageCode: 'ja', countryCode: 'JP'),
Locale.fromSubtags(languageCode: 'ml', countryCode: 'IN'),
Locale.fromSubtags(languageCode: 'nl', countryCode: 'NL'),
Locale.fromSubtags(languageCode: 'pl', countryCode: 'PL'),
Locale.fromSubtags(languageCode: 'pt', countryCode: 'BR'),

View File

@ -333,8 +333,10 @@ void showLinkMenu(
final textNode = node.first as TextNode;
String? linkText;
if (textNode.allSatisfyLinkInSelection(selection)) {
linkText =
textNode.getAttributeInSelection(selection, BuiltInAttributeKey.href);
linkText = textNode.getAttributeInSelection<String>(
selection,
BuiltInAttributeKey.href,
);
}
_linkMenuOverlay = OverlayEntry(builder: (context) {
return Positioned(

View File

@ -33,6 +33,7 @@ dev_dependencies:
sdk: flutter
flutter_lints: ^2.0.1
network_image_mock: ^2.1.1
mockito: ^5.3.2
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View File

@ -0,0 +1,201 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('NodeAttributesExtensions::', () {
test('heading', () {
final Attributes attribute = {
'subtype': 'heading',
'heading': 'AppFlowy',
};
expect(attribute.heading, 'AppFlowy');
});
test('heading - text is not String return null', () {
final Attributes attribute = {
'subtype': 'heading',
'heading': 123,
};
expect(attribute.heading, null);
});
test('heading - subtype is not "heading" return null', () {
final Attributes attribute = {
'subtype': 'code',
'heading': 'Hello World!',
};
expect(attribute.heading, null);
});
test('quote', () {
final Attributes attribute = {
'quote': 'quote text',
};
expect(attribute.quote, true);
});
test('number - int', () {
final Attributes attribute = {
'number': 99,
};
expect(attribute.number, 99);
});
test('number - double', () {
final Attributes attribute = {
'number': 12.34,
};
expect(attribute.number, 12.34);
});
test('number - return null', () {
final Attributes attribute = {
'code': 12.34,
};
expect(attribute.number, null);
});
test('code', () {
final Attributes attribute = {
'code': true,
};
expect(attribute.code, true);
});
test('code - return false', () {
final Attributes attribute = {
'quote': true,
};
expect(attribute.code, false);
});
test('check', () {
final Attributes attribute = {
'checkbox': true,
};
expect(attribute.check, true);
});
test('check - return false', () {
final Attributes attribute = {
'quote': true,
};
expect(attribute.check, false);
});
});
group('DeltaAttributesExtensions::', () {
test('bold', () {
final Attributes attribute = {
'bold': true,
};
expect(attribute.bold, true);
});
test('bold - return false', () {
final Attributes attribute = {
'bold': 123,
};
expect(attribute.bold, false);
});
test('italic', () {
final Attributes attribute = {
'italic': true,
};
expect(attribute.italic, true);
});
test('italic - return false', () {
final Attributes attribute = {
'italic': 123,
};
expect(attribute.italic, false);
});
test('underline', () {
final Attributes attribute = {
'underline': true,
};
expect(attribute.underline, true);
});
test('underline - return false', () {
final Attributes attribute = {
'underline': 123,
};
expect(attribute.underline, false);
});
test('strikethrough', () {
final Attributes attribute = {
'strikethrough': true,
};
expect(attribute.strikethrough, true);
});
test('strikethrough - return false', () {
final Attributes attribute = {
'strikethrough': 123,
};
expect(attribute.strikethrough, false);
});
test('color', () {
final Attributes attribute = {
'color': '0xff212fff',
};
expect(attribute.color, const Color(0XFF212FFF));
});
test('color - return null', () {
final Attributes attribute = {
'color': 123,
};
expect(attribute.color, null);
});
test('color - parse failure return white', () {
final Attributes attribute = {
'color': 'hello123',
};
expect(attribute.color, const Color(0XFFFFFFFF));
});
test('backgroundColor', () {
final Attributes attribute = {
'backgroundColor': '0xff678fff',
};
expect(attribute.backgroundColor, const Color(0XFF678FFF));
});
test('backgroundColor - return null', () {
final Attributes attribute = {
'backgroundColor': 123,
};
expect(attribute.backgroundColor, null);
});
test('backgroundColor - parse failure return white', () {
final Attributes attribute = {
'backgroundColor': 'hello123',
};
expect(attribute.backgroundColor, const Color(0XFFFFFFFF));
});
test('href', () {
final Attributes attribute = {
'href': '/app/flowy',
};
expect(attribute.href, '/app/flowy');
});
test('href - return null', () {
final Attributes attribute = {
'href': 123,
};
expect(attribute.href, null);
});
});
}

View File

@ -0,0 +1,40 @@
import 'package:appflowy_editor/src/extensions/color_extension.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('ColorExtension::', () {
const white = Color(0XFFFFFFFF);
const black = Color(0XFF000000);
const blue = Color(0XFF000FFF);
const blueRgba = 'rgba(0, 15, 255, 255)';
test('ToRgbaString', () {
expect(blue.toRgbaString(), 'rgba(0, 15, 255, 255)');
expect(white.toRgbaString(), 'rgba(255, 255, 255, 255)');
expect(black.toRgbaString(), 'rgba(0, 0, 0, 255)');
});
test('tryFromRgbaString', () {
final color = ColorExtension.tryFromRgbaString(blueRgba);
expect(color, const Color.fromARGB(255, 0, 15, 255));
});
test('tryFromRgbaString - wrong rgba format return null', () {
const wrongRgba = 'abc(1,2,3,4)';
final color = ColorExtension.tryFromRgbaString(wrongRgba);
expect(color, null);
});
test('tryFromRgbaString - wrong length return null', () {
const wrongRgba = 'rgba(0, 15, 255)';
final color = ColorExtension.tryFromRgbaString(wrongRgba);
expect(color, null);
});
test('tryFromRgbaString - wrong values return null', () {
const wrongRgba = 'rgba(-12, 999, 1234, 619)';
final color = ColorExtension.tryFromRgbaString(wrongRgba);
expect(color, null);
});
});
}

View File

@ -0,0 +1,57 @@
import 'dart:collection';
import 'dart:ui';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:appflowy_editor/src/extensions/node_extensions.dart';
class MockNode extends Mock implements Node {}
void main() {
final mockNode = MockNode();
group('NodeExtensions::', () {
final selection = Selection(
start: Position(path: [0]),
end: Position(path: [1]),
);
test('rect - renderBox is null', () {
when(mockNode.renderBox).thenReturn(null);
final result = mockNode.rect;
expect(result, Rect.zero);
});
test('inSelection', () {
// I use an empty implementation instead of mock, because the mocked
// version throws error trying to access the path.
final subLinkedList = LinkedList<Node>()
..addAll([
Node(type: 'type', children: LinkedList(), attributes: {}),
Node(type: 'type', children: LinkedList(), attributes: {}),
Node(type: 'type', children: LinkedList(), attributes: {}),
Node(type: 'type', children: LinkedList(), attributes: {}),
Node(type: 'type', children: LinkedList(), attributes: {}),
]);
final linkedList = LinkedList<Node>()
..addAll([
Node(
type: 'type',
children: subLinkedList,
attributes: {},
),
]);
final node = Node(
type: 'type',
children: linkedList,
attributes: {},
);
final result = node.inSelection(selection);
expect(result, false);
});
});
}

View File

@ -0,0 +1,20 @@
import 'dart:io';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:appflowy_editor/src/extensions/object_extensions.dart';
void main() {
group('FlowyObjectExtensions::', () {
test('unwrapOrNull', () {
final result = const TextSpan().unwrapOrNull<HitTestTarget>();
assert(result is TextSpan);
});
test('unwrapOrNull - return null', () {
final result = const TextSpan().unwrapOrNull<ServerSocket>();
expect(result, null);
});
});
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
void main() {
group('TextNodeExtension::', () {
test('description', () {});
});
}

View File

@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:appflowy_editor/src/extensions/text_style_extension.dart';
void main() {
group('TextStyleExtensions::', () {
const style = TextStyle(
color: Colors.blue,
backgroundColor: Colors.white,
fontSize: 14,
height: 100,
wordSpacing: 2,
fontWeight: FontWeight.w700,
);
const otherStyle = TextStyle(
color: Colors.red,
backgroundColor: Colors.black,
fontSize: 12,
height: 10,
wordSpacing: 1,
);
test('combine', () {
final result = style.combine(otherStyle);
expect(result.color, Colors.red);
expect(result.backgroundColor, Colors.black);
expect(result.fontSize, 12);
expect(result.height, 10);
expect(result.wordSpacing, 1);
});
test('combine - return this', () {
final result = style.combine(null);
expect(result, style);
});
test('combine - return null with inherit', () {
final styleCopy = otherStyle.copyWith(inherit: false);
final result = style.combine(styleCopy);
expect(result, styleCopy);
});
});
}

View File

@ -0,0 +1,10 @@
import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('safeLaunchUrl without scheme', () async {
const href = null;
final result = await safeLaunchUrl(href);
expect(result, false);
});
}