nandi/frqpublic Fork 0
32dfa6e7f898369d116106e25c4202474502f104
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

A face that opens someone 9bb81a1 · on 32dfa6e7f898369d116106e25c4202474502f104 · nandi · 14h ago
nim_layout_test.dart · 181 lines · 6.2 KBDart Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/// Every screen, laid out for real, at sizes that squeeze.
///
/// This is the test that was missing. `Cannot hit test a render box that has
/// never been laid out` is what a failed layout looks like from the outside,
/// and nothing automated ever laid the chat screen out — a GUI on Wayland
/// cannot be clicked, so every check stopped at the room list while the
/// biggest screen in the app went out unverified.
///
/// `tester.takeException()` is the whole point: a layout error is reported to
/// FlutterError rather than thrown at the caller, so a test that only pumps
/// and asserts on widgets passes while the screen is broken. These fail.
///
///   just test layout
library;

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:frq_core/frq_core.dart' as core;
import 'package:frq/nim_renderer.dart';

/// Phone, small desktop, and a deliberately cramped one. The head row of the
/// chat screen asks for more than 360 points has, which is why it wraps.
const sizes = <String, Size>{
  'phone': Size(360, 690),
  'desktop': Size(1280, 800),
  'cramped': Size(300, 500),
};

Future<void> layOut(WidgetTester tester, Size size) async {
  await tester.binding.setSurfaceSize(size);
  addTearDown(() => tester.binding.setSurfaceSize(null));
  await tester.pumpWidget(const NimApp());
  await tester.pump();
}

/// Nothing went to FlutterError while that frame was built.
void expectLaidOut(WidgetTester tester, String what) {
  final e = tester.takeException();
  expect(e, isNull, reason: '$what reported: $e');
}

void main() {
  // No offline guard needed: `demoUi` sets the state directly and none of
  // these dispatch `connect`, so nothing here opens a socket.

  group('the connect screen', () {
    for (final entry in sizes.entries) {
      testWidgets('lays out at ${entry.key}', (tester) async {
        core.resetUi();
        await layOut(tester, entry.value);
        expectLaidOut(tester, 'connect at ${entry.key}');
      });
    }

    testWidgets('lays out in every auth mode', (tester) async {
      for (final mode in ['guest', 'bluesky', 'app-password']) {
        core.resetUi();
        core.dispatch('mode.$mode');
        await layOut(tester, sizes['phone']!);
        expectLaidOut(tester, 'connect in $mode');
      }
    });
  });

  group('the chat screen', () {
    // The one that was never laid out by anything automated.
    for (final entry in sizes.entries) {
      testWidgets('lays out at ${entry.key}', (tester) async {
        core.demoUi();
        await layOut(tester, entry.value);
        expectLaidOut(tester, 'chat at ${entry.key}');
      });
    }

    testWidgets('renders the conversation it was given', (tester) async {
      core.demoUi();
      await layOut(tester, sizes['desktop']!);
      expect(find.text('hello there'), findsOneWidget);
      expect(find.text('#test'), findsWidgets);
      expectLaidOut(tester, 'chat content');
    });

    testWidgets('lays out with the people panel up', (tester) async {
      core.demoUi();
      core.dispatch('users.toggle');
      await layOut(tester, sizes['desktop']!);
      expectLaidOut(tester, 'chat with people');
    });

    testWidgets('lays out with every compose banner showing', (tester) async {
      core.demoUi();
      core.dispatch('reply.to:2');
      await layOut(tester, sizes['phone']!);
      expectLaidOut(tester, 'chat replying');

      core.demoUi();
      core.dispatch('edit.start:7');
      await layOut(tester, sizes['phone']!);
      expectLaidOut(tester, 'chat editing');
    });

    testWidgets('lays out with the emoji picker open', (tester) async {
      // 120 emoji in a grid under a message, on a phone.
      core.demoUi();
      core.dispatch('react.open:2');
      await layOut(tester, sizes['phone']!);
      expectLaidOut(tester, 'chat with the picker');
    });

    testWidgets('lays out with the picker showing a whole group',
        (tester) async {
      core.demoUi();
      core.dispatch('react.open:2');
      core.dispatch('emoji.group:Smileys & Emotion');
      await layOut(tester, sizes['cramped']!);
      expectLaidOut(tester, 'chat with a full picker');
    });

    testWidgets('lays out with the overview open', (tester) async {
      core.demoUi();
      core.dispatch('overview.toggle');
      await layOut(tester, sizes['phone']!);
      expectLaidOut(tester, 'chat with the overview');
    });

    testWidgets('lays out with the lightbox open', (tester) async {
      core.demoUi();
      core.dispatch('lightbox:https://example.com/a.png');
      await layOut(tester, sizes['phone']!);
      expectLaidOut(tester, 'chat with the lightbox');
    });

    testWidgets('lays out with a profile open', (tester) async {
      core.demoUi();
      // A guest: no identity to fetch, so the panel says so rather than
      // spinning — and it lays out without a network.
      core.dispatch('profile.open:alice:');
      await layOut(tester, sizes['phone']!);
      expectLaidOut(tester, 'chat with a guest profile');
    });

    testWidgets('lays out when scrolled off the present', (tester) async {
      core.demoUi();
      core.dispatch('jump.present');
      await layOut(tester, sizes['phone']!);
      expectLaidOut(tester, 'chat jumping');
    });
  });

  group('the chats list', () {
    for (final entry in sizes.entries) {
      testWidgets('lays out at ${entry.key}', (tester) async {
        core.demoUi();
        core.dispatch('screen.chats');
        await layOut(tester, entry.value);
        expectLaidOut(tester, 'chats at ${entry.key}');
      });
    }

    testWidgets('lays out with a search term in the box', (tester) async {
      core.demoUi();
      core.dispatch('screen.chats');
      core.dispatch('search.change', 'te');
      await layOut(tester, sizes['phone']!);
      expectLaidOut(tester, 'chats searching');
    });
  });

  group('discover and settings', () {
    for (final screen in ['discover', 'settings']) {
      for (final entry in sizes.entries) {
        testWidgets('$screen lays out at ${entry.key}', (tester) async {
          core.demoUi();
          core.dispatch('screen.$screen');
          await layOut(tester, entry.value);
          expectLaidOut(tester, '$screen at ${entry.key}');
        });
      }
    }
  });
}