// Copyright 2014 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 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; class TestCanvas implements Canvas { final List invocations = []; @override void noSuchMethod(Invocation invocation) { invocations.add(invocation); } } void main() { // the textDirection values below are intentionally sometimes different and // sometimes the same as the layoutDirection, to make sure that they don't // affect the layout. test('A Banner with a location of topStart paints in the top left (LTR)', () { final bannerPainter = BannerPainter( message: 'foo', textDirection: TextDirection.rtl, location: BannerLocation.topStart, layoutDirection: TextDirection.ltr, ); final canvas = TestCanvas(); bannerPainter.paint(canvas, const Size(1000.0, 1000.0)); final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #translate; }); expect(translateCommand, isNotNull); expect(translateCommand.positionalArguments[0], lessThan(100.0)); expect(translateCommand.positionalArguments[1], lessThan(100.0)); final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #rotate; }); expect(rotateCommand, isNotNull); expect(rotateCommand.positionalArguments[0], equals(-math.pi / 4.0)); }); test('A Banner with a location of topStart paints in the top right (RTL)', () { final bannerPainter = BannerPainter( message: 'foo', textDirection: TextDirection.ltr, location: BannerLocation.topStart, layoutDirection: TextDirection.rtl, ); final canvas = TestCanvas(); bannerPainter.paint(canvas, const Size(1000.0, 1000.0)); final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #translate; }); expect(translateCommand, isNotNull); expect(translateCommand.positionalArguments[0], greaterThan(900.0)); expect(translateCommand.positionalArguments[1], lessThan(100.0)); final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #rotate; }); expect(rotateCommand, isNotNull); expect(rotateCommand.positionalArguments[0], equals(math.pi / 4.0)); }); test('A Banner with a location of topEnd paints in the top right (LTR)', () { final bannerPainter = BannerPainter( message: 'foo', textDirection: TextDirection.ltr, location: BannerLocation.topEnd, layoutDirection: TextDirection.ltr, ); final canvas = TestCanvas(); bannerPainter.paint(canvas, const Size(1000.0, 1000.0)); final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #translate; }); expect(translateCommand, isNotNull); expect(translateCommand.positionalArguments[0], greaterThan(900.0)); expect(translateCommand.positionalArguments[1], lessThan(100.0)); final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #rotate; }); expect(rotateCommand, isNotNull); expect(rotateCommand.positionalArguments[0], equals(math.pi / 4.0)); }); test('A Banner with a location of topEnd paints in the top left (RTL)', () { final bannerPainter = BannerPainter( message: 'foo', textDirection: TextDirection.rtl, location: BannerLocation.topEnd, layoutDirection: TextDirection.rtl, ); final canvas = TestCanvas(); bannerPainter.paint(canvas, const Size(1000.0, 1000.0)); final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #translate; }); expect(translateCommand, isNotNull); expect(translateCommand.positionalArguments[0], lessThan(100.0)); expect(translateCommand.positionalArguments[1], lessThan(100.0)); final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #rotate; }); expect(rotateCommand, isNotNull); expect(rotateCommand.positionalArguments[0], equals(-math.pi / 4.0)); }); test('A Banner with a location of bottomStart paints in the bottom left (LTR)', () { final bannerPainter = BannerPainter( message: 'foo', textDirection: TextDirection.ltr, location: BannerLocation.bottomStart, layoutDirection: TextDirection.ltr, ); final canvas = TestCanvas(); bannerPainter.paint(canvas, const Size(1000.0, 1000.0)); final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #translate; }); expect(translateCommand, isNotNull); expect(translateCommand.positionalArguments[0], lessThan(100.0)); expect(translateCommand.positionalArguments[1], greaterThan(900.0)); final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #rotate; }); expect(rotateCommand, isNotNull); expect(rotateCommand.positionalArguments[0], equals(math.pi / 4.0)); }); test('A Banner with a location of bottomStart paints in the bottom right (RTL)', () { final bannerPainter = BannerPainter( message: 'foo', textDirection: TextDirection.rtl, location: BannerLocation.bottomStart, layoutDirection: TextDirection.rtl, ); final canvas = TestCanvas(); bannerPainter.paint(canvas, const Size(1000.0, 1000.0)); final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #translate; }); expect(translateCommand, isNotNull); expect(translateCommand.positionalArguments[0], greaterThan(900.0)); expect(translateCommand.positionalArguments[1], greaterThan(900.0)); final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #rotate; }); expect(rotateCommand, isNotNull); expect(rotateCommand.positionalArguments[0], equals(-math.pi / 4.0)); }); test('A Banner with a location of bottomEnd paints in the bottom right (LTR)', () { final bannerPainter = BannerPainter( message: 'foo', textDirection: TextDirection.rtl, location: BannerLocation.bottomEnd, layoutDirection: TextDirection.ltr, ); final canvas = TestCanvas(); bannerPainter.paint(canvas, const Size(1000.0, 1000.0)); final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #translate; }); expect(translateCommand, isNotNull); expect(translateCommand.positionalArguments[0], greaterThan(900.0)); expect(translateCommand.positionalArguments[1], greaterThan(900.0)); final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #rotate; }); expect(rotateCommand, isNotNull); expect(rotateCommand.positionalArguments[0], equals(-math.pi / 4.0)); }); test('A Banner with a location of bottomEnd paints in the bottom left (RTL)', () { final bannerPainter = BannerPainter( message: 'foo', textDirection: TextDirection.ltr, location: BannerLocation.bottomEnd, layoutDirection: TextDirection.rtl, ); final canvas = TestCanvas(); bannerPainter.paint(canvas, const Size(1000.0, 1000.0)); final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #translate; }); expect(translateCommand, isNotNull); expect(translateCommand.positionalArguments[0], lessThan(100.0)); expect(translateCommand.positionalArguments[1], greaterThan(900.0)); final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) { return invocation.memberName == #rotate; }); expect(rotateCommand, isNotNull); expect(rotateCommand.positionalArguments[0], equals(math.pi / 4.0)); }); testWidgets('Banner widget', (WidgetTester tester) async { debugDisableShadows = false; await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, child: Banner(message: 'Hello', location: BannerLocation.topEnd), ), ); expect( find.byType(CustomPaint), paints ..save() ..translate(x: 800.0, y: 0.0) ..rotate(angle: math.pi / 4.0) ..rect( rect: const Rect.fromLTRB(-40.0, 28.0, 40.0, 40.0), color: const Color(0x7f000000), hasMaskFilter: true, ) ..rect( rect: const Rect.fromLTRB(-40.0, 28.0, 40.0, 40.0), color: const Color(0xa0b71c1c), hasMaskFilter: false, ) ..paragraph(offset: const Offset(-40.0, 29.0)) ..restore(), ); debugDisableShadows = true; }); testWidgets('Banner widget in MaterialApp', (WidgetTester tester) async { debugDisableShadows = false; await tester.pumpWidget(const MaterialApp(home: Placeholder())); expect( find.byType(CheckedModeBanner), paints ..save() ..translate(x: 800.0, y: 0.0) ..rotate(angle: math.pi / 4.0) ..rect( rect: const Rect.fromLTRB(-40.0, 28.0, 40.0, 40.0), color: const Color(0x7f000000), hasMaskFilter: true, ) ..rect( rect: const Rect.fromLTRB(-40.0, 28.0, 40.0, 40.0), color: const Color(0xa0b71c1c), hasMaskFilter: false, ) ..paragraph(offset: const Offset(-40.0, 29.0)) ..restore(), ); debugDisableShadows = true; }); test('BannerPainter dispatches memory events', () async { await expectLater( await memoryEvents( () => BannerPainter( message: 'foo', textDirection: TextDirection.rtl, location: BannerLocation.topStart, layoutDirection: TextDirection.ltr, ).dispose(), BannerPainter, ), areCreateAndDispose, ); }); testWidgets('Can configure shadow for Banner widget', (WidgetTester tester) async { debugDisableShadows = false; await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, child: Banner( message: 'Shadow banner', location: BannerLocation.topEnd, shadow: BoxShadow(color: Color(0xFF008000), blurRadius: 8.0), ), ), ); final Finder customPaint = find.byType(CustomPaint); expect(customPaint, findsOneWidget); final CustomPaint paintWidget = tester.widget(customPaint); final painter = paintWidget.foregroundPainter! as BannerPainter; expect(painter.shadow.color, const Color(0xFF008000)); expect(painter.shadow.blurRadius, 8.0); debugDisableShadows = true; }); testWidgets('CheckedModeBanner does not crash at zero area', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, child: Center( child: SizedBox.shrink(child: CheckedModeBanner(child: Text('X'))), ), ), ); expect(tester.getSize(find.byType(CheckedModeBanner)), Size.zero); }); }