| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.autofillservice.cts; |
| |
| import static android.autofillservice.cts.CannedFillResponse.DO_NOT_REPLY_RESPONSE; |
| import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE; |
| import static android.autofillservice.cts.CheckoutActivity.ID_CC_NUMBER; |
| import static android.autofillservice.cts.Helper.ID_PASSWORD; |
| import static android.autofillservice.cts.Helper.ID_USERNAME; |
| import static android.autofillservice.cts.Helper.NULL_DATASET_ID; |
| import static android.autofillservice.cts.Helper.assertDeprecatedClientState; |
| import static android.autofillservice.cts.Helper.assertFillEventForAuthenticationSelected; |
| import static android.autofillservice.cts.Helper.assertFillEventForDatasetAuthenticationSelected; |
| import static android.autofillservice.cts.Helper.assertFillEventForDatasetSelected; |
| import static android.autofillservice.cts.Helper.assertFillEventForSaveShown; |
| import static android.autofillservice.cts.Helper.assertNoDeprecatedClientState; |
| import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected; |
| import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected; |
| import static android.autofillservice.cts.LoginActivity.BACKDOOR_USERNAME; |
| import static android.autofillservice.cts.LoginActivity.getWelcomeMessage; |
| import static android.service.autofill.FillEventHistory.Event.TYPE_CONTEXT_COMMITTED; |
| import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC; |
| import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.truth.Truth.assertWithMessage; |
| |
| import android.autofillservice.cts.CannedFillResponse.CannedDataset; |
| import android.content.Intent; |
| import android.content.IntentSender; |
| import android.os.Bundle; |
| import android.platform.test.annotations.AppModeFull; |
| import android.service.autofill.FillEventHistory; |
| import android.service.autofill.FillEventHistory.Event; |
| import android.service.autofill.FillResponse; |
| import android.support.test.uiautomator.UiObject2; |
| import android.view.View; |
| import android.view.autofill.AutofillId; |
| |
| import com.google.common.collect.ImmutableMap; |
| |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| /** |
| * Test that uses {@link LoginActivity} to test {@link FillEventHistory}. |
| */ |
| @AppModeFull // Service-specific test |
| public class FillEventHistoryTest extends AutoFillServiceTestCase { |
| |
| @Rule |
| public final AutofillActivityTestRule<LoginActivity> mActivityRule = |
| new AutofillActivityTestRule<LoginActivity>(LoginActivity.class); |
| |
| private LoginActivity mActivity; |
| |
| @Before |
| public void setActivity() { |
| mActivity = mActivityRule.getActivity(); |
| } |
| |
| @Test |
| public void testDatasetAuthenticationSelected() throws Exception { |
| enableService(); |
| |
| // Set up FillResponse with dataset authentication |
| Bundle clientState = new Bundle(); |
| clientState.putCharSequence("clientStateKey", "clientStateValue"); |
| |
| // Prepare the authenticated response |
| final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1, |
| new CannedDataset.Builder() |
| .setField(ID_USERNAME, "dude") |
| .setField(ID_PASSWORD, "sweet") |
| .setPresentation(createPresentation("Dataset")) |
| .build()); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setField(ID_USERNAME, "username") |
| .setId("name") |
| .setPresentation(createPresentation("authentication")) |
| .setAuthentication(authentication) |
| .build()) |
| .setExtras(clientState).build()); |
| mActivity.expectAutoFill("dude", "sweet"); |
| |
| // Trigger autofill. |
| mActivity.onUsername(View::requestFocus); |
| |
| // Authenticate |
| sReplier.getNextFillRequest(); |
| mUiBot.selectDataset("authentication"); |
| mActivity.assertAutoFilled(); |
| |
| // Verify fill selection |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| assertFillEventForDatasetAuthenticationSelected(events.get(0), "name", |
| "clientStateKey", "clientStateValue"); |
| } |
| |
| @Test |
| public void testAuthenticationSelected() throws Exception { |
| enableService(); |
| |
| // Set up FillResponse with response wide authentication |
| Bundle clientState = new Bundle(); |
| clientState.putCharSequence("clientStateKey", "clientStateValue"); |
| |
| // Prepare the authenticated response |
| final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1, |
| new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setField(ID_USERNAME, "username") |
| .setId("name") |
| .setPresentation(createPresentation("dataset")) |
| .build()) |
| .setExtras(clientState).build()); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder().setExtras(clientState) |
| .setPresentation(createPresentation("authentication")) |
| .setAuthentication(authentication, ID_USERNAME) |
| .build()); |
| |
| // Trigger autofill. |
| mActivity.onUsername(View::requestFocus); |
| |
| // Authenticate |
| sReplier.getNextFillRequest(); |
| mUiBot.selectDataset("authentication"); |
| mUiBot.assertDatasets("dataset"); |
| |
| // Verify fill selection |
| final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1); |
| assertDeprecatedClientState(selection, "clientStateKey", "clientStateValue"); |
| List<Event> events = selection.getEvents(); |
| assertFillEventForAuthenticationSelected(events.get(0), NULL_DATASET_ID, |
| "clientStateKey", "clientStateValue"); |
| } |
| |
| @Test |
| public void testDatasetSelected_twoResponses() throws Exception { |
| enableService(); |
| |
| // Set up first partition with an anonymous dataset |
| Bundle clientState1 = new Bundle(); |
| clientState1.putCharSequence("clientStateKey", "Value1"); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setField(ID_USERNAME, "username") |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .setExtras(clientState1) |
| .build()); |
| mActivity.expectAutoFill("username"); |
| |
| // Trigger autofill on username |
| mActivity.onUsername(View::requestFocus); |
| waitUntilConnected(); |
| sReplier.getNextFillRequest(); |
| mUiBot.selectDataset("dataset1"); |
| mActivity.assertAutoFilled(); |
| |
| { |
| // Verify fill selection |
| final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1); |
| assertDeprecatedClientState(selection, "clientStateKey", "Value1"); |
| final List<Event> events = selection.getEvents(); |
| assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID, |
| "clientStateKey", "Value1"); |
| } |
| |
| // Set up second partition with a named dataset |
| Bundle clientState2 = new Bundle(); |
| clientState2.putCharSequence("clientStateKey", "Value2"); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder() |
| .addDataset( |
| new CannedDataset.Builder() |
| .setField(ID_PASSWORD, "password2") |
| .setPresentation(createPresentation("dataset2")) |
| .setId("name2") |
| .build()) |
| .addDataset( |
| new CannedDataset.Builder() |
| .setField(ID_PASSWORD, "password3") |
| .setPresentation(createPresentation("dataset3")) |
| .setId("name3") |
| .build()) |
| .setExtras(clientState2) |
| .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_PASSWORD).build()); |
| mActivity.expectPasswordAutoFill("password3"); |
| |
| // Trigger autofill on password |
| mActivity.onPassword(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| mUiBot.selectDataset("dataset3"); |
| mActivity.assertAutoFilled(); |
| |
| { |
| // Verify fill selection |
| final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1); |
| assertDeprecatedClientState(selection, "clientStateKey", "Value2"); |
| final List<Event> events = selection.getEvents(); |
| assertFillEventForDatasetSelected(events.get(0), "name3", |
| "clientStateKey", "Value2"); |
| } |
| |
| mActivity.onPassword((v) -> v.setText("new password")); |
| mActivity.syncRunOnUiThread(() -> mActivity.finish()); |
| waitUntilDisconnected(); |
| |
| { |
| // Verify fill selection |
| final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2); |
| assertDeprecatedClientState(selection, "clientStateKey", "Value2"); |
| |
| final List<Event> events = selection.getEvents(); |
| assertFillEventForDatasetSelected(events.get(0), "name3", |
| "clientStateKey", "Value2"); |
| assertFillEventForSaveShown(events.get(1), NULL_DATASET_ID, |
| "clientStateKey", "Value2"); |
| } |
| } |
| |
| @Test |
| public void testNoEvents_whenServiceReturnsNullResponse() throws Exception { |
| enableService(); |
| |
| // First reset |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setField(ID_USERNAME, "username") |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .build()); |
| mActivity.expectAutoFill("username"); |
| |
| mActivity.onUsername(View::requestFocus); |
| waitUntilConnected(); |
| sReplier.getNextFillRequest(); |
| mUiBot.selectDataset("dataset1"); |
| mActivity.assertAutoFilled(); |
| |
| { |
| // Verify fill selection |
| final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1); |
| assertNoDeprecatedClientState(selection); |
| final List<Event> events = selection.getEvents(); |
| assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID); |
| } |
| |
| // Second request |
| sReplier.addResponse(NO_RESPONSE); |
| mActivity.onPassword(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| mUiBot.assertNoDatasets(); |
| waitUntilDisconnected(); |
| |
| InstrumentedAutoFillService.assertNoFillEventHistory(); |
| } |
| |
| @Test |
| public void testNoEvents_whenServiceReturnsFailure() throws Exception { |
| enableService(); |
| |
| // First reset |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setField(ID_USERNAME, "username") |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .build()); |
| mActivity.expectAutoFill("username"); |
| |
| mActivity.onUsername(View::requestFocus); |
| waitUntilConnected(); |
| sReplier.getNextFillRequest(); |
| mUiBot.selectDataset("dataset1"); |
| mActivity.assertAutoFilled(); |
| |
| { |
| // Verify fill selection |
| final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1); |
| assertNoDeprecatedClientState(selection); |
| final List<Event> events = selection.getEvents(); |
| assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID); |
| } |
| |
| // Second request |
| sReplier.addResponse(new CannedFillResponse.Builder().returnFailure("D'OH!").build()); |
| mActivity.onPassword(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| mUiBot.assertNoDatasets(); |
| waitUntilDisconnected(); |
| |
| InstrumentedAutoFillService.assertNoFillEventHistory(); |
| } |
| |
| @Test |
| public void testNoEvents_whenServiceTimesout() throws Exception { |
| enableService(); |
| |
| // First reset |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setField(ID_USERNAME, "username") |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .build()); |
| mActivity.expectAutoFill("username"); |
| |
| mActivity.onUsername(View::requestFocus); |
| waitUntilConnected(); |
| sReplier.getNextFillRequest(); |
| mUiBot.selectDataset("dataset1"); |
| mActivity.assertAutoFilled(); |
| |
| { |
| // Verify fill selection |
| final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1); |
| assertNoDeprecatedClientState(selection); |
| final List<Event> events = selection.getEvents(); |
| assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID); |
| } |
| |
| // Second request |
| sReplier.addResponse(DO_NOT_REPLY_RESPONSE); |
| mActivity.onPassword(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| waitUntilDisconnected(); |
| |
| InstrumentedAutoFillService.assertNoFillEventHistory(); |
| } |
| |
| private Bundle getBundle(String key, String value) { |
| final Bundle bundle = new Bundle(); |
| bundle.putString(key, value); |
| return bundle; |
| } |
| |
| /** |
| * Tests the following scenario: |
| * |
| * <ol> |
| * <li>Activity A is launched. |
| * <li>Activity A triggers autofill. |
| * <li>Activity B is launched. |
| * <li>Activity B triggers autofill. |
| * <li>User goes back to Activity A. |
| * <li>User triggers save on Activity A - at this point, service should have stats of |
| * activity B, and stats for activity A should have beeen discarded. |
| * </ol> |
| */ |
| @Test |
| public void testEventsFromPreviousSessionIsDiscarded() throws Exception { |
| enableService(); |
| |
| // Launch activity A |
| sReplier.addResponse(new CannedFillResponse.Builder() |
| .setExtras(getBundle("activity", "A")) |
| .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD) |
| .build()); |
| |
| // Trigger autofill on activity A |
| mActivity.onUsername(View::requestFocus); |
| waitUntilConnected(); |
| sReplier.getNextFillRequest(); |
| |
| // Verify fill selection for Activity A |
| final FillEventHistory selectionA = InstrumentedAutoFillService.getFillEventHistory(0); |
| assertDeprecatedClientState(selectionA, "activity", "A"); |
| |
| // Launch activity B |
| mActivity.startActivity(new Intent(mActivity, CheckoutActivity.class)); |
| mUiBot.assertShownByRelativeId(ID_CC_NUMBER); |
| |
| // Trigger autofill on activity B |
| sReplier.addResponse(new CannedFillResponse.Builder() |
| .setExtras(getBundle("activity", "B")) |
| .addDataset(new CannedDataset.Builder() |
| .setField(ID_CC_NUMBER, "4815162342") |
| .setPresentation(createPresentation("datasetB")) |
| .build()) |
| .build()); |
| mUiBot.focusByRelativeId(ID_CC_NUMBER); |
| sReplier.getNextFillRequest(); |
| |
| // Verify fill selection for Activity B |
| final FillEventHistory selectionB = InstrumentedAutoFillService.getFillEventHistory(0); |
| assertDeprecatedClientState(selectionB, "activity", "B"); |
| |
| // Now switch back to A... |
| mUiBot.pressBack(); // dismiss autofill |
| mUiBot.pressBack(); // dismiss keyboard (or task, if there was no keyboard) |
| final AtomicBoolean focusOnA = new AtomicBoolean(); |
| mActivity.syncRunOnUiThread(() -> focusOnA.set(mActivity.hasWindowFocus())); |
| if (!focusOnA.get()) { |
| mUiBot.pressBack(); // dismiss task, if the last pressBack dismissed only the keyboard |
| } |
| mUiBot.assertShownByRelativeId(ID_USERNAME); |
| assertWithMessage("root window has no focus") |
| .that(mActivity.getWindow().getDecorView().hasWindowFocus()).isTrue(); |
| |
| // ...and trigger save |
| // Set credentials... |
| mActivity.onUsername((v) -> v.setText("malkovich")); |
| mActivity.onPassword((v) -> v.setText("malkovich")); |
| final String expectedMessage = getWelcomeMessage("malkovich"); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD); |
| sReplier.getNextSaveRequest(); |
| |
| // Finally, make sure history is right |
| final FillEventHistory finalSelection = InstrumentedAutoFillService.getFillEventHistory(0); |
| assertDeprecatedClientState(finalSelection, "activity", "B"); |
| } |
| |
| @Test |
| public void testContextCommitted_whenServiceDidntDoAnything() throws Exception { |
| enableService(); |
| |
| sReplier.addResponse(CannedFillResponse.NO_RESPONSE); |
| |
| // Trigger autofill on username |
| mActivity.onUsername(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| mUiBot.assertNoDatasetsEver(); |
| |
| // Trigger save |
| mActivity.onUsername((v) -> v.setText("malkovich")); |
| mActivity.onPassword((v) -> v.setText("malkovich")); |
| final String expectedMessage = getWelcomeMessage("malkovich"); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); |
| |
| // Assert no events where generated |
| InstrumentedAutoFillService.assertNoFillEventHistory(); |
| } |
| |
| @Test |
| public void textContextCommitted_withoutDatasets() throws Exception { |
| enableService(); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder() |
| .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED) |
| .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD) |
| .build()); |
| |
| // Trigger autofill on username |
| mActivity.onUsername(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| mUiBot.assertNoDatasetsEver(); |
| |
| // Trigger save |
| mActivity.onUsername((v) -> v.setText("malkovich")); |
| mActivity.onPassword((v) -> v.setText("malkovich")); |
| final String expectedMessage = getWelcomeMessage("malkovich"); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD); |
| sReplier.getNextSaveRequest(); |
| |
| // Assert it |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| assertFillEventForSaveShown(events.get(0), NULL_DATASET_ID); |
| } |
| |
| @Test |
| public void testContextCommitted_withoutFlagOnLastResponse() throws Exception { |
| enableService(); |
| // Trigger 1st autofill request |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setId("id1") |
| .setField(ID_USERNAME, BACKDOOR_USERNAME) |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED) |
| .build()); |
| mActivity.expectAutoFill(BACKDOOR_USERNAME); |
| mActivity.onUsername(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| mUiBot.selectDataset("dataset1"); |
| mActivity.assertAutoFilled(); |
| // Verify fill history |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| assertFillEventForDatasetSelected(events.get(0), "id1"); |
| } |
| |
| // Trigger 2st autofill request (which will clear the fill event history) |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setId("id2") |
| .setField(ID_PASSWORD, "whatever") |
| .setPresentation(createPresentation("dataset2")) |
| .build()) |
| // don't set flags |
| .build()); |
| mActivity.expectPasswordAutoFill("whatever"); |
| mActivity.onPassword(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| mUiBot.selectDataset("dataset2"); |
| mActivity.assertAutoFilled(); |
| // Verify fill history |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| assertFillEventForDatasetSelected(events.get(0), "id2"); |
| } |
| |
| // Finish the context by login in |
| final String expectedMessage = getWelcomeMessage(BACKDOOR_USERNAME); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); |
| |
| { |
| // Verify fill history |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| |
| assertFillEventForDatasetSelected(events.get(0), "id2"); |
| } |
| } |
| |
| @Test |
| public void testContextCommitted_idlessDatasets() throws Exception { |
| enableService(); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setField(ID_USERNAME, "username1") |
| .setField(ID_PASSWORD, "password1") |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .addDataset(new CannedDataset.Builder() |
| .setField(ID_USERNAME, "username2") |
| .setField(ID_PASSWORD, "password2") |
| .setPresentation(createPresentation("dataset2")) |
| .build()) |
| .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED) |
| .build()); |
| mActivity.expectAutoFill("username1", "password1"); |
| |
| // Trigger autofill on username |
| mActivity.onUsername(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| |
| final UiObject2 datasetPicker = mUiBot.assertDatasets("dataset1", "dataset2"); |
| mUiBot.selectDataset(datasetPicker, "dataset1"); |
| mActivity.assertAutoFilled(); |
| |
| // Verify dataset selection |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID); |
| } |
| |
| // Finish the context by login in |
| mActivity.onUsername((v) -> v.setText("USERNAME")); |
| mActivity.onPassword((v) -> v.setText("USERNAME")); |
| |
| final String expectedMessage = getWelcomeMessage("USERNAME"); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); |
| |
| // ...and check again |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID); |
| } |
| } |
| |
| @Test |
| public void testContextCommitted_idlessDatasetSelected_datasetWithIdIgnored() |
| throws Exception { |
| enableService(); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setField(ID_USERNAME, "username1") |
| .setField(ID_PASSWORD, "password1") |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .addDataset(new CannedDataset.Builder() |
| .setId("id2") |
| .setField(ID_USERNAME, "username2") |
| .setField(ID_PASSWORD, "password2") |
| .setPresentation(createPresentation("dataset2")) |
| .build()) |
| .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED) |
| .build()); |
| mActivity.expectAutoFill("username1", "password1"); |
| |
| // Trigger autofill on username |
| mActivity.onUsername(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| |
| final UiObject2 datasetPicker = mUiBot.assertDatasets("dataset1", "dataset2"); |
| mUiBot.selectDataset(datasetPicker, "dataset1"); |
| mActivity.assertAutoFilled(); |
| |
| // Verify dataset selection |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID); |
| } |
| |
| // Finish the context by login in |
| mActivity.onPassword((v) -> v.setText("username1")); |
| |
| final String expectedMessage = getWelcomeMessage("username1"); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); |
| |
| // ...and check again |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(2); |
| assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID); |
| |
| FillEventHistory.Event event2 = events.get(1); |
| assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED); |
| assertThat(event2.getDatasetId()).isNull(); |
| assertThat(event2.getClientState()).isNull(); |
| assertThat(event2.getSelectedDatasetIds()).isEmpty(); |
| assertThat(event2.getIgnoredDatasetIds()).containsExactly("id2"); |
| final AutofillId passwordId = mActivity.getPassword().getAutofillId(); |
| final Map<AutofillId, String> changedFields = event2.getChangedFields(); |
| assertThat(changedFields).containsExactly(passwordId, "id2"); |
| assertThat(event2.getManuallyEnteredField()).isEmpty(); |
| } |
| } |
| |
| @Test |
| public void testContextCommitted_idlessDatasetIgnored_datasetWithIdSelected() |
| throws Exception { |
| enableService(); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setField(ID_USERNAME, "username1") |
| .setField(ID_PASSWORD, "password1") |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .addDataset(new CannedDataset.Builder() |
| .setId("id2") |
| .setField(ID_USERNAME, "username2") |
| .setField(ID_PASSWORD, "password2") |
| .setPresentation(createPresentation("dataset2")) |
| .build()) |
| .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED) |
| .build()); |
| mActivity.expectAutoFill("username2", "password2"); |
| |
| // Trigger autofill on username |
| mActivity.onUsername(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| |
| final UiObject2 datasetPicker = mUiBot.assertDatasets("dataset1", "dataset2"); |
| mUiBot.selectDataset(datasetPicker, "dataset2"); |
| mActivity.assertAutoFilled(); |
| |
| // Verify dataset selection |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| assertFillEventForDatasetSelected(events.get(0), "id2"); |
| } |
| |
| // Finish the context by login in |
| mActivity.onPassword((v) -> v.setText("username2")); |
| |
| final String expectedMessage = getWelcomeMessage("username2"); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); |
| |
| // ...and check again |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(2); |
| assertFillEventForDatasetSelected(events.get(0), "id2"); |
| |
| final FillEventHistory.Event event2 = events.get(1); |
| assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED); |
| assertThat(event2.getDatasetId()).isNull(); |
| assertThat(event2.getClientState()).isNull(); |
| assertThat(event2.getSelectedDatasetIds()).containsExactly("id2"); |
| assertThat(event2.getIgnoredDatasetIds()).isEmpty(); |
| final AutofillId passwordId = mActivity.getPassword().getAutofillId(); |
| final Map<AutofillId, String> changedFields = event2.getChangedFields(); |
| assertThat(changedFields).containsExactly(passwordId, "id2"); |
| assertThat(event2.getManuallyEnteredField()).isEmpty(); |
| } |
| } |
| |
| /** |
| * Tests scenario where the context was committed, no dataset was selected by the user, |
| * neither the user entered values that were present in these datasets. |
| */ |
| @Test |
| public void testContextCommitted_noDatasetSelected_valuesNotManuallyEntered() throws Exception { |
| enableService(); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setId("id1") |
| .setField(ID_USERNAME, "username1") |
| .setField(ID_PASSWORD, "password1") |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .addDataset(new CannedDataset.Builder() |
| .setId("id2") |
| .setField(ID_USERNAME, "username2") |
| .setField(ID_PASSWORD, "password2") |
| .setPresentation(createPresentation("dataset2")) |
| .build()) |
| .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED) |
| .build()); |
| // Trigger autofill on username |
| mActivity.onUsername(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| mUiBot.assertDatasets("dataset1", "dataset2"); |
| |
| // Verify history |
| InstrumentedAutoFillService.getFillEventHistory(0); |
| |
| // Enter values not present at the datasets |
| mActivity.onUsername((v) -> v.setText("USERNAME")); |
| mActivity.onPassword((v) -> v.setText("USERNAME")); |
| |
| // Finish the context by login in |
| final String expectedMessage = getWelcomeMessage("USERNAME"); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); |
| |
| // Verify history again |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| final Event event = events.get(0); |
| assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED); |
| assertThat(event.getDatasetId()).isNull(); |
| assertThat(event.getClientState()).isNull(); |
| assertThat(event.getIgnoredDatasetIds()).containsExactly("id1", "id2"); |
| assertThat(event.getChangedFields()).isEmpty(); |
| assertThat(event.getManuallyEnteredField()).isEmpty(); |
| } |
| } |
| |
| /** |
| * Tests scenario where the context was committed, just one dataset was selected by the user, |
| * and the user changed the values provided by the service. |
| */ |
| @Test |
| public void testContextCommitted_oneDatasetSelected() throws Exception { |
| enableService(); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setId("id1") |
| .setField(ID_USERNAME, "username1") |
| .setField(ID_PASSWORD, "password1") |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .addDataset(new CannedDataset.Builder() |
| .setId("id2") |
| .setField(ID_USERNAME, "username2") |
| .setField(ID_PASSWORD, "password2") |
| .setPresentation(createPresentation("dataset2")) |
| .build()) |
| .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED) |
| .build()); |
| mActivity.expectAutoFill("username1", "password1"); |
| |
| // Trigger autofill on username |
| mActivity.onUsername(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| |
| final UiObject2 datasetPicker = mUiBot.assertDatasets("dataset1", "dataset2"); |
| mUiBot.selectDataset(datasetPicker, "dataset1"); |
| mActivity.assertAutoFilled(); |
| |
| // Verify dataset selection |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| assertFillEventForDatasetSelected(events.get(0), "id1"); |
| } |
| |
| // Finish the context by login in |
| mActivity.onUsername((v) -> v.setText("USERNAME")); |
| mActivity.onPassword((v) -> v.setText("USERNAME")); |
| |
| final String expectedMessage = getWelcomeMessage("USERNAME"); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); |
| |
| // ...and check again |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(2); |
| assertFillEventForDatasetSelected(events.get(0), "id1"); |
| |
| final FillEventHistory.Event event2 = events.get(1); |
| assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED); |
| assertThat(event2.getDatasetId()).isNull(); |
| assertThat(event2.getClientState()).isNull(); |
| assertThat(event2.getSelectedDatasetIds()).containsExactly("id1"); |
| assertThat(event2.getIgnoredDatasetIds()).containsExactly("id2"); |
| final Map<AutofillId, String> changedFields = event2.getChangedFields(); |
| final AutofillId usernameId = mActivity.getUsername().getAutofillId(); |
| final AutofillId passwordId = mActivity.getPassword().getAutofillId(); |
| assertThat(changedFields).containsExactlyEntriesIn( |
| ImmutableMap.of(usernameId, "id1", passwordId, "id1")); |
| assertThat(event2.getManuallyEnteredField()).isEmpty(); |
| } |
| } |
| |
| /** |
| * Tests scenario where the context was committed, both datasets were selected by the user, |
| * and the user changed the values provided by the service. |
| */ |
| @Test |
| public void testContextCommitted_multipleDatasetsSelected() throws Exception { |
| enableService(); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setId("id1") |
| .setField(ID_USERNAME, "username") |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .addDataset(new CannedDataset.Builder() |
| .setId("id2") |
| .setField(ID_PASSWORD, "password") |
| .setPresentation(createPresentation("dataset2")) |
| .build()) |
| .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED) |
| .build()); |
| mActivity.expectAutoFill("username"); |
| |
| // Trigger autofill |
| mActivity.onUsername(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| |
| // Autofill username |
| mUiBot.selectDataset("dataset1"); |
| mActivity.assertAutoFilled(); |
| { |
| // Verify fill history |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| assertFillEventForDatasetSelected(events.get(0), "id1"); |
| } |
| |
| // Autofill password |
| mActivity.expectPasswordAutoFill("password"); |
| |
| mActivity.onPassword(View::requestFocus); |
| mUiBot.selectDataset("dataset2"); |
| mActivity.assertAutoFilled(); |
| |
| { |
| // Verify fill history |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(2); |
| |
| assertFillEventForDatasetSelected(events.get(0), "id1"); |
| assertFillEventForDatasetSelected(events.get(1), "id2"); |
| } |
| |
| // Finish the context by login in |
| mActivity.onPassword((v) -> v.setText("username")); |
| |
| final String expectedMessage = getWelcomeMessage("username"); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); |
| |
| { |
| // Verify fill history |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(3); |
| |
| assertFillEventForDatasetSelected(events.get(0), "id1"); |
| assertFillEventForDatasetSelected(events.get(1), "id2"); |
| |
| final FillEventHistory.Event event3 = events.get(2); |
| assertThat(event3.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED); |
| assertThat(event3.getDatasetId()).isNull(); |
| assertThat(event3.getClientState()).isNull(); |
| assertThat(event3.getSelectedDatasetIds()).containsExactly("id1", "id2"); |
| assertThat(event3.getIgnoredDatasetIds()).isEmpty(); |
| final Map<AutofillId, String> changedFields = event3.getChangedFields(); |
| final AutofillId passwordId = mActivity.getPassword().getAutofillId(); |
| assertThat(changedFields).containsExactly(passwordId, "id2"); |
| assertThat(event3.getManuallyEnteredField()).isEmpty(); |
| } |
| } |
| |
| /** |
| * Tests scenario where the context was committed, both datasets were selected by the user, |
| * and the user didn't change the values provided by the service. |
| */ |
| @Test |
| public void testContextCommitted_multipleDatasetsSelected_butNotChanged() throws Exception { |
| enableService(); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setId("id1") |
| .setField(ID_USERNAME, BACKDOOR_USERNAME) |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .addDataset(new CannedDataset.Builder() |
| .setId("id2") |
| .setField(ID_PASSWORD, "whatever") |
| .setPresentation(createPresentation("dataset2")) |
| .build()) |
| .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED) |
| .build()); |
| mActivity.expectAutoFill(BACKDOOR_USERNAME); |
| |
| // Trigger autofill |
| mActivity.onUsername(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| |
| // Autofill username |
| mUiBot.selectDataset("dataset1"); |
| mActivity.assertAutoFilled(); |
| { |
| // Verify fill history |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| assertFillEventForDatasetSelected(events.get(0), "id1"); |
| } |
| |
| // Autofill password |
| mActivity.expectPasswordAutoFill("whatever"); |
| |
| mActivity.onPassword(View::requestFocus); |
| mUiBot.selectDataset("dataset2"); |
| mActivity.assertAutoFilled(); |
| |
| { |
| // Verify fill history |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(2); |
| |
| assertFillEventForDatasetSelected(events.get(0), "id1"); |
| assertFillEventForDatasetSelected(events.get(1), "id2"); |
| } |
| |
| // Finish the context by login in |
| final String expectedMessage = getWelcomeMessage(BACKDOOR_USERNAME); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); |
| |
| { |
| // Verify fill history |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(3); |
| |
| assertFillEventForDatasetSelected(events.get(0), "id1"); |
| assertFillEventForDatasetSelected(events.get(1), "id2"); |
| |
| final FillEventHistory.Event event3 = events.get(2); |
| assertThat(event3.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED); |
| assertThat(event3.getDatasetId()).isNull(); |
| assertThat(event3.getClientState()).isNull(); |
| assertThat(event3.getSelectedDatasetIds()).containsExactly("id1", "id2"); |
| assertThat(event3.getIgnoredDatasetIds()).isEmpty(); |
| assertThat(event3.getChangedFields()).isEmpty(); |
| assertThat(event3.getManuallyEnteredField()).isEmpty(); |
| } |
| } |
| |
| /** |
| * Tests scenario where the context was committed, the user selected the dataset, than changed |
| * the autofilled values, but then change the values again so they match what was provided by |
| * the service. |
| */ |
| @Test |
| public void testContextCommitted_oneDatasetSelected_Changed_thenChangedBack() |
| throws Exception { |
| enableService(); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setId("id1") |
| .setField(ID_USERNAME, "username") |
| .setField(ID_PASSWORD, "username") |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED) |
| .build()); |
| mActivity.expectAutoFill("username", "username"); |
| |
| // Trigger autofill on username |
| mActivity.onUsername(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| |
| mUiBot.selectDataset("dataset1"); |
| mActivity.assertAutoFilled(); |
| |
| // Verify dataset selection |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| assertFillEventForDatasetSelected(events.get(0), "id1"); |
| } |
| |
| // Change the fields to different values from datasets |
| mActivity.onUsername((v) -> v.setText("USERNAME")); |
| mActivity.onPassword((v) -> v.setText("USERNAME")); |
| |
| // Then change back to dataset values |
| mActivity.onUsername((v) -> v.setText("username")); |
| mActivity.onPassword((v) -> v.setText("username")); |
| |
| final String expectedMessage = getWelcomeMessage("username"); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); |
| |
| // ...and check again |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(2); |
| assertFillEventForDatasetSelected(events.get(0), "id1"); |
| |
| FillEventHistory.Event event2 = events.get(1); |
| assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED); |
| assertThat(event2.getDatasetId()).isNull(); |
| assertThat(event2.getClientState()).isNull(); |
| assertThat(event2.getSelectedDatasetIds()).containsExactly("id1"); |
| assertThat(event2.getIgnoredDatasetIds()).isEmpty(); |
| assertThat(event2.getChangedFields()).isEmpty(); |
| assertThat(event2.getManuallyEnteredField()).isEmpty(); |
| } |
| } |
| |
| /** |
| * Tests scenario where the context was committed, the user did not selected any dataset, but |
| * the user manually entered values that match what was provided by the service. |
| */ |
| @Test |
| public void testContextCommitted_noDatasetSelected_butManuallyEntered() |
| throws Exception { |
| enableService(); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setId("id1") |
| .setField(ID_USERNAME, BACKDOOR_USERNAME) |
| .setField(ID_PASSWORD, "NotUsedPassword") |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .addDataset(new CannedDataset.Builder() |
| .setId("id2") |
| .setField(ID_USERNAME, "NotUserUsername") |
| .setField(ID_PASSWORD, "whatever") |
| .setPresentation(createPresentation("dataset2")) |
| .build()) |
| .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED) |
| .build()); |
| // Trigger autofill on username |
| mActivity.onUsername(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| mUiBot.assertDatasets("dataset1", "dataset2"); |
| |
| // Verify history |
| InstrumentedAutoFillService.getFillEventHistory(0); |
| |
| // Enter values present at the datasets |
| mActivity.onUsername((v) -> v.setText(BACKDOOR_USERNAME)); |
| mActivity.onPassword((v) -> v.setText("whatever")); |
| |
| // Finish the context by login in |
| final String expectedMessage = getWelcomeMessage(BACKDOOR_USERNAME); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); |
| |
| // Verify history |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| FillEventHistory.Event event = events.get(0); |
| assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED); |
| assertThat(event.getDatasetId()).isNull(); |
| assertThat(event.getClientState()).isNull(); |
| assertThat(event.getSelectedDatasetIds()).isEmpty(); |
| assertThat(event.getIgnoredDatasetIds()).containsExactly("id1", "id2"); |
| assertThat(event.getChangedFields()).isEmpty(); |
| final AutofillId usernameId = mActivity.getUsername().getAutofillId(); |
| final AutofillId passwordId = mActivity.getPassword().getAutofillId(); |
| |
| final Map<AutofillId, Set<String>> manuallyEnteredFields = |
| event.getManuallyEnteredField(); |
| assertThat(manuallyEnteredFields).isNotNull(); |
| assertThat(manuallyEnteredFields.size()).isEqualTo(2); |
| assertThat(manuallyEnteredFields.get(usernameId)).containsExactly("id1"); |
| assertThat(manuallyEnteredFields.get(passwordId)).containsExactly("id2"); |
| } |
| } |
| |
| /** |
| * Tests scenario where the context was committed, the user did not selected any dataset, but |
| * the user manually entered values that match what was provided by the service on different |
| * datasets. |
| */ |
| @Test |
| public void testContextCommitted_noDatasetSelected_butManuallyEntered_matchingMultipleDatasets() |
| throws Exception { |
| enableService(); |
| |
| sReplier.addResponse(new CannedFillResponse.Builder().addDataset( |
| new CannedDataset.Builder() |
| .setId("id1") |
| .setField(ID_USERNAME, BACKDOOR_USERNAME) |
| .setField(ID_PASSWORD, "NotUsedPassword") |
| .setPresentation(createPresentation("dataset1")) |
| .build()) |
| .addDataset(new CannedDataset.Builder() |
| .setId("id2") |
| .setField(ID_USERNAME, "NotUserUsername") |
| .setField(ID_PASSWORD, "whatever") |
| .setPresentation(createPresentation("dataset2")) |
| .build()) |
| .addDataset(new CannedDataset.Builder() |
| .setId("id3") |
| .setField(ID_USERNAME, BACKDOOR_USERNAME) |
| .setField(ID_PASSWORD, "whatever") |
| .setPresentation(createPresentation("dataset3")) |
| .build()) |
| .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED) |
| .build()); |
| // Trigger autofill on username |
| mActivity.onUsername(View::requestFocus); |
| sReplier.getNextFillRequest(); |
| mUiBot.assertDatasets("dataset1", "dataset2", "dataset3"); |
| |
| // Verify history |
| InstrumentedAutoFillService.getFillEventHistory(0); |
| |
| // Enter values present at the datasets |
| mActivity.onUsername((v) -> v.setText(BACKDOOR_USERNAME)); |
| mActivity.onPassword((v) -> v.setText("whatever")); |
| |
| // Finish the context by login in |
| final String expectedMessage = getWelcomeMessage(BACKDOOR_USERNAME); |
| final String actualMessage = mActivity.tapLogin(); |
| assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); |
| mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); |
| |
| // Verify history |
| { |
| final List<Event> events = InstrumentedAutoFillService.getFillEvents(1); |
| |
| final FillEventHistory.Event event = events.get(0); |
| assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED); |
| assertThat(event.getDatasetId()).isNull(); |
| assertThat(event.getClientState()).isNull(); |
| assertThat(event.getSelectedDatasetIds()).isEmpty(); |
| assertThat(event.getIgnoredDatasetIds()).containsExactly("id1", "id2", "id3"); |
| assertThat(event.getChangedFields()).isEmpty(); |
| final AutofillId usernameId = mActivity.getUsername().getAutofillId(); |
| final AutofillId passwordId = mActivity.getPassword().getAutofillId(); |
| |
| final Map<AutofillId, Set<String>> manuallyEnteredFields = |
| event.getManuallyEnteredField(); |
| assertThat(manuallyEnteredFields.size()).isEqualTo(2); |
| assertThat(manuallyEnteredFields.get(usernameId)).containsExactly("id1", "id3"); |
| assertThat(manuallyEnteredFields.get(passwordId)).containsExactly("id2", "id3"); |
| } |
| } |
| } |