diff --git a/lib/features/erd/erd_view.dart b/lib/features/erd/erd_view.dart index b20c89e1..b857f0d4 100644 --- a/lib/features/erd/erd_view.dart +++ b/lib/features/erd/erd_view.dart @@ -678,14 +678,32 @@ class _ErdViewState extends material.State { onAction: _load, ), ); + } else if (full != null && full.tables.isNotEmpty && schema!.isEmpty) { + // Every table was hidden from the diagram: they exist, so say so and + // offer them back instead of "No tables found". + body = material.Center( + child: QueryaEmptyState( + icon: material.Icon(material.Icons.visibility_off_outlined, + color: wb.mutedForeground), + title: 'All tables are hidden', + description: '${_hidden.length} ' + '${_hidden.length == 1 ? 'table is' : 'tables are'} hidden ' + 'from the diagram.', + actionLabel: 'Show all tables', + onAction: _showAllTables, + ), + ); } else if (schema == null || layout == null || schema.isEmpty) { + final db = widget.databaseName; body = material.Center( child: QueryaEmptyState( icon: material.Icon(material.Icons.table_chart_outlined, color: wb.mutedForeground), title: 'No tables found', - description: - 'This schema has no tables, or this role cannot see them.', + description: db.isEmpty + ? 'This schema has no tables, or this role cannot see them.' + : 'The current schema of $db has no tables, or this role ' + 'cannot see them.', ), ); } else if (widget.neighbourhoodDepth != null && diff --git a/test/features/erd/erd_test.dart b/test/features/erd/erd_test.dart index 28e2da1e..e6363bc1 100644 --- a/test/features/erd/erd_test.dart +++ b/test/features/erd/erd_test.dart @@ -749,6 +749,83 @@ void main() { closeTo(height, 0.5)); }); + testWidgets('Copy Mermaid puts the diagram on the clipboard', (t) async { + await t.binding.setSurfaceSize(const material.Size(1200, 800)); + String? copied; + t.binding.defaultBinaryMessenger.setMockMethodCallHandler( + SystemChannels.platform, (call) async { + if (call.method == 'Clipboard.setData') { + copied = (call.arguments as Map)['text'] as String?; + } + return null; + }); + addTearDown(() => t.binding.defaultBinaryMessenger + .setMockMethodCallHandler(SystemChannels.platform, null)); + await t.pumpWidget(queryaThemeTestShell( + child: ErdView( + source: SqlErdSource(delegate: delegate(), dialect: SqlDialect.sqlite)), + )); + await t.pump(); + await t.pump(); + + await t.tap(find.byKey(const material.ValueKey('erd_export'))); + await t.pump(); + await t.tap(find.text('Copy Mermaid')); + await t.pump(); + expect(copied, startsWith('erDiagram')); + expect(copied, contains('users ||--|{ orders')); + // The toast's 5 s timer starts after its entry animation: step the + // clock frame by frame until it has gone. + for (var i = 0; i < 10; i++) { + await t.pump(const Duration(seconds: 1)); + } + }); + + testWidgets('hiding every table says so and offers them back', + variant: desktop, (t) async { + await t.binding.setSurfaceSize(const material.Size(1200, 800)); + await t.pumpWidget(queryaThemeTestShell( + child: ErdView( + source: SqlErdSource(delegate: delegate(), dialect: SqlDialect.sqlite)), + )); + await t.pump(); + await t.pump(); + + for (final table in ['users', 'orders']) { + await t.tap(find.byKey(material.ValueKey('erd_table_$table')), + buttons: kSecondaryButton); + await t.pump(); + await t.pump(const Duration(milliseconds: 300)); + await t.tap(find.text('Hide from diagram')); + // Let the menu popover close, or it takes the next tap. + await t.pumpAndSettle(); + } + expect(find.text('All tables are hidden'), findsOneWidget); + expect(find.text('No tables found'), findsNothing); + + await t.tap(find.text('Show all tables')); + await t.pump(); + await t.pump(); + expect(find.byKey(const material.ValueKey('erd_table_users')), + findsOneWidget); + }); + + testWidgets('an empty schema names the database', (t) async { + await t.pumpWidget(queryaThemeTestShell( + child: ErdView( + source: SqlErdSource( + delegate: FakeSqlExecutionDelegate( + onExecute: (_) => const SqlExecutionResult()), + dialect: SqlDialect.postgres), + databaseName: 'shop', + ), + )); + await t.pump(); + await t.pump(); + expect(find.text('No tables found'), findsOneWidget); + expect(find.textContaining('shop'), findsOneWidget); + }); + testWidgets('a card can be dragged and Auto layout puts it back', (t) async { await t.binding.setSurfaceSize(const material.Size(1200, 800));