400 lines
12 KiB
Dart
400 lines
12 KiB
Dart
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'utils.dart';
|
|
|
|
void main() => defineTests();
|
|
|
|
void defineTests() {
|
|
group('Data', () {
|
|
testWidgets(
|
|
'simple data',
|
|
(WidgetTester tester) async {
|
|
// extract to variable; if run with --track-widget-creation using const
|
|
// widgets aren't necessarily identical if created on different lines.
|
|
const Markdown markdown = Markdown(data: 'Data1');
|
|
|
|
await tester.pumpWidget(boilerplate(markdown));
|
|
expectTextStrings(tester.allWidgets, <String>['Data1']);
|
|
|
|
final String stateBefore = dumpRenderView();
|
|
await tester.pumpWidget(boilerplate(markdown));
|
|
final String stateAfter = dumpRenderView();
|
|
expect(stateBefore, equals(stateAfter));
|
|
|
|
await tester.pumpWidget(boilerplate(const Markdown(data: 'Data2')));
|
|
expectTextStrings(tester.allWidgets, <String>['Data2']);
|
|
},
|
|
);
|
|
});
|
|
|
|
group('Text', () {
|
|
testWidgets(
|
|
'Empty string',
|
|
(WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
const MarkdownBody(data: ''),
|
|
),
|
|
);
|
|
|
|
final Iterable<Widget> widgets = selfAndDescendantWidgetsOf(
|
|
find.byType(MarkdownBody),
|
|
tester,
|
|
);
|
|
expectWidgetTypes(widgets, <Type>[
|
|
MarkdownBody,
|
|
Column,
|
|
]);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'Simple string',
|
|
(WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
const MarkdownBody(data: 'Hello'),
|
|
),
|
|
);
|
|
|
|
final Iterable<Widget> widgets = selfAndDescendantWidgetsOf(
|
|
find.byType(MarkdownBody),
|
|
tester,
|
|
);
|
|
expectWidgetTypes(widgets, <Type>[
|
|
MarkdownBody,
|
|
Column,
|
|
Wrap,
|
|
Text,
|
|
RichText,
|
|
]);
|
|
expectTextStrings(widgets, <String>['Hello']);
|
|
},
|
|
);
|
|
});
|
|
|
|
group('Leading spaces', () {
|
|
testWidgets(
|
|
// Example 192 from the GitHub Flavored Markdown specification.
|
|
'leading space are ignored', (WidgetTester tester) async {
|
|
const String data = ' aaa\n bbb';
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
const MarkdownBody(data: data),
|
|
),
|
|
);
|
|
|
|
final Iterable<Widget> widgets = selfAndDescendantWidgetsOf(
|
|
find.byType(MarkdownBody),
|
|
tester,
|
|
);
|
|
expectWidgetTypes(widgets, <Type>[
|
|
MarkdownBody,
|
|
Column,
|
|
Wrap,
|
|
Text,
|
|
RichText,
|
|
]);
|
|
expectTextStrings(widgets, <String>['aaa bbb']);
|
|
});
|
|
});
|
|
|
|
group('Line Break', () {
|
|
testWidgets(
|
|
// Example 654 from the GitHub Flavored Markdown specification.
|
|
'two spaces at end of line inside a block element',
|
|
(WidgetTester tester) async {
|
|
const String data = 'line 1 \nline 2';
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
const MarkdownBody(data: data),
|
|
),
|
|
);
|
|
|
|
final Iterable<Widget> widgets = selfAndDescendantWidgetsOf(
|
|
find.byType(MarkdownBody),
|
|
tester,
|
|
);
|
|
expectWidgetTypes(widgets, <Type>[MarkdownBody, Column, Wrap, Text, RichText]);
|
|
expectTextStrings(widgets, <String>['line 1\nline 2']);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
// Example 655 from the GitHub Flavored Markdown specification.
|
|
'backslash at end of line inside a block element',
|
|
(WidgetTester tester) async {
|
|
const String data = 'line 1\\\nline 2';
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
const MarkdownBody(data: data),
|
|
),
|
|
);
|
|
|
|
final Iterable<Widget> widgets = selfAndDescendantWidgetsOf(
|
|
find.byType(MarkdownBody),
|
|
tester,
|
|
);
|
|
expectWidgetTypes(widgets, <Type>[MarkdownBody, Column, Wrap, Text, RichText]);
|
|
expectTextStrings(widgets, <String>['line 1\nline 2']);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'non-applicable line break',
|
|
(WidgetTester tester) async {
|
|
const String data = 'line 1.\nline 2.';
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
const MarkdownBody(data: data),
|
|
),
|
|
);
|
|
|
|
final Iterable<Widget> widgets = selfAndDescendantWidgetsOf(
|
|
find.byType(MarkdownBody),
|
|
tester,
|
|
);
|
|
expectWidgetTypes(widgets, <Type>[
|
|
MarkdownBody,
|
|
Column,
|
|
Wrap,
|
|
Text,
|
|
RichText,
|
|
]);
|
|
expectTextStrings(widgets, <String>['line 1. line 2.']);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'non-applicable line break',
|
|
(WidgetTester tester) async {
|
|
const String data = 'line 1.\nline 2.';
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
const MarkdownBody(data: data),
|
|
),
|
|
);
|
|
|
|
final Iterable<Widget> widgets = selfAndDescendantWidgetsOf(
|
|
find.byType(MarkdownBody),
|
|
tester,
|
|
);
|
|
expectWidgetTypes(widgets, <Type>[
|
|
MarkdownBody,
|
|
Column,
|
|
Wrap,
|
|
Text,
|
|
RichText,
|
|
]);
|
|
expectTextStrings(widgets, <String>['line 1. line 2.']);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'soft line break',
|
|
(WidgetTester tester) async {
|
|
const String data = 'line 1.\nline 2.';
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
const MarkdownBody(
|
|
data: data,
|
|
softLineBreak: true,
|
|
),
|
|
),
|
|
);
|
|
|
|
final Iterable<Widget> widgets = selfAndDescendantWidgetsOf(
|
|
find.byType(MarkdownBody),
|
|
tester,
|
|
);
|
|
expectWidgetTypes(widgets, <Type>[MarkdownBody, Column, Wrap, Text, RichText]);
|
|
expectTextStrings(widgets, <String>['line 1.\nline 2.']);
|
|
},
|
|
);
|
|
});
|
|
|
|
group('Selectable', () {
|
|
testWidgets(
|
|
'header with line of text',
|
|
(WidgetTester tester) async {
|
|
const String data = '# Title\nHello _World_!';
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
const MediaQuery(
|
|
data: MediaQueryData(),
|
|
child: Markdown(
|
|
data: data,
|
|
selectable: true,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.byType(SelectableText), findsNWidgets(2));
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'header with line of text and onTap callback',
|
|
(WidgetTester tester) async {
|
|
const String data = '# Title\nHello _World_!';
|
|
String? textTapResults;
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
MediaQuery(
|
|
data: const MediaQueryData(),
|
|
child: Markdown(
|
|
data: data,
|
|
selectable: true,
|
|
onTapText: () => textTapResults = 'Text has been tapped.',
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Iterable<Widget> selectableWidgets = tester.widgetList(find.byType(SelectableText));
|
|
expect(selectableWidgets.length, 2);
|
|
|
|
final SelectableText selectableTitle = selectableWidgets.first as SelectableText;
|
|
expect(selectableTitle, isNotNull);
|
|
expect(selectableTitle.onTap, isNotNull);
|
|
selectableTitle.onTap!();
|
|
expect(textTapResults == 'Text has been tapped.', true);
|
|
|
|
textTapResults = null;
|
|
final SelectableText selectableText = selectableWidgets.last as SelectableText;
|
|
expect(selectableText, isNotNull);
|
|
expect(selectableText.onTap, isNotNull);
|
|
selectableText.onTap!();
|
|
expect(textTapResults == 'Text has been tapped.', true);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'Selectable without onSelectionChanged',
|
|
(WidgetTester tester) async {
|
|
const String data = '# abc def ghi\njkl opq';
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Material(
|
|
child: MarkdownBody(
|
|
data: data,
|
|
selectable: true,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Find the positions before character 'd' and 'f'.
|
|
final Offset dPos = positionInRenderedText(tester, 'abc def ghi', 4);
|
|
final Offset fPos = positionInRenderedText(tester, 'abc def ghi', 6);
|
|
// Select from 'd' until 'f'.
|
|
final TestGesture firstGesture = await tester.startGesture(dPos, kind: PointerDeviceKind.mouse);
|
|
addTearDown(firstGesture.removePointer);
|
|
await tester.pump();
|
|
await firstGesture.moveTo(fPos);
|
|
await firstGesture.up();
|
|
await tester.pump();
|
|
|
|
// Find the positions before character 'j' and 'o'.
|
|
final Offset jPos = positionInRenderedText(tester, 'jkl opq', 0);
|
|
final Offset oPos = positionInRenderedText(tester, 'jkl opq', 4);
|
|
// Select from 'j' until 'o'.
|
|
final TestGesture secondGesture = await tester.startGesture(jPos, kind: PointerDeviceKind.mouse);
|
|
addTearDown(secondGesture.removePointer);
|
|
await tester.pump();
|
|
await secondGesture.moveTo(oPos);
|
|
await secondGesture.up();
|
|
await tester.pump();
|
|
|
|
expect(tester.takeException(), isNull);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'header with line of text and onSelectionChanged callback',
|
|
(WidgetTester tester) async {
|
|
const String data = '# abc def ghi\njkl opq';
|
|
String? selectableText;
|
|
String? selectedText;
|
|
void onSelectionChanged(String? text, TextSelection selection, SelectionChangedCause? cause) {
|
|
selectableText = text;
|
|
selectedText = text != null ? selection.textInside(text) : null;
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Material(
|
|
child: MarkdownBody(
|
|
data: data,
|
|
selectable: true,
|
|
onSelectionChanged: onSelectionChanged,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Find the positions before character 'd' and 'f'.
|
|
final Offset dPos = positionInRenderedText(tester, 'abc def ghi', 4);
|
|
final Offset fPos = positionInRenderedText(tester, 'abc def ghi', 6);
|
|
// Select from 'd' until 'f'.
|
|
final TestGesture firstGesture = await tester.startGesture(dPos, kind: PointerDeviceKind.mouse);
|
|
addTearDown(firstGesture.removePointer);
|
|
await tester.pump();
|
|
await firstGesture.moveTo(fPos);
|
|
await firstGesture.up();
|
|
await tester.pump();
|
|
|
|
expect(selectableText, 'abc def ghi');
|
|
expect(selectedText, 'de');
|
|
|
|
// Find the positions before character 'j' and 'o'.
|
|
final Offset jPos = positionInRenderedText(tester, 'jkl opq', 0);
|
|
final Offset oPos = positionInRenderedText(tester, 'jkl opq', 4);
|
|
// Select from 'j' until 'o'.
|
|
final TestGesture secondGesture = await tester.startGesture(jPos, kind: PointerDeviceKind.mouse);
|
|
addTearDown(secondGesture.removePointer);
|
|
await tester.pump();
|
|
await secondGesture.moveTo(oPos);
|
|
await secondGesture.up();
|
|
await tester.pump();
|
|
|
|
expect(selectableText, 'jkl opq');
|
|
expect(selectedText, 'jkl ');
|
|
},
|
|
);
|
|
});
|
|
|
|
group('Strikethrough', () {
|
|
testWidgets('single word', (WidgetTester tester) async {
|
|
const String data = '~~strikethrough~~';
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
const MarkdownBody(data: data),
|
|
),
|
|
);
|
|
|
|
final Iterable<Widget> widgets = selfAndDescendantWidgetsOf(
|
|
find.byType(MarkdownBody),
|
|
tester,
|
|
);
|
|
expectWidgetTypes(widgets, <Type>[
|
|
MarkdownBody,
|
|
Column,
|
|
Wrap,
|
|
Text,
|
|
RichText,
|
|
]);
|
|
expectTextStrings(widgets, <String>['strikethrough']);
|
|
});
|
|
});
|
|
}
|