Skip to content

Commit

Permalink
chore: add tests and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Juliotati committed Aug 15, 2024
1 parent f446a99 commit 9020676
Show file tree
Hide file tree
Showing 9 changed files with 280 additions and 22 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@Juliotati
47 changes: 47 additions & 0 deletions .github/workflows/ci_tests_pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Tests Pipeline

on:
pull_request:
types:
- opened
- synchronize
branches:
- main

jobs:
test-station:
runs-on: macos-latest
steps:
- name: setup-repo
uses: actions/checkout@v4

- name: setup-jdk
uses: actions/setup-java@v2
with:
java-version: '12.x'
distribution: 'zulu'

- uses: subosito/flutter-action@v2
with:
cache: true
channel: 'stable'
cache-key: 'flutter-macos-stable-3.24.0-x64'
cache-path: '${{ runner.tool_cache }}/flutter-macos-stable-3.24.0-x64-hash'
pub-cache-key: 'flutter-pub-macos-stable-3.24.0-x64-hash'
pub-cache-path: '${{ runner.tool_cache }}/flutter/stable-3.24.0-x64'
flutter-version: '3.24.0'

- name: load-dependencies
run: dart pub get

- name: analyse-link_target
run: dart analyze --fatal-warnings

- name: analyse-example
run: dart analyze example --fatal-warnings

- name: validate-format
run: dart format --line-length 80 --set-exit-if-changed .

- name: run-tests
run: flutter test
7 changes: 7 additions & 0 deletions .run/RUN_WEB_AUTO.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RUN_WEB_AUTO" type="FlutterRunConfigurationType" factoryName="Flutter">
<option name="additionalArgs" value="-d chrome --web-renderer auto" />
<option name="filePath" value="$PROJECT_DIR$/example/lib/main.dart" />
<method v="2" />
</configuration>
</component>
25 changes: 5 additions & 20 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@ class LinkTargetExample extends StatelessWidget {

@override
Widget build(BuildContext context) {
final titleMedium = Theme.of(context).textTheme.titleMedium;
return MaterialApp(
title: 'Link Target Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: LinkTargetRegion(
child: Scaffold(
body: Center(
Expand All @@ -26,31 +23,19 @@ class LinkTargetExample extends StatelessWidget {
children: [
LinkTargetDetector(
target: 'https://github.com/flutter/flutter',
child: Text(
'Preview flutter repo',
style: Theme.of(context).textTheme.titleMedium,
),
child: Text('Preview flutter repo', style: titleMedium),
),
LinkTargetDetector(
target: 'https://flutter.dev/development',
child: Text(
'Preview flutter.dev',
style: Theme.of(context).textTheme.titleMedium,
),
child: Text('Preview flutter.dev', style: titleMedium),
),
LinkTargetDetector(
target: 'https://dart.dev/',
child: Text(
'Preview dart.dev',
style: Theme.of(context).textTheme.titleMedium,
),
child: Text('Preview dart.dev', style: titleMedium),
),
LinkTargetDetector(
target: 'https://www.youtube.com/@flutterdev',
child: Text(
'Preview Flutter YouTube',
style: Theme.of(context).textTheme.titleMedium,
),
child: Text('Preview Flutter YouTube', style: titleMedium),
),
],
),
Expand Down
9 changes: 9 additions & 0 deletions lib/src/detector.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:link_target/src/provider.dart';
import 'package:provider/provider.dart';
Expand Down Expand Up @@ -31,6 +34,12 @@ class _LinkTargetDetectorState extends State<LinkTargetDetector> {

@override
Widget build(BuildContext context) {
if (!kIsWeb) {
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
return widget.child;
}
}

return MouseRegion(
onHover: (_) {
if (_isHovered) return;
Expand Down
4 changes: 3 additions & 1 deletion lib/src/provider.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:developer';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

/// Manages and provides link target displayed by [LinkTargetRegion].
final class LinkTargetProvider extends ChangeNotifier {
Expand All @@ -15,6 +16,7 @@ final class LinkTargetProvider extends ChangeNotifier {
/// Updates the target "URL" shown on the page by [LinkTargetRegion]
void onHover(String value) {
_linkTarget = value.trim();
if (kDebugMode) log(name: 'LinkTargetProvider', 'onHover: $_linkTarget');
notifyListeners();
}

Expand Down
10 changes: 9 additions & 1 deletion lib/src/region.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:link_target/src/provider.dart';
Expand All @@ -19,7 +21,11 @@ final class LinkTargetRegion extends StatelessWidget {

@override
Widget build(BuildContext context) {
if (!kIsWeb) return child;
if (!kIsWeb) {
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
return child;
}
}

return ListenableProvider(
create: (_) => LinkTargetProvider(),
Expand Down Expand Up @@ -53,6 +59,8 @@ final class LinkTargetRegion extends StatelessWidget {
),
child: Text(
provider.linkTarget,
maxLines: 1,
softWrap: false,
style: const TextStyle(
fontSize: 12,
color: Color.fromRGBO(240, 240, 240, 1.0),
Expand Down
23 changes: 23 additions & 0 deletions test/src/detector_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'package:flutter/material.dart' show MouseRegion, Text, TextDirection;
import 'package:flutter_test/flutter_test.dart';
import 'package:link_target/src/detector.dart';

void main() {
testWidgets(
'LinkTargetDetector should be build correctly',
(tester) async {
await tester.pumpWidget(
const LinkTargetDetector(
target: 'https://example.com',
child: Text(
'Click here',
textDirection: TextDirection.ltr,
),
),
);

expect(find.text('Click here'), findsOneWidget);
expect(find.byType(MouseRegion), findsOneWidget);
},
);
}
176 changes: 176 additions & 0 deletions test/src/region_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart' show Card, MaterialApp, Scaffold, Text;
import 'package:flutter_test/flutter_test.dart';
import 'package:link_target/link_target.dart';

void main() {
group('LinkTargetRegion', () {
Future<void> hasPositionedAndAnimatedSwitchWidget() async {
final positioned = find
.ancestor(of: find.byType(Stack), matching: find.byType(Positioned))
.evaluate()
.first
.widget as Positioned;

expect(positioned.left, 0.0);
expect(positioned.bottom, 0.0);
expect(positioned.top, isNull);
expect(positioned.right, isNull);
expect(positioned.width, isNull);
expect(positioned.height, isNull);

final switcher = find.byType(AnimatedSwitcher).evaluate().first.widget
as AnimatedSwitcher;

expect(switcher.duration, const Duration(milliseconds: 200));
expect(switcher.reverseDuration, const Duration(milliseconds: 300));
}

Future<({Rect rect, TestGesture gesture})> hoverOnText(
WidgetTester tester,
String text,
) async {
final textRect = tester.getRect(find.text(text));
final gesture = await tester.startGesture(
textRect.topCenter,
kind: PointerDeviceKind.mouse,
);

await gesture.up();
await gesture.moveTo(textRect.topLeft);
await tester.pumpAndSettle(const Duration(milliseconds: 550));

return (rect: textRect, gesture: gesture);
}

testWidgets('should layout empty sizedBox', (tester) async {
await tester.pumpWidget(const LinkTargetExample());

await hasPositionedAndAnimatedSwitchWidget();

expect(find.byType(SizedBox), findsOneWidget);
expect(find.byType(Card), findsNothing);
});

testWidgets('should layout link target content', (tester) async {
await tester.pumpWidget(const LinkTargetExample());

await hasPositionedAndAnimatedSwitchWidget();
await hoverOnText(tester, 'Preview flutter repo');

final card = find.byType(Card).evaluate().first.widget as Card;

expect(find.byType(SizedBox), findsNothing);
expect(card.elevation, 0.0);
expect(card.margin, EdgeInsets.zero);
expect(card.color, const Color.fromRGBO(40, 40, 40, 1.0));
expect(
card.shape,
const RoundedRectangleBorder(
borderRadius: BorderRadius.only(topRight: Radius.circular(6.0)),
),
);
expect(
(card.child as Padding).padding,
const EdgeInsets.symmetric(vertical: 6.0, horizontal: 8.0),
);

final text = (card.child as Padding).child as Text;

expect(text.data, 'https://github.com/flutter/flutter');
expect(text.maxLines, 1);
expect(text.softWrap, isFalse);
expect(
text.style,
const TextStyle(
fontSize: 12,
color: Color.fromRGBO(240, 240, 240, 1.0),
),
);
});

testWidgets('should hover and exit single widget', (tester) async {
await tester.pumpWidget(const LinkTargetExample());

expect(find.text('https://github.com/flutter/flutter'), findsNothing);

// 1. Hovers on a single widget
final hover = await hoverOnText(tester, 'Preview flutter repo');

expect(find.text('https://github.com/flutter/flutter'), findsOneWidget);

// 2. Exits hover mode and moves away from all widgets
await hover.gesture.moveTo(const Offset(0.0, 0.0));
await tester.pumpAndSettle(const Duration(milliseconds: 300));

expect(find.text('https://github.com/flutter/flutter'), findsNothing);
});

testWidgets('should hover on multiple widgets at a time', (tester) async {
await tester.pumpWidget(const LinkTargetExample());
expect(find.text('https://github.com/flutter/flutter'), findsNothing);

// 1. Hovers on the first widget
final hover = await hoverOnText(tester, 'Preview flutter repo');

expect(find.text('https://github.com/flutter/flutter'), findsOneWidget);
expect(find.text('https://flutter.dev/development'), findsNothing);

// 2. Exits first widget and hovers on the second widget
await hover.gesture.moveTo(hover.rect.bottomLeft);
await tester.pumpAndSettle(const Duration(milliseconds: 300));

expect(find.text('https://github.com/flutter/flutter'), findsNothing);
expect(find.text('https://flutter.dev/development'), findsOneWidget);

// 3. Exits hover mode and moves away from all widgets
await hover.gesture.down(const Offset(0.0, 0.0));
await hover.gesture.cancel();
await tester.pumpAndSettle(const Duration(milliseconds: 300));

expect(find.text('https://github.com/flutter/flutter'), findsNothing);
expect(find.text('https://flutter.dev/development'), findsNothing);
});
});
}

class LinkTargetExample extends StatelessWidget {
const LinkTargetExample({super.key});

@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Link Target Example',
home: LinkTargetRegion(
child: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
LinkTargetDetector(
target: 'https://github.com/flutter/flutter',
child: Text('Preview flutter repo'),
),
LinkTargetDetector(
target: 'https://flutter.dev/development',
child: Text('Preview flutter.dev'),
),
LinkTargetDetector(
target: 'https://dart.dev/',
child: Text('Preview dart.dev'),
),
LinkTargetDetector(
target: 'https://www.youtube.com/@flutterdev',
child: Text('Preview Flutter YouTube'),
),
],
),
),
),
),
);
}
}

0 comments on commit 9020676

Please sign in to comment.