arblant
Railynx / test / ui / home_page_test.dart
home_page_test.dart182 lines · 6.7 KB
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 182
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:railway_clean_ip_scanner/app/controller.dart';
import 'package:railway_clean_ip_scanner/main.dart';
import 'package:railway_clean_ip_scanner/ui/widgets.dart';

import '../fakes.dart';

void _useDesktopSize(WidgetTester tester) {
  tester.view.physicalSize = const Size(1280, 1400);
  tester.view.devicePixelRatio = 1;
  addTearDown(tester.view.resetPhysicalSize);
  addTearDown(tester.view.resetDevicePixelRatio);
}

AppController _controller(FakeEngine engine) {
  final c = AppController(engine: engine);
  c.animatedAurora = false;
  return c;
}

void main() {
  testWidgets('empty state renders the title, status and empty results text', (tester) async {
    _useDesktopSize(tester);
    final c = _controller(FakeEngine());
    addTearDown(c.dispose);
    await tester.pumpWidget(ScannerApp(controller: c));
    await tester.pump();

    expect(find.text('Twilight edition'), findsOneWidget);
    expect(find.text('Ready'), findsOneWidget);
    expect(find.text('Start scan'), findsOneWidget);
    expect(find.text('Made by arblant & Muse'), findsOneWidget);
    expect(
      find.text('PC port by arblant — based on the Android app by Unknown37841 (MIT)'),
      findsOneWidget,
    );
    expect(find.text('No clean IPs yet'), findsOneWidget);
    expect(
      find.text('Start a scan. Every IP is verified through a real tunnel, and the fastest ones appear here, ranked.'),
      findsOneWidget,
    );
    expect(find.text('Copy top 5'), findsOneWidget);
    expect(find.text('Build configs'), findsOneWidget);
  });

  testWidgets('starting a scan without a config shows the paste-first toast', (tester) async {
    _useDesktopSize(tester);
    final c = _controller(FakeEngine());
    addTearDown(c.dispose);
    await tester.pumpWidget(ScannerApp(controller: c));
    await tester.pump();

    await tester.tap(find.text('Start scan'));
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 400));

    expect(find.text('Paste a VLESS config first to load your server'), findsOneWidget);
    expect(
      find.text('Please paste a VLESS config above to load your server.'),
      findsOneWidget,
    );
  });

  testWidgets('results show the fastest card and the ranked list', (tester) async {
    _useDesktopSize(tester);
    final engine = FakeEngine(scanResults: [ok('10.0.0.1', 90), ok('10.0.0.2', 240)]);
    final c = _controller(engine);
    addTearDown(c.dispose);
    await c.parseLink(sampleLink);
    await tester.pumpWidget(ScannerApp(controller: c));
    await tester.runAsync(() => c.toggleScan());
    await tester.pump();

    expect(find.text('FASTEST'), findsOneWidget);
    expect(find.text('TOP PICK'), findsOneWidget);
    expect(find.text('STABLE'), findsOneWidget);
    expect(find.text('Clean IPs'), findsOneWidget);
    expect(find.text('by delay'), findsOneWidget);
    expect(find.text('01'), findsNothing);
    expect(find.text('02'), findsOneWidget);
    expect(find.text('90 ms'), findsWidgets);
    expect(find.text('240 ms'), findsOneWidget);
  });

  testWidgets('Build configs opens the workbench with the rows and actions', (tester) async {
    _useDesktopSize(tester);
    final engine = FakeEngine(scanResults: [ok('10.0.0.1', 90)]);
    final c = _controller(engine);
    addTearDown(c.dispose);
    await c.parseLink(sampleLink);
    await tester.pumpWidget(ScannerApp(controller: c));
    await tester.runAsync(() => c.toggleScan());
    await tester.pump();

    await tester.tap(find.text('Build configs'));
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 400));

    expect(find.text('CONFIGS'), findsOneWidget);
    expect(find.text('Top N'), findsOneWidget);
    expect(find.text('Best for all'), findsOneWidget);
    expect(find.text('Custom'), findsOneWidget);
    expect(find.text('Test these'), findsOneWidget);
    expect(find.text('Generate & copy'), findsOneWidget);
    expect(find.text('01'), findsOneWidget);
    await tester.scrollUntilVisible(
      find.text('05'),
      120,
      scrollable: find.byType(Scrollable).last,
    );
    expect(find.text('05'), findsOneWidget);
    expect(find.text('Only 1 clean IP found, so some configs will share one.'), findsOneWidget);
  });

  testWidgets('the workbench says to scan first when there are no results', (tester) async {
    _useDesktopSize(tester);
    final c = _controller(FakeEngine());
    addTearDown(c.dispose);
    await tester.pumpWidget(ScannerApp(controller: c));
    await tester.pump();

    await tester.tap(find.text('Build configs'));
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 400));

    expect(find.text('Scan first to build configs'), findsOneWidget);
  });

  testWidgets('there is no Parse button, and Paste parses the link at once', (tester) async {
    _useDesktopSize(tester);
    tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, (call) async {
      if (call.method == 'Clipboard.getData') {
        return <String, dynamic>{'text': sampleLink};
      }
      return null;
    });
    addTearDown(() => tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, null));
    final c = _controller(FakeEngine());
    addTearDown(c.dispose);
    await tester.pumpWidget(ScannerApp(controller: c));
    await tester.pump();

    expect(find.text('Parse'), findsNothing);
    expect(find.text('Paste'), findsOneWidget);

    await tester.tap(find.text('Paste'));
    await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 50)));
    await tester.pump();

    expect(c.uuid.text, isNotEmpty);
    expect(c.parseHint, startsWith('Parsed VLESS'));
  });

  testWidgets('the idle ring shows 0 of 254 and the Ready status', (tester) async {
    _useDesktopSize(tester);
    final c = _controller(FakeEngine());
    addTearDown(c.dispose);
    await tester.pumpWidget(ScannerApp(controller: c));
    await tester.pump(const Duration(milliseconds: 700));

    expect(find.byType(ScanRing), findsOneWidget);
    expect(find.text('of 254'), findsOneWidget);
    expect(find.text('Ready'), findsOneWidget);
  });

  testWidgets('the layout fits a phone-sized window', (tester) async {
    tester.view.physicalSize = const Size(390, 844);
    tester.view.devicePixelRatio = 1;
    addTearDown(tester.view.resetPhysicalSize);
    addTearDown(tester.view.resetDevicePixelRatio);
    final c = _controller(FakeEngine());
    addTearDown(c.dispose);
    await tester.pumpWidget(ScannerApp(controller: c));
    await tester.pump(const Duration(milliseconds: 700));

    expect(tester.takeException(), isNull);
    expect(find.text('Start scan'), findsOneWidget);
    expect(find.text('Copy top 5'), findsOneWidget);
  });
}