xworkmate-app/test/web/web_assistant_page_browser_test.dart
2026-03-25 13:38:40 +08:00

169 lines
5.7 KiB
Dart

@TestOn('browser')
library;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:xworkmate/runtime/assistant_artifacts.dart';
import 'package:xworkmate/runtime/runtime_models.dart';
import 'package:xworkmate/theme/app_theme.dart';
import 'package:xworkmate/widgets/assistant_artifact_sidebar.dart';
import 'package:xworkmate/widgets/pane_resize_handle.dart';
void main() {
testWidgets('artifact sidebar opens, resizes, and collapses in browser', (
WidgetTester tester,
) async {
await tester.pumpWidget(
MaterialApp(
theme: AppTheme.light(),
home: const Scaffold(body: _BrowserArtifactSidebarHarness()),
),
);
await tester.pump();
await tester.pump(const Duration(milliseconds: 200));
expect(find.byKey(const Key('assistant-artifact-pane')), findsNothing);
expect(
find.byKey(const Key('assistant-artifact-pane-toggle')),
findsOneWidget,
);
await tester.tap(find.byKey(const Key('assistant-artifact-pane-toggle')));
await tester.pump();
await tester.pump(const Duration(milliseconds: 200));
expect(find.byKey(const Key('assistant-artifact-pane')), findsOneWidget);
final beforeWidth = tester
.getSize(find.byKey(const Key('assistant-artifact-pane')))
.width;
await tester.drag(
find.byKey(const Key('assistant-artifact-pane-resize-handle')),
const Offset(-80, 0),
);
await tester.pump();
await tester.pump(const Duration(milliseconds: 200));
final afterWidth = tester
.getSize(find.byKey(const Key('assistant-artifact-pane')))
.width;
expect(afterWidth, greaterThan(beforeWidth));
await tester.tap(find.byKey(const Key('assistant-artifact-pane-collapse')));
await tester.pump();
await tester.pump(const Duration(milliseconds: 200));
expect(find.byKey(const Key('assistant-artifact-pane')), findsNothing);
});
}
class _BrowserArtifactSidebarHarness extends StatefulWidget {
const _BrowserArtifactSidebarHarness();
@override
State<_BrowserArtifactSidebarHarness> createState() =>
_BrowserArtifactSidebarHarnessState();
}
class _BrowserArtifactSidebarHarnessState
extends State<_BrowserArtifactSidebarHarness> {
bool _collapsed = true;
double _width = 360;
late final AssistantArtifactSnapshot _snapshot = AssistantArtifactSnapshot(
workspaceRef: 'object://thread/browser-thread',
workspaceRefKind: WorkspaceRefKind.objectStore,
resultEntries: <AssistantArtifactEntry>[
const AssistantArtifactEntry(
id: 'readme',
label: 'README.md',
relativePath: 'README.md',
kind: AssistantArtifactEntryKind.object,
mimeType: 'text/markdown',
previewable: true,
workspaceRef: 'object://thread/browser-thread',
),
],
fileEntries: <AssistantArtifactEntry>[
const AssistantArtifactEntry(
id: 'readme',
label: 'README.md',
relativePath: 'README.md',
kind: AssistantArtifactEntryKind.object,
mimeType: 'text/markdown',
previewable: true,
workspaceRef: 'object://thread/browser-thread',
),
],
);
@override
Widget build(BuildContext context) {
return SizedBox.expand(
child: LayoutBuilder(
builder: (context, constraints) {
final maxWidth = constraints.maxWidth * 0.48;
final paneWidth = _width.clamp(280.0, maxWidth).toDouble();
return Stack(
children: [
Positioned.fill(
child: Row(
children: [
const Expanded(child: SizedBox.expand()),
if (!_collapsed) ...[
SizedBox(
key: const Key('assistant-artifact-pane-resize-handle'),
width: 8,
child: PaneResizeHandle(
axis: Axis.horizontal,
onDelta: (delta) {
setState(() {
_width = (_width - delta).clamp(280.0, maxWidth);
});
},
),
),
const SizedBox(width: 8),
SizedBox(
width: paneWidth,
child: AssistantArtifactSidebar(
sessionKey: 'browser-thread',
threadTitle: 'Browser thread',
workspaceRef: _snapshot.workspaceRef,
workspaceRefKind: _snapshot.workspaceRefKind,
onCollapse: () {
setState(() {
_collapsed = true;
});
},
loadSnapshot: () async => _snapshot,
loadPreview: (entry) async =>
const AssistantArtifactPreview(
kind: AssistantArtifactPreviewKind.markdown,
content: '# Browser artifact',
),
),
),
],
],
),
),
if (_collapsed)
Positioned(
right: 8,
top: 120,
child: AssistantArtifactSidebarRevealButton(
onTap: () {
setState(() {
_collapsed = false;
});
},
),
),
],
);
},
),
);
}
}