feat: add alot
This commit is contained in:
@@ -1,39 +1,90 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:kooltab2/models/product.dart';
|
||||
import 'package:kooltab2/models/tab_item.dart';
|
||||
import 'package:kooltab2/services/bar_tab_service.dart';
|
||||
import 'package:kooltab2/services/product_service.dart';
|
||||
import 'package:kooltab2/services/remote_sync_service.dart';
|
||||
import 'package:kooltab2/viewmodels/bar_screen_view_model.dart';
|
||||
import 'package:kooltab2/viewmodels/inventory_view_model.dart';
|
||||
import 'package:kooltab2/viewmodels/remote_sync_view_model.dart';
|
||||
import 'package:kooltap/models/bar_tab.dart';
|
||||
import 'package:kooltap/models/tab_item.dart';
|
||||
import 'package:kooltap/services/bar_tab_service.dart';
|
||||
import 'package:kooltap/services/product_service.dart';
|
||||
import 'package:kooltap/viewmodels/bar_screen_view_model.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockBarTabService extends Mock implements BarTabService {}
|
||||
|
||||
class MockProductService extends Mock implements ProductService {}
|
||||
|
||||
class MockRemoteSyncService extends Mock implements RemoteSyncService {}
|
||||
BarTab _tab(String id) {
|
||||
return BarTab(
|
||||
id: id,
|
||||
customerName: id,
|
||||
openedAt: DateTime(2026),
|
||||
items: const [],
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
late MockBarTabService barTabService;
|
||||
late MockProductService productService;
|
||||
late InventoryViewModel inventory;
|
||||
late StreamController<List<BarTab>> openTabs;
|
||||
late BarScreenViewModel viewModel;
|
||||
late RemoteSyncViewModel remoteSyncViewModel;
|
||||
|
||||
setUp(() {
|
||||
barTabService = MockBarTabService();
|
||||
productService = MockProductService();
|
||||
inventory = InventoryViewModel(productService: productService);
|
||||
remoteSyncViewModel = RemoteSyncViewModel(
|
||||
syncService: MockRemoteSyncService(),
|
||||
);
|
||||
viewModel = BarScreenViewModel(
|
||||
barTabService: barTabService,
|
||||
inventory: inventory,
|
||||
remoteSyncViewModel: remoteSyncViewModel,
|
||||
);
|
||||
openTabs = StreamController<List<BarTab>>.broadcast();
|
||||
when(
|
||||
() => barTabService.watchOpenTabs(),
|
||||
).thenAnswer((_) => openTabs.stream);
|
||||
|
||||
viewModel = BarScreenViewModel(barTabService: barTabService);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
viewModel.dispose();
|
||||
await openTabs.close();
|
||||
});
|
||||
|
||||
test('shows tabs from the stream and selects the first one', () async {
|
||||
viewModel.ensureLoaded();
|
||||
expect(viewModel.isLoading, true);
|
||||
|
||||
openTabs.add([_tab('a'), _tab('b')]);
|
||||
await pumpEventQueue();
|
||||
|
||||
expect(viewModel.isLoading, false);
|
||||
expect(viewModel.tabs.map((tab) => tab.id), ['a', 'b']);
|
||||
expect(viewModel.selectedTabId, 'a');
|
||||
});
|
||||
|
||||
test('keeps the selection while that tab still exists', () async {
|
||||
viewModel.ensureLoaded();
|
||||
openTabs.add([_tab('a'), _tab('b')]);
|
||||
await pumpEventQueue();
|
||||
viewModel.selectTab('b');
|
||||
|
||||
openTabs.add([_tab('c'), _tab('b')]);
|
||||
await pumpEventQueue();
|
||||
|
||||
expect(viewModel.selectedTabId, 'b');
|
||||
});
|
||||
|
||||
test(
|
||||
'falls back to the first tab when the selected one disappears',
|
||||
() async {
|
||||
viewModel.ensureLoaded();
|
||||
openTabs.add([_tab('a'), _tab('b')]);
|
||||
await pumpEventQueue();
|
||||
viewModel.selectTab('b');
|
||||
|
||||
openTabs.add([_tab('a')]);
|
||||
await pumpEventQueue();
|
||||
|
||||
expect(viewModel.selectedTabId, 'a');
|
||||
},
|
||||
);
|
||||
|
||||
test('exposes a load error when the stream fails', () async {
|
||||
viewModel.ensureLoaded();
|
||||
openTabs.addError(StateError('database closed'));
|
||||
await pumpEventQueue();
|
||||
|
||||
expect(viewModel.loadError, isNotNull);
|
||||
expect(viewModel.isLoading, false);
|
||||
});
|
||||
|
||||
test('propagates an insufficient-stock error from the transaction', () async {
|
||||
@@ -55,41 +106,29 @@ void main() {
|
||||
throwsA(isA<InsufficientStockException>()),
|
||||
);
|
||||
|
||||
expect(viewModel.errorMessage, isNull);
|
||||
verify(
|
||||
() => barTabService.adjustTabItemQuantity(tabItemId: 'item-1', delta: 1),
|
||||
).called(1);
|
||||
expect(viewModel.loadError, isNull);
|
||||
});
|
||||
|
||||
test('updates inventory when a tab is deleted', () async {
|
||||
final product = Product(
|
||||
id: 'prod-1',
|
||||
name: 'Chips',
|
||||
category: 'Snacks',
|
||||
stockQuantity: 8,
|
||||
lowStockThreshold: 2,
|
||||
priceInCents: 150,
|
||||
);
|
||||
const item = TabItem(
|
||||
id: 'item-1',
|
||||
tabId: 'tab-1',
|
||||
productId: 'prod-1',
|
||||
productName: 'Chips',
|
||||
quantity: 3,
|
||||
unitPriceInCents: 150,
|
||||
);
|
||||
|
||||
when(() => productService.getProducts()).thenAnswer((_) async => [product]);
|
||||
await inventory.load();
|
||||
test('selects a newly created tab', () async {
|
||||
when(
|
||||
() => barTabService.deleteTab('tab-1'),
|
||||
).thenAnswer((_) async => [item]);
|
||||
when(() => barTabService.getOpenTabs()).thenAnswer((_) async => []);
|
||||
() => barTabService.createTab(customerName: 'New'),
|
||||
).thenAnswer((_) async => _tab('new'));
|
||||
|
||||
await viewModel.deleteTab('tab-1');
|
||||
await viewModel.createTab('New');
|
||||
|
||||
expect(inventory.products.first.stockQuantity, 11);
|
||||
verify(() => barTabService.deleteTab('tab-1')).called(1);
|
||||
verify(() => barTabService.getOpenTabs()).called(1);
|
||||
expect(viewModel.selectedTabId, 'new');
|
||||
});
|
||||
|
||||
test(
|
||||
'rethrows failed actions without turning them into a load error',
|
||||
() async {
|
||||
when(
|
||||
() => barTabService.deleteTab('tab-1'),
|
||||
).thenAnswer((_) async => throw StateError('boom'));
|
||||
|
||||
await expectLater(viewModel.deleteTab('tab-1'), throwsStateError);
|
||||
|
||||
expect(viewModel.loadError, isNull);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user