Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c6a9da4
Add async autocomplete sample tests
shai-almog Nov 27, 2025
5c8ab5d
Refine async autocomplete test scheduling
shai-almog Nov 27, 2025
3616641
Adjust async autocomplete test filtering triggers
shai-almog Nov 27, 2025
99ac4b5
Simplify async autocomplete scheduling
shai-almog Nov 27, 2025
c4483c6
Trigger autocomplete filtering on text change
shai-almog Nov 27, 2025
fe4b033
Fix autocomplete async test imports
shai-almog Nov 27, 2025
722f945
Handle null text in async autocomplete filter
shai-almog Nov 27, 2025
7c8cd97
Guard autocomplete filter during initialization
shai-almog Nov 28, 2025
8d793a3
Sync async autocomplete filter with suggestion model
shai-almog Nov 28, 2025
4a1e616
Fix async autocomplete super filter call
shai-almog Nov 28, 2025
e9d1e2c
Trigger async filter on key press
shai-almog Nov 28, 2025
423c191
Add timeout to FormTest interceptor
shai-almog Nov 28, 2025
2ed01a2
Simplify async autocomplete filtering
shai-almog Nov 28, 2025
ae6a00c
Fix autocomplete async test input handling
shai-almog Nov 28, 2025
0c8f9d1
Fix autocomplete test current form lookup
shai-almog Nov 28, 2025
49e6d0b
Add missing DisplayTest import to autocomplete async test
shai-almog Nov 28, 2025
936a1b1
Rely on key dispatch for async autocomplete typing
shai-almog Nov 28, 2025
a5543b2
Revive async autocomplete test with delayed filtering
shai-almog Nov 28, 2025
2b6eefe
Simplify autocomplete sample test filtering
shai-almog Nov 29, 2025
b13ce23
Enable qwerty input for autocomplete async test
shai-almog Nov 29, 2025
050f503
Simplify async autocomplete test input handling
shai-almog Nov 29, 2025
8037c77
Fixed stupid codex bug
shai-almog Nov 29, 2025
738e5f3
Merge branch 'codex/port-autocompleteasynctest-to-unit-tests' of http…
shai-almog Nov 29, 2025
eeefd2c
Revert "Merge branch 'codex/port-autocompleteasynctest-to-unit-tests'…
shai-almog Nov 29, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

public class EDTTestInterceptor implements InvocationInterceptor {

private static final long DEFAULT_TIMEOUT_MILLIS = 5000;

@Override
public void interceptTestMethod(Invocation<Void> invocation,
ReflectiveInvocationContext<Method> ctx,
Expand All @@ -35,13 +37,36 @@ public void interceptAfterEachMethod(Invocation<Void> invocation,

private void runOnMyThread(Invocation<Void> invocation) throws Throwable {
AtomicReference<Throwable> thrown = new AtomicReference<>();
CN.callSeriallyAndWait(() -> {
final Object lock = new Object();
final boolean[] completed = new boolean[1];

CN.callSerially(() -> {
try {
invocation.proceed();
} catch (Throwable t) {
thrown.set(t);
}
synchronized (lock) {
completed[0] = true;
lock.notifyAll();
}
});

try {
synchronized (lock) {
if (!completed[0]) {
lock.wait(DEFAULT_TIMEOUT_MILLIS);
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw ie;
}

if (!completed[0]) {
throw new AssertionError("FormTest timed out after " + DEFAULT_TIMEOUT_MILLIS + "ms");
}

Throwable t = thrown.get();
if (t != null) throw t; // preserves the original stack trace
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package com.codename1.samples;

import com.codename1.junit.FormTest;
import com.codename1.junit.UITestBase;
import com.codename1.ui.AutoCompleteTextField;
import com.codename1.ui.CN;
import com.codename1.ui.Component;
import com.codename1.ui.DisplayTest;
import com.codename1.ui.Form;
import com.codename1.ui.TextField;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.ui.list.DefaultListModel;

import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;

class AutocompleteAsyncSampleTest extends UITestBase {

private static final String[] CITY_DATABASE = new String[]{
"Dubai",
"Dibba Al-Fujairah",
"Dibba Al-Hisn",
"Sharjah",
"Abu Dhabi",
"Ajman",
"Charikar",
"Kabul"
};

@FormTest
void asyncFilteringAddsPrefixMatchesAfterDelay() {
Form form = new Form("Async Autocomplete", BoxLayout.y());
AsyncAutoCompleteField field = createAsyncField();
form.add(field);

form.show();
startEditing(field);

typeText("Du");
waitForAsync(field);

List<String> suggestions = field.copySuggestions();
assertFalse(suggestions.isEmpty());
assertTrue(suggestions.contains("Dubai"));
}

@FormTest
void latestQueryCancelsPreviousPendingResult() {
Form form = new Form("Async Autocomplete", BoxLayout.y());
AsyncAutoCompleteField field = createAsyncField();
form.add(field);

form.show();
startEditing(field);

typeText("D");
typeText("i");
waitForAsync(field);

List<String> suggestions = field.copySuggestions();
assertTrue(suggestions.contains("Dibba Al-Fujairah"));
assertTrue(suggestions.contains("Dibba Al-Hisn"));
assertFalse(suggestions.contains("Dubai"));
}

@FormTest
void clearingTextRemovesSuggestions() {
Form form = new Form("Async Autocomplete", BoxLayout.y());
AsyncAutoCompleteField field = createAsyncField();
form.add(field);

form.show();
startEditing(field);

typeText("Ka");
waitForAsync(field);
assertFalse(field.copySuggestions().isEmpty());

field.setText("");
waitForAsync(field);

assertEquals(0, field.copySuggestions().size());
}

private AsyncAutoCompleteField createAsyncField() {
DefaultListModel<String> options = new DefaultListModel<String>();
AsyncAutoCompleteField field = new AsyncAutoCompleteField(options, CITY_DATABASE);
field.setQwertyInput(true);
return field;
}

private void typeText(String text) {
for (int i = 0; i < text.length(); i++) {
implementation.dispatchKeyPress(text.charAt(i));
}
}

private void waitForAsync(AsyncAutoCompleteField field) {
for (int i = 0; i < 25; i++) {
DisplayTest.flushEdt();
flushSerialCalls();
if (!field.hasPendingQuery()) {
break;
}
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
break;
}
}
DisplayTest.flushEdt();
flushSerialCalls();
}

private void startEditing(TextField textField) {
textField.startEditingAsync();
flushSerialCalls();
DisplayTest.flushEdt();
flushSerialCalls();

if (!implementation.isEditingText(textField)) {
textField.requestFocus();
flushSerialCalls();
DisplayTest.flushEdt();
flushSerialCalls();
}

if (!implementation.isEditingText(textField)) {
textField.startEditingAsync();
flushSerialCalls();
DisplayTest.flushEdt();
flushSerialCalls();
}
}

private static class AsyncAutoCompleteField extends AutoCompleteTextField {

private final DefaultListModel<String> options;
private final String[] database;
AsyncAutoCompleteField(DefaultListModel<String> options, String[] database) {
super(options);
this.options = options;
this.database = database;
}

@Override
protected boolean filter(final String textParam) {
String text = textParam == null ? "" : textParam;

options.removeAll();
if (text.length() > 0) {
String lower = text.toLowerCase();
for (int i = 0; i < database.length; i++) {
String city = database[i];
if (city.toLowerCase().startsWith(lower)) {
options.addItem(city);
}
}
}

super.filter(text);
updateFilterList();
return true;
}

List<String> copySuggestions() {
ArrayList<String> suggestions = new ArrayList<String>();
for (int i = 0; i < options.getSize(); i++) {
suggestions.add(options.getItemAt(i));
}
return suggestions;
}

boolean hasPendingQuery() {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1267,9 +1267,10 @@ public void dispatchKeyPress(final int keyCode) {
final boolean reenter = beginAllowingEditDuringKey(keyCode);
if (editing != null && shouldInsertCharacter(editing.isEditable(), keyCode)) {
insertCharacter(editing, (char) keyCode, editing.getMaxSize());
} else {
display.keyPressed(keyCode);
display.keyReleased(keyCode);
}
display.keyPressed(keyCode);
display.keyReleased(keyCode);
if (reenter) {
display.callSerially(new Runnable() {
public void run() {
Expand Down