Merge ACP contract CI and remove golden fallback coverage

This commit is contained in:
Haitao Pan 2026-04-14 15:26:09 +08:00
parent 69ecca5ac8
commit ad6adca3fd
8 changed files with 6 additions and 274 deletions

View File

@ -1,66 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:xworkmate/app/app_controller.dart';
import 'package:xworkmate/features/assistant/assistant_page_composer_clipboard.dart';
import 'package:xworkmate/features/assistant/assistant_page_composer_skill_models.dart';
import 'package:xworkmate/features/assistant/assistant_page_main.dart';
import 'package:xworkmate/theme/app_theme.dart';
import 'package:xworkmate/widgets/surface_card.dart';
void main() {
testWidgets('assistant lower pane matches desktop baseline', (tester) async {
final controller = AppController();
addTearDown(controller.dispose);
await controller.sessionsController.switchSession('session-1');
await tester.binding.setSurfaceSize(const Size(1400, 360));
addTearDown(() async {
await tester.binding.setSurfaceSize(null);
});
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: Material(
child: Center(
child: SizedBox(
width: 1400,
height: 360,
child: SurfaceCard(
child: AssistantLowerPaneInternal(
bottomContentInset: 0,
controller: controller,
inputController: TextEditingController(text: '修复智能体模式'),
focusNode: FocusNode(),
thinkingLabel: 'medium',
showModelControl: false,
modelLabel: 'gpt-5.4',
modelOptions: const <String>[],
attachments: const <ComposerAttachmentInternal>[],
availableSkills: const <ComposerSkillOptionInternal>[],
selectedSkillKeys: const <String>[],
onRemoveAttachment: (_) {},
onToggleSkill: (_) {},
onThinkingChanged: (_) {},
onModelChanged: (_) async {},
onPickAttachments: () {},
onAddAttachment: (_) {},
onPasteImageAttachment: () async => null,
onComposerContentHeightChanged: (_) {},
onComposerInputHeightChanged: (_) {},
onSend: () async {},
),
),
),
),
),
),
);
await tester.pumpAndSettle();
await expectLater(
find.byType(MaterialApp),
matchesGoldenFile('goldens/assistant_lower_pane.png'),
);
});
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

View File

@ -1,51 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:xworkmate/features/settings/settings_about_panel.dart';
import 'package:xworkmate/theme/app_theme.dart';
import 'package:xworkmate/widgets/surface_card.dart';
void main() {
testWidgets('settings about panel matches baseline', (tester) async {
await tester.binding.setSurfaceSize(const Size(1280, 780));
addTearDown(() => tester.binding.setSurfaceSize(null));
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: Material(
child: Center(
child: SizedBox(
width: 1100,
child: SurfaceCard(
child: SettingsAboutPanel(
snapshot: const SettingsAboutSnapshot(
appVersion: '1.0.0-beta.2',
appBuildNumber: '4',
appBuildDate: '2026-03-28',
appCommit: 'f153d7b',
bridgeEndpoint: 'https://xworkmate-bridge.svc.plus',
bridgeStatus: 'ok',
bridgeVersion: '991ecb0ae2f270cdf6cc7bd456d4391cce664ae2',
bridgeBuildDate: '',
bridgeCommit:
'991ecb0ae2f270cdf6cc7bd456d4391cce664ae2',
bridgeImage:
'ghcr.io/x-evor/xworkmate-bridge:991ecb0ae2f270cdf6cc7bd456d4391cce664ae2',
),
busy: false,
onRefresh: () async {},
),
),
),
),
),
),
);
await tester.pumpAndSettle();
expect(
find.byType(SettingsAboutPanel),
matchesGoldenFile('goldens/settings_about_panel/default.png'),
);
});
}

View File

@ -1,156 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:xworkmate/features/settings/settings_account_panel.dart';
import 'package:xworkmate/runtime/runtime_models.dart';
import 'package:xworkmate/theme/app_theme.dart';
import 'package:xworkmate/widgets/surface_card.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('SettingsAccountPanel golden', () {
testWidgets('signed out state', (tester) async {
final controllers = _TestControllers();
addTearDown(controllers.dispose);
await tester.pumpWidget(
_buildGoldenHarness(
child: SettingsAccountPanel(
settings: SettingsSnapshot.defaults(),
accountSession: null,
accountState: null,
accountBusy: false,
accountSignedIn: false,
accountMfaRequired: false,
accountBaseUrlController: controllers.baseUrl,
accountIdentifierController: controllers.identifier,
accountPasswordController: controllers.password,
accountMfaCodeController: controllers.mfaCode,
onSaveAccountProfile: () async {},
onLogin: () async {},
onVerifyMfa: () async {},
onCancelMfa: () async {},
onSync: () async {},
onLogout: () async {},
),
),
);
await tester.pumpAndSettle();
expectLater(
find.byType(MaterialApp),
matchesGoldenFile('goldens/settings_account_panel/signed_out.png'),
);
});
testWidgets('signed in managed state', (tester) async {
final controllers = _TestControllers();
addTearDown(controllers.dispose);
final settings = SettingsSnapshot.defaults().copyWith(
accountBaseUrl: 'https://accounts.svc.plus',
accountUsername: 'review@svc.plus',
acpBridgeServerModeConfig: AcpBridgeServerModeConfig.defaults()
.copyWith(
cloudSynced: AcpBridgeServerModeConfig.defaults().cloudSynced
.copyWith(
lastSyncAt: DateTime(
2026,
4,
12,
10,
0,
).millisecondsSinceEpoch,
remoteServerSummary: AcpBridgeServerModeConfig.defaults()
.cloudSynced
.remoteServerSummary
.copyWith(endpoint: 'https://bridge.svc.plus'),
),
),
);
await tester.pumpWidget(
_buildGoldenHarness(
child: SettingsAccountPanel(
settings: settings,
accountSession: const AccountSessionSummary(
userId: 'u-1',
email: 'review@svc.plus',
name: 'Review User',
role: 'operator',
mfaEnabled: true,
totpEnabled: true,
),
accountState: AccountSyncState.defaults().copyWith(
syncState: 'ready',
syncMessage: 'Bridge access synced',
profileScope: 'bridge',
tokenConfigured: const AccountTokenConfigured(
bridge: true,
vault: false,
apisix: false,
),
),
accountBusy: false,
accountSignedIn: true,
accountMfaRequired: false,
accountBaseUrlController: controllers.baseUrl,
accountIdentifierController: controllers.identifier,
accountPasswordController: controllers.password,
accountMfaCodeController: controllers.mfaCode,
onSaveAccountProfile: () async {},
onLogin: () async {},
onVerifyMfa: () async {},
onCancelMfa: () async {},
onSync: () async {},
onLogout: () async {},
),
),
);
await tester.pumpAndSettle();
expectLater(
find.byType(MaterialApp),
matchesGoldenFile(
'goldens/settings_account_panel/signed_in_managed.png',
),
);
});
});
}
Widget _buildGoldenHarness({required Widget child}) {
return MaterialApp(
theme: AppTheme.light(),
home: Material(
child: Center(
child: SizedBox(
width: 1200,
height: 900,
child: Padding(
padding: const EdgeInsets.all(24),
child: SurfaceCard(child: child),
),
),
),
),
);
}
class _TestControllers {
final TextEditingController baseUrl = TextEditingController(
text: 'https://accounts.svc.plus',
);
final TextEditingController identifier = TextEditingController(
text: 'review@svc.plus',
);
final TextEditingController password = TextEditingController();
final TextEditingController mfaCode = TextEditingController();
void dispose() {
baseUrl.dispose();
identifier.dispose();
password.dispose();
mfaCode.dispose();
}
}

View File

@ -365,7 +365,12 @@ void main() {
);
addTearDown(() async {
if (await storeRoot.exists()) {
await storeRoot.delete(recursive: true);
try {
await storeRoot.delete(recursive: true);
} on FileSystemException {
// Temp cleanup is best effort here. The controller may still be
// releasing files when teardown starts.
}
}
});