Commit fcda521b3c6 for woocommerce
commit fcda521b3c6d32719a1454800e313ebe763662f6
Author: Seghir Nadir <nadir.seghir@gmail.com>
Date: Mon Sep 28 16:41:04 2026 +0100
Restore browser autofill when checkout address suggestions fail (#69101)
* Restore browser autofill after address suggestion failures
* Add changelog entry for address autocomplete fallback
diff --git a/plugins/woocommerce/changelog/wooplug-7787-address-autocomplete-error-fallback b/plugins/woocommerce/changelog/wooplug-7787-address-autocomplete-error-fallback
new file mode 100644
index 00000000000..fa24319fe40
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooplug-7787-address-autocomplete-error-fallback
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Restore browser autofill when checkout address suggestions fail or return no matches.
diff --git a/plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js b/plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js
index 3f2355f529b..5723145a65c 100644
--- a/plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js
+++ b/plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js
@@ -318,6 +318,10 @@
// Handle errors using shared function
handleApiError( data, response );
+ if ( ! response.ok ) {
+ return [];
+ }
+
if ( Array.isArray( data ) ) {
data = data.map( ( item ) => ( {
id: item.id,
@@ -329,6 +333,7 @@
cacheResult( inputValue, country, data );
return data;
}
+ return [];
} catch ( e ) {
if ( e.name === 'AbortError' ) {
// Ignore abort errors from cancelled requests
diff --git a/plugins/woocommerce/client/legacy/js/frontend/address-autocomplete.js b/plugins/woocommerce/client/legacy/js/frontend/address-autocomplete.js
index 394b9508a3d..baadb844bd8 100644
--- a/plugins/woocommerce/client/legacy/js/frontend/address-autocomplete.js
+++ b/plugins/woocommerce/client/legacy/js/frontend/address-autocomplete.js
@@ -462,6 +462,10 @@ if (
'Invalid suggestions response - not an array'
);
hideSuggestions( type );
+ enableBrowserAutofill(
+ addressInput,
+ document.activeElement === addressInput
+ );
return;
}
@@ -474,6 +478,10 @@ if (
if ( safeSuggestions.length === 0 ) {
hideSuggestions( type );
+ enableBrowserAutofill(
+ addressInput,
+ document.activeElement === addressInput
+ );
return;
}
diff --git a/plugins/woocommerce/client/legacy/js/frontend/test/a8c-address-autocomplete-service.js b/plugins/woocommerce/client/legacy/js/frontend/test/a8c-address-autocomplete-service.js
new file mode 100644
index 00000000000..b3d9a2ce953
--- /dev/null
+++ b/plugins/woocommerce/client/legacy/js/frontend/test/a8c-address-autocomplete-service.js
@@ -0,0 +1,56 @@
+/**
+ * @jest-environment jest-fixed-jsdom
+ */
+
+describe( 'Automattic address autocomplete service', () => {
+ let provider;
+
+ beforeEach( () => {
+ jest.useFakeTimers();
+ jest.resetModules();
+ global.a8cAddressAutocompleteServiceKeys = {
+ 'test-provider': { key: 'test-key', canTelemetry: false },
+ };
+ window.wc = {
+ addressAutocomplete: {
+ registerAddressAutocompleteProvider: ( registered ) => {
+ provider = registered;
+ },
+ },
+ };
+ require( '../a8c-address-autocomplete-service' );
+ } );
+
+ afterEach( () => {
+ jest.useRealTimers();
+ delete global.a8cAddressAutocompleteServiceKeys;
+ delete global.fetch;
+ delete window.wc;
+ } );
+
+ test.each( [ false, true ] )(
+ 'returns no suggestions without caching an invalid response (HTTP ok: %s)',
+ async ( ok ) => {
+ const suggestion = { id: 'address-1', label: '123 Main Street' };
+ global.fetch = jest
+ .fn()
+ .mockResolvedValueOnce( {
+ ok,
+ json: async () => ( { code: 'query_too_short' } ),
+ } )
+ .mockResolvedValueOnce( {
+ ok: true,
+ json: async () => [ suggestion ],
+ } );
+
+ const failedSearch = provider.search( '123', 'US' );
+ await jest.advanceTimersByTimeAsync( 300 );
+ expect( await failedSearch ).toEqual( [] );
+
+ const retriedSearch = provider.search( '123', 'US' );
+ await jest.advanceTimersByTimeAsync( 300 );
+ expect( await retriedSearch ).toEqual( [ suggestion ] );
+ expect( global.fetch ).toHaveBeenCalledTimes( 2 );
+ }
+ );
+} );
diff --git a/plugins/woocommerce/client/legacy/js/frontend/test/address-autocomplete.js b/plugins/woocommerce/client/legacy/js/frontend/test/address-autocomplete.js
index fdd416c62d3..a0a5947b7e2 100644
--- a/plugins/woocommerce/client/legacy/js/frontend/test/address-autocomplete.js
+++ b/plugins/woocommerce/client/legacy/js/frontend/test/address-autocomplete.js
@@ -1263,6 +1263,38 @@ describe( 'Address Suggestions Component', () => {
'false'
);
} );
+
+ test.each( [
+ [ 'no suggestions', [] ],
+ [ 'an invalid response', undefined ],
+ ] )(
+ 'should restore browser autofill without moving focus after %s',
+ async ( _, result ) => {
+ const consoleSpy = jest
+ .spyOn( console, 'error' )
+ .mockImplementation( () => {} );
+ billingAddressInput.value = '123';
+ billingAddressInput.focus();
+ billingAddressInput.dispatchEvent( new Event( 'input' ) );
+ await new Promise( ( resolve ) => setTimeout( resolve, 150 ) );
+ expect(
+ billingAddressInput.getAttribute( 'autocomplete' )
+ ).toBe( 'none' );
+
+ mockProvider.search.mockResolvedValue( result );
+ billingAddressInput.value = '1234';
+ billingAddressInput.dispatchEvent( new Event( 'input' ) );
+ const cityInput = document.getElementById( 'billing_city' );
+ cityInput.focus();
+ await new Promise( ( resolve ) => setTimeout( resolve, 150 ) );
+
+ expect(
+ billingAddressInput.getAttribute( 'autocomplete' )
+ ).toBe( 'address-line1' );
+ expect( document.activeElement ).toBe( cityInput );
+ consoleSpy.mockRestore();
+ }
+ );
} );
describe( 'Security and Sanitization', () => {