added unit test
This commit is contained in:
@@ -116,6 +116,8 @@ public class CustomerPictureService extends AbstractService {
|
||||
criteriaQuery = criteriaQuery.where(builder.and(predicates.toArray(new Predicate[0])));
|
||||
}
|
||||
|
||||
//criteriaQuery = criteriaQuery.orderBy(builder.asc(builder.lower(customerRoot.get("name")))); FIXME: this causes errors
|
||||
|
||||
TypedQuery<Customer> typedQuery = entityManager.createQuery(criteriaQuery);
|
||||
List<Customer> customers = typedQuery.getResultList();
|
||||
customers.forEach(c -> c.getPictures().size());
|
||||
|
||||
@@ -24,4 +24,9 @@ class PictureDto {
|
||||
username: json['username'] as String?
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PictureDto{id: $id}';
|
||||
}
|
||||
}
|
||||
@@ -328,7 +328,7 @@ class _CustomerWidgetState extends State<CustomerWidget> {
|
||||
}
|
||||
|
||||
Future<void> _actionSelect(BuildContext context, CustomerDto customerDto, PictureDto pictureDto) async {
|
||||
String uri = "${GlobalRouter.pathHome}${GlobalRouter.pathPicture}/${customerDto.id}/${pictureDto.id}";
|
||||
String uri = "${GlobalRouter.pathHome}${GlobalRouter.pathCustomer}/${customerDto.id}${GlobalRouter.pathPicture}/${pictureDto.id}";
|
||||
context.go(uri);
|
||||
setState(() {
|
||||
_dto = _customerController.get(id: widget.customerId);
|
||||
|
||||
@@ -14,41 +14,55 @@ class PictureFullscreenDialog extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
final dialogWidth = screenSize.width * 0.9;
|
||||
final dialogHeight = screenSize.height * 0.9 - 50;
|
||||
|
||||
final imageBytes = base64Decode(dto.image);
|
||||
return Dialog(
|
||||
backgroundColor: _generalStyle.pageBackgroundColor,
|
||||
insetPadding: const EdgeInsets.all(24),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
height: MediaQuery.of(context).size.height * 0.9,
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
InteractiveViewer(
|
||||
panEnabled: true,
|
||||
scaleEnabled: true,
|
||||
minScale: 0.5,
|
||||
maxScale: 5.0,
|
||||
child: Center(
|
||||
child: Image.memory(
|
||||
base64Decode(dto.image),
|
||||
fit: BoxFit.contain,
|
||||
backgroundColor: Colors.black,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0, right: 8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
IconButton(
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: _generalStyle.primaryButtonBackgroundColor,
|
||||
shape: const CircleBorder(),
|
||||
minimumSize: const Size(36, 36),
|
||||
padding: const EdgeInsets.all(6),
|
||||
),
|
||||
icon: const Icon(Icons.close, color: Colors.white, size: 20),
|
||||
onPressed: () async => await _actionBack(context),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 16,
|
||||
right: 16,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.close, color: _generalStyle.primaryButtonBackgroundColor, size: 32),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: dialogWidth,
|
||||
height: dialogHeight,
|
||||
child: InteractiveViewer(
|
||||
constrained: true,
|
||||
//boundaryMargin: EdgeInsets.all(20),
|
||||
panEnabled: true,
|
||||
scaleEnabled: true,
|
||||
minScale: 0.5,
|
||||
maxScale: 5.0,
|
||||
child: Image.memory(
|
||||
imageBytes,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _actionBack(BuildContext context) async {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,13 +113,17 @@ class _PictureWidgetState extends State<PictureWidget> {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final isNarrow = constraints.maxWidth < 800;
|
||||
final maxImageWidth = constraints.maxWidth * 0.5;
|
||||
return SingleChildScrollView(
|
||||
child: isNarrow
|
||||
? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_imageWidget(selectedPicture),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: maxImageWidth),
|
||||
child: _imageWidget(selectedPicture),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
_contentWidget(selectedPicture),
|
||||
],
|
||||
@@ -128,7 +132,10 @@ class _PictureWidgetState extends State<PictureWidget> {
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_imageWidget(selectedPicture),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: maxImageWidth),
|
||||
child: _imageWidget(selectedPicture),
|
||||
),
|
||||
const SizedBox(width: 32),
|
||||
Expanded(child: _contentWidget(selectedPicture)),
|
||||
],
|
||||
@@ -142,7 +149,7 @@ class _PictureWidgetState extends State<PictureWidget> {
|
||||
return GestureDetector(
|
||||
key: const Key("image"),
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => _showFullscreenImage(dto),
|
||||
onTap: () => _actionShowFullscreenImage(dto),
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: ConstrainedBox(
|
||||
@@ -156,15 +163,6 @@ class _PictureWidgetState extends State<PictureWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showFullscreenImage(PictureDto dto) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return PictureFullscreenDialog(dto: dto);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _contentWidget(PictureDto dto) {
|
||||
final labelStyle = TextStyle(
|
||||
fontWeight: FontWeight.normal,
|
||||
@@ -211,9 +209,12 @@ class _PictureWidgetState extends State<PictureWidget> {
|
||||
style: contentStyle,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.pictureWidgetLabelZip,
|
||||
style: labelStyle,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 24.0),
|
||||
child: Text(
|
||||
AppLocalizations.of(context)!.pictureWidgetLabelZip,
|
||||
style: labelStyle,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
@@ -222,9 +223,12 @@ class _PictureWidgetState extends State<PictureWidget> {
|
||||
style: contentStyle,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.pictureWidgetLabelCity,
|
||||
style: labelStyle,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 24.0),
|
||||
child: Text(
|
||||
AppLocalizations.of(context)!.pictureWidgetLabelCity,
|
||||
style: labelStyle,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
@@ -233,32 +237,6 @@ class _PictureWidgetState extends State<PictureWidget> {
|
||||
style: contentStyle,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20.0),
|
||||
child: Text(
|
||||
AppLocalizations.of(context)!.pictureWidgetLabelComment,
|
||||
style: labelStyle,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 150,
|
||||
child: Scrollbar(
|
||||
controller: _commentScrollController,
|
||||
thumbVisibility: true,
|
||||
child: SingleChildScrollView(
|
||||
controller: _commentScrollController,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
dto.comment ?? "",
|
||||
style: contentStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -266,6 +244,32 @@ class _PictureWidgetState extends State<PictureWidget> {
|
||||
_evaluationCard(dto),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 24.0),
|
||||
child: Text(
|
||||
AppLocalizations.of(context)!.pictureWidgetLabelComment,
|
||||
style: labelStyle,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 150,
|
||||
child: Scrollbar(
|
||||
controller: _commentScrollController,
|
||||
thumbVisibility: true,
|
||||
child: SingleChildScrollView(
|
||||
controller: _commentScrollController,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
dto.comment ?? "",
|
||||
style: contentStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -332,8 +336,8 @@ class _PictureWidgetState extends State<PictureWidget> {
|
||||
onTap: () async => _actionUpdateEvaluation(value),
|
||||
customBorder: const CircleBorder(),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
shape: BoxShape.circle,
|
||||
@@ -345,10 +349,14 @@ class _PictureWidgetState extends State<PictureWidget> {
|
||||
|
||||
// Bottom navigation buttons
|
||||
Widget _bottomNavigationWidget(List<PictureDto> pictures, PictureDto selectedPicture) {
|
||||
pictures.sort((a, b) => a.pictureDate.compareTo(b.pictureDate));
|
||||
print(pictures);
|
||||
final currentIndex = pictures.indexWhere((p) => p.id == selectedPicture.id);
|
||||
final hasPrevious = currentIndex > 0;
|
||||
final hasNext = currentIndex < pictures.length - 1;
|
||||
|
||||
print("hasnext $hasNext hasPrevious $hasPrevious current $currentIndex");
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 24.0),
|
||||
child: Row(
|
||||
@@ -405,6 +413,15 @@ class _PictureWidgetState extends State<PictureWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
void _actionShowFullscreenImage(PictureDto dto) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return PictureFullscreenDialog(dto: dto);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _actionUpdateEvaluation(int value) async {
|
||||
_selectedPicture?.evaluation = value;
|
||||
_pictureController.updateEvaluation(_selectedPicture!);
|
||||
@@ -412,11 +429,14 @@ class _PictureWidgetState extends State<PictureWidget> {
|
||||
}
|
||||
|
||||
void _actionNavigateToPicture(int index) {
|
||||
print("navigate. too $index");
|
||||
final pictures = _customerDto.pictures;
|
||||
if (index >= 0 && index < pictures.length) {
|
||||
setState(() {
|
||||
_selectedPicture = pictures[index];
|
||||
});
|
||||
}else {
|
||||
print("empty");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,23 +40,25 @@ class GlobalRouter {
|
||||
builder: (context, state) => CustomerListWidget(),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "$pathCustomer/:id",
|
||||
path: "$pathCustomer/:customerId",
|
||||
builder: (context, state) {
|
||||
var idStr = state.pathParameters['id'];
|
||||
var idStr = state.pathParameters['customerId'];
|
||||
var id = idStr == null ? null : int.tryParse(idStr);
|
||||
return CustomerWidget(customerId: id ?? -1);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "$pathPicture/:customerId/:pictureId",
|
||||
builder: (context, state) {
|
||||
var customerIdStr = state.pathParameters['customerId'];
|
||||
var customerId = customerIdStr == null ? null : int.tryParse(customerIdStr);
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "$pathPicture/:pictureId",
|
||||
builder: (context, state) {
|
||||
var customerIdStr = state.pathParameters['customerId'];
|
||||
var customerId = customerIdStr == null ? null : int.tryParse(customerIdStr);
|
||||
|
||||
var pictureIdStr = state.pathParameters['pictureId'];
|
||||
var pictureId = pictureIdStr == null ? null : int.tryParse(pictureIdStr);
|
||||
return PictureWidget(customerId: customerId ?? -1, pictureId: pictureId ?? -1);
|
||||
},
|
||||
var pictureIdStr = state.pathParameters['pictureId'];
|
||||
var pictureId = pictureIdStr == null ? null : int.tryParse(pictureIdStr);
|
||||
return PictureWidget(customerId: customerId ?? -1, pictureId: pictureId ?? -1);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -75,7 +75,7 @@ void main() {
|
||||
when(mockCustomerController.get(id: 1)).thenAnswer((_) async => customerDto);
|
||||
when(mockCustomerController.getAll("", "")).thenAnswer((_) async => _list);
|
||||
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathPicture}/1/1");
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathCustomer}/1${GlobalRouter.pathPicture}/1");
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify customer name is displayed
|
||||
@@ -95,7 +95,7 @@ void main() {
|
||||
when(mockCustomerController.get(id: 1)).thenAnswer((_) async => customerDto);
|
||||
when(mockCustomerController.getAll("", "")).thenAnswer((_) async => _list);
|
||||
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathPicture}/1/1");
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathCustomer}/1${GlobalRouter.pathPicture}/1");
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify the comment is displayed
|
||||
@@ -108,7 +108,7 @@ void main() {
|
||||
when(mockCustomerController.get(id: 1)).thenAnswer((_) async => customerDto);
|
||||
when(mockCustomerController.getAll("", "")).thenAnswer((_) async => _list);
|
||||
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathPicture}/1/1");
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathCustomer}/1${GlobalRouter.pathPicture}/1");
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// The comment field should be empty but the label should exist
|
||||
@@ -121,7 +121,7 @@ void main() {
|
||||
when(mockCustomerController.get(id: 1)).thenAnswer((_) async => customerDto);
|
||||
when(mockCustomerController.getAll("", "")).thenAnswer((_) async => _list);
|
||||
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathPicture}/1/1");
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathCustomer}/1${GlobalRouter.pathPicture}/1");
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Both navigation buttons should always be visible at the bottom
|
||||
@@ -135,7 +135,7 @@ void main() {
|
||||
when(mockCustomerController.get(id: 1)).thenAnswer((_) async => customerDto);
|
||||
when(mockCustomerController.getAll("", "")).thenAnswer((_) async => _list);
|
||||
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathPicture}/1/1");
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathCustomer}/1${GlobalRouter.pathPicture}/1");
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Both navigation buttons should be visible
|
||||
@@ -149,7 +149,7 @@ void main() {
|
||||
when(mockCustomerController.get(id: 1)).thenAnswer((_) async => customerDto);
|
||||
when(mockCustomerController.getAll("", "")).thenAnswer((_) async => _list);
|
||||
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathPicture}/1/1");
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathCustomer}/1${GlobalRouter.pathPicture}/1");
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Both navigation buttons should be visible (right one is disabled)
|
||||
@@ -163,16 +163,17 @@ void main() {
|
||||
when(mockCustomerController.get(id: 1)).thenAnswer((_) async => customerDto);
|
||||
when(mockCustomerController.getAll("", "")).thenAnswer((_) async => _list);
|
||||
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathPicture}/1/1");
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathCustomer}/1${GlobalRouter.pathPicture}/1");
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify first picture comment is shown
|
||||
expect(find.text('First picture comment'), findsOneWidget);
|
||||
|
||||
// Tap right navigation button
|
||||
await tester.tap(find.byIcon(Icons.chevron_right));
|
||||
print(find.byKey(Key("navigate_right")));
|
||||
await tester.tap(find.byKey(Key("navigate_right")));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
|
||||
// Verify second picture comment is now shown
|
||||
expect(find.text('Second picture comment'), findsOneWidget);
|
||||
expect(find.text('First picture comment'), findsNothing);
|
||||
@@ -184,7 +185,7 @@ void main() {
|
||||
when(mockCustomerController.get(id: 1)).thenAnswer((_) async => customerDto);
|
||||
when(mockCustomerController.getAll("", "")).thenAnswer((_) async => _list);
|
||||
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathPicture}/1/2");
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathCustomer}/1${GlobalRouter.pathPicture}/2");
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify second picture comment is shown
|
||||
@@ -212,7 +213,7 @@ void main() {
|
||||
when(mockCustomerController.get(id: 1)).thenAnswer((_) async => singlePictureCustomer);
|
||||
when(mockCustomerController.getAll("", "")).thenAnswer((_) async => _list);
|
||||
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathPicture}/1/1");
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathCustomer}/1${GlobalRouter.pathPicture}/1");
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Both navigation buttons should be shown but disabled
|
||||
@@ -226,7 +227,7 @@ void main() {
|
||||
when(mockCustomerController.get(id: 1)).thenAnswer((_) async => customerDto);
|
||||
when(mockCustomerController.getAll("", "")).thenAnswer((_) async => _list);
|
||||
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathPicture}/1/1");
|
||||
await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathCustomer}/1${GlobalRouter.pathPicture}/1");
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Find the GestureDetector with the image key
|
||||
|
||||
Reference in New Issue
Block a user