diff --git a/lib/ui/app/actions_menu_button.dart b/lib/ui/app/actions_menu_button.dart index 873ef8fb7..733e81152 100644 --- a/lib/ui/app/actions_menu_button.dart +++ b/lib/ui/app/actions_menu_button.dart @@ -61,3 +61,29 @@ class ActionMenuButton extends StatelessWidget { ); } } + +/// This class is used to differentiate List and View ActionMenuButtons +/// during tests +class ViewActionMenuButton extends StatelessWidget { + const ViewActionMenuButton({ + @required this.entity, + @required this.onSelected, + this.isSaving = false, + this.entityActions, + }); + + final BaseEntity entity; + final List entityActions; + final Function(BuildContext, EntityAction) onSelected; + final bool isSaving; + + @override + Widget build(BuildContext context) { + return ActionMenuButton( + entity: entity, + onSelected: onSelected, + isSaving: isSaving, + entityActions: entityActions + ); + } +} diff --git a/lib/ui/app/app_bottom_bar.dart b/lib/ui/app/app_bottom_bar.dart index 1a71d6c11..a874ffd56 100644 --- a/lib/ui/app/app_bottom_bar.dart +++ b/lib/ui/app/app_bottom_bar.dart @@ -335,6 +335,7 @@ class _AppBottomBarState extends State { final prefState = store.state.prefState; final isList = prefState.moduleLayout == ModuleLayout.list || widget.entityType.isSetting; + final company = state.company; return BottomAppBar( shape: CircularNotchedRectangle(), @@ -384,7 +385,6 @@ class _AppBottomBarState extends State { ), if (widget.statuses.isNotEmpty) IconButton( - tooltip: localization.filter, icon: Icon(Icons.filter), onPressed: _showFilterStatusSheet, color: store.state @@ -395,7 +395,6 @@ class _AppBottomBarState extends State { ), if (widget.customValues1.isNotEmpty) IconButton( - tooltip: localization.filter, icon: Icon(Icons.looks_one), onPressed: _showFilterCustom1Sheet, color: store.state @@ -406,7 +405,6 @@ class _AppBottomBarState extends State { ), if (widget.customValues2.isNotEmpty) IconButton( - tooltip: localization.filter, icon: Icon(Icons.looks_two), onPressed: _showFilterCustom2Sheet, color: store.state @@ -417,7 +415,6 @@ class _AppBottomBarState extends State { ), if (widget.customValues3.isNotEmpty) IconButton( - tooltip: localization.filter, icon: Icon(Icons.looks_two), onPressed: _showFilterCustom3Sheet, color: store.state @@ -428,7 +425,6 @@ class _AppBottomBarState extends State { ), if (widget.customValues4.isNotEmpty) IconButton( - tooltip: localization.filter, icon: Icon(Icons.looks_two), onPressed: _showFilterCustom4Sheet, color: store.state diff --git a/lib/ui/app/entity_dropdown.dart b/lib/ui/app/entity_dropdown.dart index 1819da04d..fd6a55f71 100644 --- a/lib/ui/app/entity_dropdown.dart +++ b/lib/ui/app/entity_dropdown.dart @@ -120,6 +120,7 @@ class _EntityDropdownState extends State { alignment: Alignment.centerRight, children: [ TypeAheadFormField( + validator: widget.validator, noItemsFoundBuilder: (context) => SizedBox(), suggestionsBoxDecoration: SuggestionsBoxDecoration( constraints: BoxConstraints( diff --git a/lib/ui/app/menu_drawer.dart b/lib/ui/app/menu_drawer.dart index 7fffeed4c..5d223c013 100644 --- a/lib/ui/app/menu_drawer.dart +++ b/lib/ui/app/menu_drawer.dart @@ -243,60 +243,70 @@ class MenuDrawer extends StatelessWidget { entityType: EntityType.client, icon: getEntityIcon(EntityType.client), title: localization.clients, + iconTooltip: localization.newClient, ), DrawerTile( company: company, entityType: EntityType.product, icon: getEntityIcon(EntityType.product), title: localization.products, + iconTooltip: localization.newProduct, ), DrawerTile( company: company, entityType: EntityType.invoice, icon: getEntityIcon(EntityType.invoice), title: localization.invoices, + iconTooltip: localization.newInvoice, ), DrawerTile( company: company, entityType: EntityType.payment, icon: getEntityIcon(EntityType.payment), title: localization.payments, + iconTooltip: localization.newPayment, ), DrawerTile( company: company, entityType: EntityType.quote, icon: getEntityIcon(EntityType.quote), title: localization.quotes, + iconTooltip: localization.newQuote, ), DrawerTile( company: company, entityType: EntityType.credit, icon: getEntityIcon(EntityType.credit), title: localization.credits, + iconTooltip: localization.newCredit, ), DrawerTile( company: company, entityType: EntityType.project, icon: getEntityIcon(EntityType.project), title: localization.projects, + iconTooltip: localization.newProject, ), DrawerTile( company: company, entityType: EntityType.task, icon: getEntityIcon(EntityType.task), title: localization.tasks, + iconTooltip: localization.newTask, ), DrawerTile( company: company, entityType: EntityType.vendor, icon: getEntityIcon(EntityType.vendor), title: localization.vendors, + iconTooltip: localization.newVendor, ), DrawerTile( company: company, entityType: EntityType.expense, icon: getEntityIcon(EntityType.expense), title: localization.expenses, + iconTooltip: localization.newExpense, ), // STARTER: menu - do not remove comment DrawerTile( @@ -349,6 +359,7 @@ class DrawerTile extends StatefulWidget { this.entityType, this.onLongPress, this.onCreateTap, + this.iconTooltip, }); final CompanyEntity company; @@ -358,6 +369,7 @@ class DrawerTile extends StatefulWidget { final Function onTap; final Function onLongPress; final Function onCreateTap; + final String iconTooltip; @override _DrawerTileState createState() => _DrawerTileState(); @@ -421,6 +433,7 @@ class _DrawerTileState extends State { ); } else if (userCompany.canCreate(widget.entityType)) { trailingWidget = IconButton( + tooltip: widget.iconTooltip, icon: Icon( Icons.add_circle_outline, color: textColor, diff --git a/lib/ui/app/view_scaffold.dart b/lib/ui/app/view_scaffold.dart index 1e1f0e90b..a057ef9d0 100644 --- a/lib/ui/app/view_scaffold.dart +++ b/lib/ui/app/view_scaffold.dart @@ -88,7 +88,7 @@ class ViewScaffold extends StatelessWidget { ); }) : Container(), - ActionMenuButton( + ViewActionMenuButton( isSaving: state.isSaving, entity: entity, onSelected: (context, action) => diff --git a/lib/ui/dashboard/dashboard_panels.dart b/lib/ui/dashboard/dashboard_panels.dart index c24dc1944..de518cee4 100644 --- a/lib/ui/dashboard/dashboard_panels.dart +++ b/lib/ui/dashboard/dashboard_panels.dart @@ -237,7 +237,7 @@ class DashboardPanels extends StatelessWidget { currentData: currentData, previousData: previousData, isLoaded: isLoaded, - title: AppLocalization.of(context).invoices); + title: AppLocalization.of(context).invoiced); } Widget _paymentChart(BuildContext context) { diff --git a/lib/ui/invoice/edit/invoice_edit_desktop.dart b/lib/ui/invoice/edit/invoice_edit_desktop.dart index 52c59d5d8..ef0a0cd01 100644 --- a/lib/ui/invoice/edit/invoice_edit_desktop.dart +++ b/lib/ui/invoice/edit/invoice_edit_desktop.dart @@ -42,8 +42,6 @@ class InvoiceEditDesktopState extends State TabController _tabController; FocusNode _focusNode; - static final GlobalKey _formKey = - GlobalKey(debugLabel: '_invoiceDesktopEdit'); final _invoiceNumberController = TextEditingController(); final _poNumberController = TextEditingController(); @@ -173,232 +171,228 @@ class InvoiceEditDesktopState extends State final invoice = viewModel.invoice; final company = viewModel.company; - return AppForm( - formKey: _formKey, - focusNode: _focusNode, - child: ListView( - key: ValueKey('__invoice_${invoice.id}__'), - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: FormCard( - padding: const EdgeInsets.only( - top: kMobileDialogPadding, - right: kMobileDialogPadding / 2, - bottom: kMobileDialogPadding, - left: kMobileDialogPadding), - children: [ - if (invoice.isNew) - ClientPicker( - autofocus: kIsWeb, - clientId: invoice.clientId, - clientState: viewModel.state.clientState, - onSelected: (client) => - viewModel.onClientChanged(invoice, client), - onAddPressed: (completer) => - viewModel.onAddClientPressed(context, completer), - ), - ConstrainedBox( - constraints: BoxConstraints(maxHeight: 200), - child: InvoiceEditContactsScreen(), + return ListView( + key: ValueKey('__invoice_${invoice.id}__'), + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: FormCard( + padding: const EdgeInsets.only( + top: kMobileDialogPadding, + right: kMobileDialogPadding / 2, + bottom: kMobileDialogPadding, + left: kMobileDialogPadding), + children: [ + if (invoice.isNew) + ClientPicker( + autofocus: kIsWeb, + clientId: invoice.clientId, + clientState: viewModel.state.clientState, + onSelected: (client) => + viewModel.onClientChanged(invoice, client), + onAddPressed: (completer) => + viewModel.onAddClientPressed(context, completer), ), - ], - ), + ConstrainedBox( + constraints: BoxConstraints(maxHeight: 200), + child: InvoiceEditContactsScreen(), + ), + ], ), - Expanded( - child: FormCard( - padding: const EdgeInsets.only( - top: kMobileDialogPadding, - right: kMobileDialogPadding / 2, - bottom: kMobileDialogPadding, - left: kMobileDialogPadding / 2), - children: [ + ), + Expanded( + child: FormCard( + padding: const EdgeInsets.only( + top: kMobileDialogPadding, + right: kMobileDialogPadding / 2, + bottom: kMobileDialogPadding, + left: kMobileDialogPadding / 2), + children: [ + DatePicker( + validator: (String val) => val.trim().isEmpty + ? AppLocalization.of(context).pleaseSelectADate + : null, + labelText: widget.entityType == EntityType.credit + ? localization.creditDate + : widget.entityType == EntityType.quote + ? localization.quoteDate + : localization.invoiceDate, + selectedDate: invoice.date, + onSelected: (date) { + viewModel + .onChanged(invoice.rebuild((b) => b..date = date)); + }, + ), + if (widget.entityType != EntityType.credit) DatePicker( - validator: (String val) => val.trim().isEmpty - ? AppLocalization.of(context).pleaseSelectADate - : null, - labelText: widget.entityType == EntityType.credit - ? localization.creditDate - : widget.entityType == EntityType.quote - ? localization.quoteDate - : localization.invoiceDate, - selectedDate: invoice.date, + allowClearing: true, + labelText: widget.entityType == EntityType.quote + ? localization.validUntil + : localization.dueDate, + selectedDate: invoice.dueDate, onSelected: (date) { - viewModel - .onChanged(invoice.rebuild((b) => b..date = date)); + viewModel.onChanged( + invoice.rebuild((b) => b..dueDate = date)); }, ), - if (widget.entityType != EntityType.credit) - DatePicker( - allowClearing: true, - labelText: widget.entityType == EntityType.quote - ? localization.validUntil - : localization.dueDate, - selectedDate: invoice.dueDate, - onSelected: (date) { - viewModel.onChanged( - invoice.rebuild((b) => b..dueDate = date)); - }, - ), - DecoratedFormField( - label: localization.partialDeposit, - controller: _partialController, - keyboardType: - TextInputType.numberWithOptions(decimal: true), + DecoratedFormField( + label: localization.partialDeposit, + controller: _partialController, + keyboardType: + TextInputType.numberWithOptions(decimal: true), + ), + if (invoice.partial != null && invoice.partial > 0) + DatePicker( + labelText: localization.partialDueDate, + selectedDate: invoice.partialDueDate, + onSelected: (date) { + viewModel.onChanged( + invoice.rebuild((b) => b..partialDueDate = date)); + }, ), - if (invoice.partial != null && invoice.partial > 0) - DatePicker( - labelText: localization.partialDueDate, - selectedDate: invoice.partialDueDate, - onSelected: (date) { - viewModel.onChanged( - invoice.rebuild((b) => b..partialDueDate = date)); - }, - ), - CustomField( - controller: _custom1Controller, - field: CustomFieldType.invoice1, - value: invoice.customValue1, - ), - CustomField( - controller: _custom3Controller, - field: CustomFieldType.invoice3, - value: invoice.customValue3, - ), - ], - ), + CustomField( + controller: _custom1Controller, + field: CustomFieldType.invoice1, + value: invoice.customValue1, + ), + CustomField( + controller: _custom3Controller, + field: CustomFieldType.invoice3, + value: invoice.customValue3, + ), + ], ), - Expanded( - child: FormCard( - padding: const EdgeInsets.only( - top: kMobileDialogPadding, - right: kMobileDialogPadding, - bottom: kMobileDialogPadding, - left: kMobileDialogPadding / 2), - children: [ - DecoratedFormField( - controller: _invoiceNumberController, - label: widget.entityType == EntityType.credit - ? localization.creditNumber - : widget.entityType == EntityType.quote - ? localization.quoteNumber - : localization.invoiceNumber, - validator: (String val) => - val.trim().isEmpty && invoice.isOld - ? AppLocalization.of(context) - .pleaseEnterAnInvoiceNumber - : null, - ), - DecoratedFormField( - label: localization.poNumber, - controller: _poNumberController, - ), - DiscountField( - controller: _discountController, - value: invoice.discount, - isAmountDiscount: invoice.isAmountDiscount, - onTypeChanged: (value) => viewModel.onChanged( - invoice.rebuild((b) => b..isAmountDiscount = value)), - ), - CustomField( - controller: _custom2Controller, - field: CustomFieldType.invoice2, - value: invoice.customValue2, - ), - CustomField( - controller: _custom4Controller, - field: CustomFieldType.invoice4, - value: invoice.customValue4, - ), - ], - ), + ), + Expanded( + child: FormCard( + padding: const EdgeInsets.only( + top: kMobileDialogPadding, + right: kMobileDialogPadding, + bottom: kMobileDialogPadding, + left: kMobileDialogPadding / 2), + children: [ + DecoratedFormField( + controller: _invoiceNumberController, + label: widget.entityType == EntityType.credit + ? localization.creditNumber + : widget.entityType == EntityType.quote + ? localization.quoteNumber + : localization.invoiceNumber, + validator: (String val) => val.trim().isEmpty && + invoice.isOld + ? AppLocalization.of(context).pleaseEnterAnInvoiceNumber + : null, + ), + DecoratedFormField( + label: localization.poNumber, + controller: _poNumberController, + ), + DiscountField( + controller: _discountController, + value: invoice.discount, + isAmountDiscount: invoice.isAmountDiscount, + onTypeChanged: (value) => viewModel.onChanged( + invoice.rebuild((b) => b..isAmountDiscount = value)), + ), + CustomField( + controller: _custom2Controller, + field: CustomFieldType.invoice2, + value: invoice.customValue2, + ), + CustomField( + controller: _custom4Controller, + field: CustomFieldType.invoice4, + value: invoice.customValue4, + ), + ], ), - ], - ), - widget.entityType == EntityType.credit - ? CreditEditItemsScreen() - : widget.entityType == EntityType.quote - ? QuoteEditItemsScreen() - : InvoiceEditItemsScreen(), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 2, - child: FormCard( - padding: const EdgeInsets.only( - top: kMobileDialogPadding, - right: kMobileDialogPadding / 2, - bottom: kMobileDialogPadding, - left: kMobileDialogPadding), - children: [ - TabBar( + ), + ], + ), + widget.entityType == EntityType.credit + ? CreditEditItemsScreen() + : widget.entityType == EntityType.quote + ? QuoteEditItemsScreen() + : InvoiceEditItemsScreen(), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 2, + child: FormCard( + padding: const EdgeInsets.only( + top: kMobileDialogPadding, + right: kMobileDialogPadding / 2, + bottom: kMobileDialogPadding, + left: kMobileDialogPadding), + children: [ + TabBar( + controller: _tabController, + tabs: [ + Tab(text: localization.publicNotes), + Tab(text: localization.privateNotes), + Tab( + text: widget.entityType == EntityType.credit + ? localization.creditTerms + : widget.entityType == EntityType.quote + ? localization.quoteTerms + : localization.invoiceTerms), + Tab( + text: widget.entityType == EntityType.credit + ? localization.creditFooter + : widget.entityType == EntityType.quote + ? localization.quoteFooter + : localization.invoiceFooter), + ], + ), + SizedBox( + height: 100, + child: TabBarView( controller: _tabController, - tabs: [ - Tab(text: localization.publicNotes), - Tab(text: localization.privateNotes), - Tab( - text: widget.entityType == EntityType.credit - ? localization.creditTerms - : widget.entityType == EntityType.quote - ? localization.quoteTerms - : localization.invoiceTerms), - Tab( - text: widget.entityType == EntityType.credit - ? localization.creditFooter - : widget.entityType == EntityType.quote - ? localization.quoteFooter - : localization.invoiceFooter), + children: [ + DecoratedFormField( + maxLines: 4, + controller: _publicNotesController, + keyboardType: TextInputType.multiline, + label: '', + ), + DecoratedFormField( + maxLines: 4, + controller: _privateNotesController, + keyboardType: TextInputType.multiline, + label: '', + ), + DecoratedFormField( + maxLines: 4, + controller: _termsController, + keyboardType: TextInputType.multiline, + label: '', + ), + DecoratedFormField( + maxLines: 4, + controller: _footerController, + keyboardType: TextInputType.multiline, + label: '', + ), ], ), - SizedBox( - height: 100, - child: TabBarView( - controller: _tabController, - children: [ - DecoratedFormField( - maxLines: 4, - controller: _publicNotesController, - keyboardType: TextInputType.multiline, - label: '', - ), - DecoratedFormField( - maxLines: 4, - controller: _privateNotesController, - keyboardType: TextInputType.multiline, - label: '', - ), - DecoratedFormField( - maxLines: 4, - controller: _termsController, - keyboardType: TextInputType.multiline, - label: '', - ), - DecoratedFormField( - maxLines: 4, - controller: _footerController, - keyboardType: TextInputType.multiline, - label: '', - ), - ], - ), - ), - ], - ), + ), + ], ), - Expanded( - flex: 1, - child: FormCard( - padding: const EdgeInsets.only( - top: kMobileDialogPadding, - right: kMobileDialogPadding, - bottom: kMobileDialogPadding, - left: kMobileDialogPadding / 2), - children: [ - /* + ), + Expanded( + flex: 1, + child: FormCard( + padding: const EdgeInsets.only( + top: kMobileDialogPadding, + right: kMobileDialogPadding, + bottom: kMobileDialogPadding, + left: kMobileDialogPadding / 2), + children: [ + /* DecoratedFormField( controller: null, enabled: false, @@ -406,60 +400,59 @@ class InvoiceEditDesktopState extends State label: localization.subtotal, ), */ - UserPicker( - userId: invoice.assignedUserId, - onChanged: (userId) => viewModel.onChanged( - invoice.rebuild((b) => b..assignedUserId = userId)), + UserPicker( + userId: invoice.assignedUserId, + onChanged: (userId) => viewModel.onChanged( + invoice.rebuild((b) => b..assignedUserId = userId)), + ), + DesignPicker( + initialValue: invoice.designId, + onSelected: (value) => viewModel.onChanged( + invoice.rebuild((b) => b..designId = value.id)), + ), + CustomSurcharges( + surcharge1Controller: _surcharge1Controller, + surcharge2Controller: _surcharge2Controller, + surcharge3Controller: _surcharge3Controller, + surcharge4Controller: _surcharge4Controller, + ), + if (company.settings.enableFirstInvoiceTaxRate) + TaxRateDropdown( + onSelected: (taxRate) => + viewModel.onChanged(invoice.applyTax(taxRate)), + labelText: localization.tax, + initialTaxName: invoice.taxName1, + initialTaxRate: invoice.taxRate1, ), - DesignPicker( - initialValue: invoice.designId, - onSelected: (value) => viewModel.onChanged( - invoice.rebuild((b) => b..designId = value.id)), + if (company.settings.enableSecondInvoiceTaxRate) + TaxRateDropdown( + onSelected: (taxRate) => viewModel + .onChanged(invoice.applyTax(taxRate, isSecond: true)), + labelText: localization.tax, + initialTaxName: invoice.taxName2, + initialTaxRate: invoice.taxRate2, ), - CustomSurcharges( - surcharge1Controller: _surcharge1Controller, - surcharge2Controller: _surcharge2Controller, - surcharge3Controller: _surcharge3Controller, - surcharge4Controller: _surcharge4Controller, + if (company.settings.enableThirdInvoiceTaxRate) + TaxRateDropdown( + onSelected: (taxRate) => viewModel + .onChanged(invoice.applyTax(taxRate, isThird: true)), + labelText: localization.tax, + initialTaxName: invoice.taxName3, + initialTaxRate: invoice.taxRate3, ), - if (company.settings.enableFirstInvoiceTaxRate) - TaxRateDropdown( - onSelected: (taxRate) => - viewModel.onChanged(invoice.applyTax(taxRate)), - labelText: localization.tax, - initialTaxName: invoice.taxName1, - initialTaxRate: invoice.taxRate1, - ), - if (company.settings.enableSecondInvoiceTaxRate) - TaxRateDropdown( - onSelected: (taxRate) => viewModel.onChanged( - invoice.applyTax(taxRate, isSecond: true)), - labelText: localization.tax, - initialTaxName: invoice.taxName2, - initialTaxRate: invoice.taxRate2, - ), - if (company.settings.enableThirdInvoiceTaxRate) - TaxRateDropdown( - onSelected: (taxRate) => viewModel.onChanged( - invoice.applyTax(taxRate, isThird: true)), - labelText: localization.tax, - initialTaxName: invoice.taxName3, - initialTaxRate: invoice.taxRate3, - ), - CustomSurcharges( - surcharge1Controller: _surcharge1Controller, - surcharge2Controller: _surcharge2Controller, - surcharge3Controller: _surcharge3Controller, - surcharge4Controller: _surcharge4Controller, - isAfterTaxes: true, - ), - ], - ), + CustomSurcharges( + surcharge1Controller: _surcharge1Controller, + surcharge2Controller: _surcharge2Controller, + surcharge3Controller: _surcharge3Controller, + surcharge4Controller: _surcharge4Controller, + isAfterTaxes: true, + ), + ], ), - ], - ), - ], - ), + ), + ], + ), + ], ); } } diff --git a/lib/ui/invoice/edit/invoice_edit_items_desktop.dart b/lib/ui/invoice/edit/invoice_edit_items_desktop.dart index 9f97cb9ee..13d9af2b5 100644 --- a/lib/ui/invoice/edit/invoice_edit_items_desktop.dart +++ b/lib/ui/invoice/edit/invoice_edit_items_desktop.dart @@ -136,6 +136,7 @@ class _InvoiceEditItemsDesktopState extends State { Padding( padding: const EdgeInsets.only(right: kTableColumnGap), child: TypeAheadFormField( + key: ValueKey('__line_item_${index}_name__'), initialValue: lineItems[index].productKey, noItemsFoundBuilder: (context) => SizedBox(), suggestionsCallback: (pattern) { @@ -238,6 +239,7 @@ class _InvoiceEditItemsDesktopState extends State { Padding( padding: const EdgeInsets.only(right: kTableColumnGap), child: TextFormField( + key: ValueKey('__line_item_${index}_description__'), initialValue: lineItems[index].notes, onChanged: (value) => viewModel.onChangedInvoiceItem( lineItems[index].rebuild((b) => b..notes = value), @@ -346,6 +348,7 @@ class _InvoiceEditItemsDesktopState extends State { Padding( padding: const EdgeInsets.only(right: kTableColumnGap), child: TextFormField( + key: ValueKey('__line_item_${index}_cost__'), textAlign: TextAlign.right, initialValue: formatNumber(lineItems[index].cost, context, formatNumberType: FormatNumberType.input, @@ -361,6 +364,7 @@ class _InvoiceEditItemsDesktopState extends State { Padding( padding: const EdgeInsets.only(right: kTableColumnGap), child: TextFormField( + key: ValueKey('__line_item_${index}_quantity__'), textAlign: TextAlign.right, initialValue: formatNumber( lineItems[index].quantity, context, diff --git a/test_driver/invoices_it_test.dart b/test_driver/invoices_it_test.dart index b905fb244..7f491c4a7 100644 --- a/test_driver/invoices_it_test.dart +++ b/test_driver/invoices_it_test.dart @@ -16,14 +16,16 @@ void runTestSuite({bool batchMode = false}) { final clientName = makeUnique(faker.company.name()); final poNumber = - faker.randomGenerator.integer(999999, min: 100000).toString(); + faker.randomGenerator.integer(999999, min: 100000).toString(); final productKey = makeUnique(faker.food.cuisine()); + final clientKey = faker.randomGenerator.integer(999999, min: 100000) + .toString(); final description = faker.lorem.sentences(5).toString(); final cost = - faker.randomGenerator.decimal(min: 50, scale: 10).toStringAsFixed(2); + faker.randomGenerator.decimal(min: 50, scale: 10).toStringAsFixed(2); final updatedPoNumber = - faker.randomGenerator.integer(999999, min: 100000).toString(); + faker.randomGenerator.integer(999999, min: 100000).toString(); setUpAll(() async { localization = TestLocalization('en'); @@ -67,35 +69,53 @@ void runTestSuite({bool batchMode = false}) { // Create a new invoice test('Add a new invoice', () async { + print('Tap new invoice'); await driver.tap(find.byTooltip(localization.newInvoice)); print('Create new client: $clientName'); - await driver.tap(find.byValueKey(Keys.clientPickerEmptyKey)); + if(await isMobile(driver)) { + await driver.tap(find.byValueKey(Keys.clientPickerEmptyKey)); + } await driver.tap(find.byTooltip(localization.createNew)); print('Fill the client form'); - await fillTextField( - driver: driver, field: localization.name, value: clientName); + await fillTextFields(driver, { + localization.name: clientName, + localization.idNumber: clientKey + }); // Await for Debouncer await Future.delayed(Duration(milliseconds: 500)); await driver.tap(find.text(localization.save)); + // Await for Screen change + await driver.waitFor(find.text(localization.newInvoice)); + print('Fill the invoice form'); - await driver.tap(find.byTooltip(localization.addItem)); - await driver.tap(find.byTooltip(localization.createNew)); + if(await isMobile(driver)) { + await driver.tap(find.byTooltip(localization.addItem)); + await driver.tap(find.byTooltip(localization.createNew)); - await fillTextFields(driver, { - localization.product: productKey, - localization.description: description, - localization.unitCost: cost, - localization.quantity: '1', - }); + await fillTextFields(driver, { + localization.product: productKey, + localization.description: description, + localization.unitCost: cost, + localization.quantity: '1', + }); - // Await for Debouncer - await Future.delayed(Duration(milliseconds: 500)); - await driver.tap(find.text(localization.done)); - await driver.tap(find.text(localization.details)); + // Await for Debouncer + await Future.delayed(Duration(milliseconds: 500)); + await driver.tap(find.text(localization.done)); + await driver.tap(find.text(localization.details)); + + } else { + await fillTextFields(driver, { + getLineItemKey('name', 0): productKey, + getLineItemKey('description', 0): description, + getLineItemKey('cost', 0): cost, + getLineItemKey('quantity', 0): '1' + }); + } await fillAndSaveForm(driver, { localization.poNumber: poNumber, @@ -140,7 +160,8 @@ void runTestSuite({bool batchMode = false}) { await selectAction(driver, localization.enterPayment); await driver.tap(find.text(localization.save)); // "Completed" status - await driver.waitFor(find.text(localization.paymentStatus4.toUpperCase())); + await driver.waitFor( + find.text(localization.paymentStatus4.toUpperCase())); if (await isMobile(driver)) { await driver.tap(find.pageBack()); diff --git a/test_driver/utils/common_actions.dart b/test_driver/utils/common_actions.dart index 0430431de..114e42b9b 100644 --- a/test_driver/utils/common_actions.dart +++ b/test_driver/utils/common_actions.dart @@ -9,6 +9,7 @@ import 'localizations.dart'; class Keys { static const String openAppDrawer = 'Open navigation menu'; static const String clientPickerEmptyKey = '__client___'; + static const String invoiceLineItemBaseKey = '__line_item'; } Future isTablet(FlutterDriver driver) async { @@ -129,15 +130,15 @@ Future fillAndSaveForm(FlutterDriver driver, Map values, // Await for Debouncer await Future.delayed(Duration(milliseconds: 400)); + print('Check for updated values'); + await checkTextFields(driver, values, except: skipCheckFor); + print('Tap save'); await driver.tap(find.text(localization.save)); // verify snackbar //await driver.waitFor(find.text(localization.updatedProduct)); //await driver.tap(find.pageBack()); - - print('Check for updated values'); - await checkTextFields(driver, values, except: skipCheckFor); } Future testArchiveAndDelete( @@ -146,32 +147,46 @@ Future testArchiveAndDelete( String deletedMessage, String restoredMessage}) async { final localization = TestLocalization('en'); + final mobile = await isMobile(driver); + + if (!mobile) { + // Show archived and deleted entries on tablet/web + await driver.tap(find.byTooltip(localization.filter)); + await driver.tap(find.text(localization.archived)); + await driver.tap(find.text(localization.deleted)); + await driver.tap(find.byTooltip(localization.filter)); + } print('Archive record'); await selectAction(driver, localization.archive); await driver.waitFor(find.text(archivedMessage)); - await driver.waitFor(find.text(localization.archived)); + //await driver.waitFor(find.text(localization.archived)); + print('Restore record'); await selectAction(driver, localization.restore); await driver.waitFor(find.text(restoredMessage)); - await driver.waitForAbsent(find.text(localization.archived)); + await driver.waitForAbsent(find.byType('Snackbar')); print('Delete record'); await selectAction(driver, localization.delete); await driver.waitFor(find.text(deletedMessage)); - await driver.waitFor(find.text(localization.deleted)); + //await driver.waitFor(find.text(localization.deleted)); + print('Restore record'); await selectAction(driver, localization.restore); await driver.waitFor(find.text(restoredMessage)); - await driver.waitForAbsent(find.text(localization.deleted)); + await driver.waitForAbsent(find.byType('Snackbar')); } Future selectAction(FlutterDriver driver, String action) async { - await driver.tap(find.byType('ActionMenuButton')); + await driver.tap(find.byType('ViewActionMenuButton')); await driver.tap(find.text(action)); } String makeUnique(String value) => '$value ${faker.randomGenerator.integer(999999, min: 100000)}'; + +String getLineItemKey(String key, int index) => + '${Keys.invoiceLineItemBaseKey}_${index}_${key}__';