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', () => {