Help → About gains a "Check for updates" button that fetches the latest GitHub release, semver-compares it to clideVersion, and shows the result inline: up-to-date, available (with a tappable link to the release notes), or a clear error. clide's first and only outbound HTTP call — a plain GET with no user data, run ONLY on this explicit tap, never on a launch path or a timer. So it's D-64-clean with no amendment; a background/periodic poll stays deferred (would need the narrow opt-in amendment first). The fetch is injectable so no test touches the network. compareSemver handles 2.3.10 > 2.3.9 and ranks pre-releases below their release. Closes T-492 (P1); the release-channel CI for downloadable signed packages is T-491, and download/ apply (P2/P3) depend on it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
63 lines
2.4 KiB
Dart
63 lines
2.4 KiB
Dart
/// T-47 P1: the About box "Check for updates" button. The check runs ONLY on
|
|
/// the explicit tap (never on open — POLICY/D-64), and surfaces the result
|
|
/// inline: up-to-date, available (with a release link), or a clear error.
|
|
library;
|
|
|
|
import 'package:clide/builtin/menubar/src/about_dialog.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import '../../helpers/kernel_fixture.dart';
|
|
import '../../helpers/widget_harness.dart';
|
|
|
|
void main() {
|
|
late KernelFixture f;
|
|
setUp(() async => f = await KernelFixture.create());
|
|
tearDown(() async => f.dispose());
|
|
|
|
Future<void> pump(WidgetTester tester, Future<String> Function(Uri) fetch) async {
|
|
tester.view.physicalSize = const Size(700, 1000);
|
|
tester.view.devicePixelRatio = 1.0;
|
|
addTearDown(() {
|
|
tester.view.resetPhysicalSize();
|
|
tester.view.resetDevicePixelRatio();
|
|
});
|
|
await tester.pumpWidget(harness(f, AboutDialog(onDismiss: () {}, updateFetch: fetch)));
|
|
await tester.pump();
|
|
}
|
|
|
|
testWidgets('does not fetch until the user taps Check for updates (no network on open)', (tester) async {
|
|
var calls = 0;
|
|
await pump(tester, (_) async {
|
|
calls++;
|
|
return '{"tag_name":"v2.9.0","html_url":"https://x/r"}';
|
|
});
|
|
expect(calls, 0, reason: 'opening the About box must not touch the network');
|
|
|
|
await tester.tap(find.text('Check for updates'));
|
|
await pumpAsync(tester);
|
|
expect(calls, 1);
|
|
});
|
|
|
|
testWidgets('shows an update-available link when a newer release exists', (tester) async {
|
|
await pump(tester, (_) async => '{"tag_name":"v99.0.0","html_url":"https://github.com/postmeridiem/clide/releases/v99.0.0"}');
|
|
await tester.tap(find.text('Check for updates'));
|
|
await pumpAsync(tester);
|
|
expect(find.textContaining('99.0.0'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('shows up-to-date when the latest release is not newer', (tester) async {
|
|
await pump(tester, (_) async => '{"tag_name":"v0.0.1","html_url":"https://x/r"}');
|
|
await tester.tap(find.text('Check for updates'));
|
|
await pumpAsync(tester);
|
|
expect(find.textContaining('latest version'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('surfaces a clear error when the check fails', (tester) async {
|
|
await pump(tester, (_) => Future.error('offline'));
|
|
await tester.tap(find.text('Check for updates'));
|
|
await pumpAsync(tester);
|
|
expect(find.textContaining("Couldn't check"), findsOneWidget);
|
|
});
|
|
}
|