Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/src/multi_trigger_autocomplete.dart
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ class MultiTriggerAutocompleteState extends State<MultiTriggerAutocomplete> {
final text = _textEditingController.text;

var start = querySelection.baseOffset;
if (!keepTrigger) start -= 1;
if (!keepTrigger) start -= _currentTrigger?.trigger.length ?? 1;

final end = querySelection.extentOffset;

Expand Down
51 changes: 51 additions & 0 deletions test/multi_trigger_autocomplete_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,57 @@ void main() {
expect(find.byKey(fieldKey), findsOneWidget);
expect(find.byType(TextFormField), findsNothing);
});

testWidgets('handles multi-character triggers with keepTrigger=false', (tester) async {
final multiCharTrigger = AutocompleteTrigger(
trigger: '##'
optionsViewBuilder: (context, query, controller) { },
);

await tester.pumpWidget(
Boilerplate(
child: MultiTriggerAutocomplete(
autocompleteTriggers: [multiCharTrigger],
),
),
);

await tester.enterText(find.byType(TextFormField), '##');

await tester.tap(find.byType(InkWell).first);
await tester.pump();

final TextFormField field =
find.byType(TextFormField).evaluate().first.widget as TextFormField;

expect(field.controller!.text, isNot(contains('##')));
});
Comment on lines +301 to +324
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Test has syntax errors and will fail at runtime.

This test has multiple issues that will cause it to fail:

  1. Syntax error on line 303: Missing comma after trigger: '##'
  2. Empty optionsViewBuilder on line 304: The function body { } doesn't return a Widget as required. It must return a widget like ListView with tappable items.
  3. Line 317 will fail: Attempting to tap find.byType(InkWell).first but the empty optionsViewBuilder doesn't create any InkWell widgets, causing the finder to fail.
  4. Missing debounce wait: After line 315's enterText, you should add await tester.pumpAndSettle(kDebounceDuration); to allow the options to appear (see line 159 for reference).
  5. No verification of options: Should verify find.byType(ListView) finds a widget before attempting to tap.

Apply this diff to fix the test:

-    testWidgets('handles multi-character triggers with keepTrigger=false', (tester) async {
-      final multiCharTrigger = AutocompleteTrigger(
-        trigger: '##'
-        optionsViewBuilder: (context, query, controller) { },
-      );
-    
-      await tester.pumpWidget(
-        Boilerplate(
-          child: MultiTriggerAutocomplete(
-            autocompleteTriggers: [multiCharTrigger],
-          ),
-        ),
-      );
-    
-      await tester.enterText(find.byType(TextFormField), '##');
-      
-      await tester.tap(find.byType(InkWell).first);
-      await tester.pump();
-    
-      final TextFormField field = 
-        find.byType(TextFormField).evaluate().first.widget as TextFormField;
-      
-      expect(field.controller!.text, isNot(contains('##')));
-    });
+    testWidgets('handles multi-character triggers with keepTrigger=false', (tester) async {
+      final multiCharTrigger = AutocompleteTrigger(
+        trigger: '##',
+        optionsViewBuilder: (context, query, controller) {
+          return ListView.builder(
+            itemCount: 1,
+            itemBuilder: (context, index) {
+              return ListTile(
+                title: const Text('Option'),
+                onTap: () {
+                  final autocomplete = MultiTriggerAutocomplete.of(context);
+                  return autocomplete.acceptAutocompleteOption('Option');
+                },
+              );
+            },
+          );
+        },
+      );
+    
+      await tester.pumpWidget(
+        Boilerplate(
+          child: MultiTriggerAutocomplete(
+            debounceDuration: kDebounceDuration,
+            autocompleteTriggers: [multiCharTrigger],
+          ),
+        ),
+      );
+    
+      await tester.enterText(find.byType(TextFormField), '##');
+      await tester.pumpAndSettle(kDebounceDuration);
+      expect(find.byType(ListView), findsOneWidget);
+      
+      await tester.tap(find.byType(InkWell).first);
+      await tester.pump();
+    
+      final TextFormField field = 
+        find.byType(TextFormField).evaluate().first.widget as TextFormField;
+      
+      expect(field.controller!.text, isNot(contains('##')));
+    });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
testWidgets('handles multi-character triggers with keepTrigger=false', (tester) async {
final multiCharTrigger = AutocompleteTrigger(
trigger: '##'
optionsViewBuilder: (context, query, controller) { },
);
await tester.pumpWidget(
Boilerplate(
child: MultiTriggerAutocomplete(
autocompleteTriggers: [multiCharTrigger],
),
),
);
await tester.enterText(find.byType(TextFormField), '##');
await tester.tap(find.byType(InkWell).first);
await tester.pump();
final TextFormField field =
find.byType(TextFormField).evaluate().first.widget as TextFormField;
expect(field.controller!.text, isNot(contains('##')));
});
testWidgets('handles multi-character triggers with keepTrigger=false', (tester) async {
final multiCharTrigger = AutocompleteTrigger(
trigger: '##',
optionsViewBuilder: (context, query, controller) {
return ListView.builder(
itemCount: 1,
itemBuilder: (context, index) {
return ListTile(
title: const Text('Option'),
onTap: () {
final autocomplete = MultiTriggerAutocomplete.of(context);
return autocomplete.acceptAutocompleteOption('Option');
},
);
},
);
},
);
await tester.pumpWidget(
Boilerplate(
child: MultiTriggerAutocomplete(
debounceDuration: kDebounceDuration,
autocompleteTriggers: [multiCharTrigger],
),
),
);
await tester.enterText(find.byType(TextFormField), '##');
await tester.pumpAndSettle(kDebounceDuration);
expect(find.byType(ListView), findsOneWidget);
await tester.tap(find.byType(InkWell).first);
await tester.pump();
final TextFormField field =
find.byType(TextFormField).evaluate().first.widget as TextFormField;
expect(field.controller!.text, isNot(contains('##')));
});


testWidgets('cursor position correct after autocomplete with multi-character trigger', (tester) async {
final multiCharTrigger = AutocompleteTrigger(
trigger: '@@',
optionsViewBuilder: (context, query, controller) { },
);

await tester.pumpWidget(
Boilerplate(
child: MultiTriggerAutocomplete(
autocompleteTriggers: [multiCharTrigger],
),
),
);

await tester.enterText(find.byType(TextFormField), '@@sa');

await tester.tap(find.byType(InkWell).first);
await tester.pump();

final TextFormField field =
find.byType(TextFormField).evaluate().first.widget as TextFormField;

expect(field.controller!.selection.baseOffset,
equals(field.controller!.text.length));
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Test has syntax errors and will fail at runtime.

This test has the same issues as the previous test:

  1. Syntax error on line 328: Missing comma after trigger: '@@'
  2. Empty optionsViewBuilder on line 329: Must return a Widget like ListView with tappable items.
  3. Line 342 will fail: Attempting to tap a non-existent InkWell.
  4. Missing debounce wait: After line 340's enterText, add await tester.pumpAndSettle(kDebounceDuration);.
  5. No verification of options: Should verify ListView is shown before tapping.

Apply this diff to fix the test:

-    testWidgets('cursor position correct after autocomplete with multi-character trigger', (tester) async {
-      final multiCharTrigger = AutocompleteTrigger(
-        trigger: '@@',
-        optionsViewBuilder: (context, query, controller) { },
-      );
-    
-      await tester.pumpWidget(
-        Boilerplate(
-          child: MultiTriggerAutocomplete(
-            autocompleteTriggers: [multiCharTrigger],
-          ),
-        ),
-      );
-    
-      await tester.enterText(find.byType(TextFormField), '@@sa');
-      
-      await tester.tap(find.byType(InkWell).first);
-      await tester.pump();
-    
-      final TextFormField field = 
-        find.byType(TextFormField).evaluate().first.widget as TextFormField;
-      
-      expect(field.controller!.selection.baseOffset, 
-             equals(field.controller!.text.length));
-    });
+    testWidgets('cursor position correct after autocomplete with multi-character trigger', (tester) async {
+      final multiCharTrigger = AutocompleteTrigger(
+        trigger: '@@',
+        optionsViewBuilder: (context, query, controller) {
+          return ListView.builder(
+            itemCount: 1,
+            itemBuilder: (context, index) {
+              return ListTile(
+                title: const Text('Option'),
+                onTap: () {
+                  final autocomplete = MultiTriggerAutocomplete.of(context);
+                  return autocomplete.acceptAutocompleteOption('Option');
+                },
+              );
+            },
+          );
+        },
+      );
+    
+      await tester.pumpWidget(
+        Boilerplate(
+          child: MultiTriggerAutocomplete(
+            debounceDuration: kDebounceDuration,
+            autocompleteTriggers: [multiCharTrigger],
+          ),
+        ),
+      );
+    
+      await tester.enterText(find.byType(TextFormField), '@@sa');
+      await tester.pumpAndSettle(kDebounceDuration);
+      expect(find.byType(ListView), findsOneWidget);
+      
+      await tester.tap(find.byType(InkWell).first);
+      await tester.pump();
+    
+      final TextFormField field = 
+        find.byType(TextFormField).evaluate().first.widget as TextFormField;
+      
+      expect(field.controller!.selection.baseOffset, 
+             equals(field.controller!.text.length));
+    });

});
}

Expand Down