passsy / spot

Chainable powerful Flutter widget selector API, screenshots and assertions for awesome widget tests.
https://pub.dev/packages/spot
Apache License 2.0
62 stars 1 forks source link

act.tap(spotText('textSpan')) should click the exact TextSpan #46

Open passsy opened 7 months ago

passsy commented 7 months ago

Currently, act.tap() taps the center of the RenderObject. But RenderParagraph can give the exact location of the text and there should be clicked, allowing TextSpans with gesture recognizers.

      Offset locationOfTextSpan(String text) {
        assert(text.length > 1);
        final paragraph =
            spotText(text).snapshotRenderObject() as RenderParagraph;
        final fullText = paragraph.text.toPlainText();
        final start = fullText.indexOf(text);
        final end = start + text.length;
        final barLocationStart =
            paragraph.getOffsetForCaret(TextPosition(offset: start), Rect.zero);
        final barLocationEnd =
            paragraph.getOffsetForCaret(TextPosition(offset: end), Rect.zero);
        if (barLocationStart.dy == barLocationEnd.dy) {
          // on same line, click the middle
          return Offset(
            (barLocationStart.dx + barLocationEnd.dx) / 2,
            (barLocationStart.dy + barLocationEnd.dy) / 2,
          );
        } else {
          // click one pixel inside the first character
          return barLocationStart + const Offset(1, 1);
        }
      }

      Future<void> tapTextSpan(String text) async {
        final spanLocation = locationOfTextSpan(text);

        final binding = TestWidgetsFlutterBinding.instance;
        final downEvent = PointerDownEvent(position: spanLocation);
        binding.handlePointerEvent(downEvent);

        final upEvent = PointerUpEvent(position: spanLocation);
        binding.handlePointerEvent(upEvent);
      }
      testWidgets('concatenates text spans', (tester) async {
        await tester.pumpWidget(
          _stage(
            children: [
              RichText(
                text: TextSpan(
                  children: [
                    TextSpan(text: 'foo'),
                    TextSpan(
                      text: 'bar',
                      recognizer: TapGestureRecognizer()
                        ..onTap = () => print('click'),
                    ),
                  ],
                ),
              ),
            ],
          ),
        );

        spotText('foo').existsOnce();
        spotText('foobar').existsOnce();

        await tapTextSpan('bar');
      });

This example should be the default