// Copyright 2015 The Project Buendia Authors
//
// 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 distrib-
// uted 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
// specific language governing permissions and limitations under the License.
package org.projectbuendia.client.ui.chart;
import android.test.AndroidTestCase;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.projectbuendia.client.R;
import org.projectbuendia.client.events.CrudEventBus;
import org.projectbuendia.client.events.FetchXformFailedEvent;
import org.projectbuendia.client.events.FetchXformSucceededEvent;
import org.projectbuendia.client.events.SubmitXformFailedEvent;
import org.projectbuendia.client.events.SubmitXformSucceededEvent;
import org.projectbuendia.client.events.data.ItemFetchedEvent;
import org.projectbuendia.client.json.ConceptType;
import org.projectbuendia.client.models.AppModel;
import org.projectbuendia.client.models.ConceptUuids;
import org.projectbuendia.client.models.Encounter;
import org.projectbuendia.client.models.Obs;
import org.projectbuendia.client.models.Order;
import org.projectbuendia.client.models.Patient;
import org.projectbuendia.client.sync.ChartDataHelper;
import org.projectbuendia.client.sync.SyncManager;
import org.projectbuendia.client.ui.FakeEventBus;
import org.projectbuendia.client.ui.chart.PatientChartController.MinimalHandler;
import org.projectbuendia.client.ui.chart.PatientChartController.OdkResultSender;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Map;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/** Tests for {@link PatientChartController}. */
public final class PatientChartControllerTest extends AndroidTestCase {
private static final String PATIENT_UUID_1 = "patient-uuid-1";
private static final String PATIENT_NAME_1 = "Bob";
private static final String PATIENT_ID_1 = "patient-id-1";
private static final Obs OBS_1 = new Obs(
0, ConceptUuids.TEMPERATURE_UUID, ConceptType.NUMERIC, "37.2", "");
private PatientChartController mController;
@Mock private AppModel mMockAppModel;
@Mock private PatientChartController.Ui mMockUi;
@Mock private OdkResultSender mMockOdkResultSender;
@Mock private ChartDataHelper mMockChartHelper;
@Mock private SyncManager mMockSyncManager;
private FakeEventBus mFakeCrudEventBus;
private FakeEventBus mFakeGlobalEventBus;
private FakeHandler mFakeHandler;
/** Tests that suspend() unregisters from the event bus. */
public void testSuspend_UnregistersFromEventBus() {
// GIVEN an initialized controller
mController.init();
// WHEN the controller is suspended
mController.suspend();
// THEN the controller unregisters from the event bus
assertEquals(0, mFakeCrudEventBus.countRegisteredReceivers());
}
/** Tests that init() requests a single patient from the app model. */
public void testInit_RequestsPatientDetails() {
// WHEN the controller is inited
mController.init();
// THEN it requests that patient's details be fetched
mMockAppModel.fetchSinglePatient(mFakeCrudEventBus, PATIENT_UUID_1);
}
/** Tests that observations are updated in the UI when patient details fetched. */
public void testPatientDetailsLoaded_SetsObservationsOnUi() {
// GIVEN the observations provider is set up to return some dummy data
List<Obs> allObservations =
ImmutableList.of(OBS_1);
Map<String, Obs> recentObservations =
ImmutableMap.of(OBS_1.conceptUuid, OBS_1);
when(mMockChartHelper.getObservations(PATIENT_UUID_1))
.thenReturn(allObservations);
when(mMockChartHelper.getLatestObservations(PATIENT_UUID_1))
.thenReturn(recentObservations);
// GIVEN controller is initialized
mController.init();
// WHEN that patient's details are loaded
Patient patient = Patient.builder().build();
mFakeCrudEventBus.post(new ItemFetchedEvent<>(patient));
// TODO: When the handler UI updating hack in PatientChartController is removed, this can
// also be removed.
mFakeHandler.runUntilEmpty();
// THEN the controller puts observations on the UI
verify(mMockUi).updateTilesAndGrid(
null, recentObservations, allObservations, ImmutableList.<Order> of(), null, null);
verify(mMockUi).updateAdmissionDateAndFirstSymptomsDateUi(null, null);
verify(mMockUi).updateEbolaPcrTestResultUi(recentObservations);
verify(mMockUi).updatePregnancyAndIvStatusUi(recentObservations);
}
/** Tests that the UI is given updated patient data when patient data is fetched. */
public void testPatientDetailsLoaded_UpdatesUi() {
// GIVEN controller is initialized
mController.init();
// WHEN that patient's details are loaded
Patient patient = Patient.builder().build();
mFakeCrudEventBus.post(new ItemFetchedEvent<>(patient));
// THEN the controller updates the UI
verify(mMockUi).updatePatientDetailsUi(patient);
}
/** Tests that selecting a new general condition results in adding a new encounter. */
public void testSetCondition_AddsEncounterForNewCondition() {
// GIVEN controller is initialized
mController.init();
// WHEN a new general condition is set from the dialog
mController.setCondition(ConceptUuids.GENERAL_CONDITION_PALLIATIVE_UUID);
// THEN a new encounter is added
verify(mMockAppModel).addEncounter(
any(CrudEventBus.class),
any(Patient.class),
any(Encounter.class));
}
/** Tests that requesting an xform through clicking 'add observation' shows loading dialog. */
public void testAddObservation_showsLoadingDialog() {
// GIVEN controller is initialized
mController.init();
// WHEN 'add observation' is pressed
mController.onAddObservationPressed();
// THEN the controller displays the loading dialog
verify(mMockUi).showFormLoadingDialog(true);
}
/** Tests that requesting an xform through clicking on a vital shows loading dialog. */
public void testVitalClick_showsLoadingDialog() {
// GIVEN controller is initialized
mController.init();
// WHEN a vital is pressed
mController.onAddObservationPressed("foo");
// THEN the controller displays the loading dialog
verify(mMockUi).showFormLoadingDialog(true);
}
/** Tests that requesting an xform through clicking on test results shows loading dialog. */
public void testTestResultsClick_showsLoadingDialog() {
// GIVEN controller is initialized
mController.init();
// WHEN test results are pressed
mController.onAddTestResultsPressed();
// THEN the controller displays the loading dialog
verify(mMockUi).showFormLoadingDialog(true);
}
/** Tests that the xform can be fetched again if the first fetch fails. */
public void testXformLoadFailed_ReenablesXformFetch() {
// GIVEN controller is initialized
mController.init();
// WHEN an xform request fails
mFakeGlobalEventBus.post(new FetchXformFailedEvent(FetchXformFailedEvent.Reason.UNKNOWN));
// THEN the controller re-enables xform fetch
verify(mMockUi).reEnableFetch();
}
/** Tests that an error message is displayed when the xform fails to load. */
public void testXformLoadFailed_ShowsError() {
// GIVEN controller is initialized
mController.init();
// WHEN an xform request fails
mFakeGlobalEventBus.post(new FetchXformFailedEvent(FetchXformFailedEvent.Reason.UNKNOWN));
// THEN the controller displays an error message
verify(mMockUi).showError(R.string.fetch_xform_failed_unknown_reason);
}
/** Tests that a failed xform fetch hides the loading dialog. */
public void testXformLoadFailed_HidesLoadingDialog() {
// GIVEN controller is initialized
mController.init();
// WHEN an xform request fails
mFakeGlobalEventBus.post(new FetchXformFailedEvent(FetchXformFailedEvent.Reason.UNKNOWN));
// THEN the controller hides the loading dialog
verify(mMockUi).showFormLoadingDialog(false);
}
/** Tests that the xform can be fetched again if the first fetch succeeds. */
public void testXformLoadSucceeded_ReenablesXformFetch() {
// GIVEN controller is initialized
mController.init();
// WHEN an xform request succeeds
mFakeGlobalEventBus.post(new FetchXformSucceededEvent());
// THEN the controller re-enables xform fetch
verify(mMockUi).reEnableFetch();
}
/** Tests that a successful xform fetch hides the loading dialog. */
public void testXformLoadSucceeded_HidesLoadingDialog() {
// GIVEN controller is initialized
mController.init();
// WHEN an xform request succeeds
mFakeGlobalEventBus.post(new FetchXformSucceededEvent());
// THEN the controller hides the loading dialog
verify(mMockUi).showFormLoadingDialog(false);
}
/** Tests that errors in xform submission are reported to the user. */
public void testXformSubmitFailed_ShowsErrorMessage() {
// GIVEN controller is initialized
mController.init();
// WHEN an xform fails to submit
mFakeGlobalEventBus.post(new SubmitXformFailedEvent(SubmitXformFailedEvent.Reason.UNKNOWN));
// THEN the controller shows an error
verify(mMockUi).showError(R.string.submit_xform_failed_unknown_reason);
}
// TODO/completeness: Test that starting an xform submission shows the submission dialog.
/** Tests that errors in xform submission hide the submission dialog. */
public void testXformSubmitFailed_HidesSubmissionDialog() {
// GIVEN controller is initialized
mController.init();
// WHEN an xform fails to submit
mFakeGlobalEventBus.post(new SubmitXformFailedEvent(SubmitXformFailedEvent.Reason.UNKNOWN));
// THEN the controller hides the submission dialog
verify(mMockUi).showFormSubmissionDialog(false);
}
/** Tests that successful xform submission hides the submission dialog. */
public void testXformSubmitSucceeded_EventuallyHidesSubmissionDialog() {
// GIVEN controller is initialized
mController.init();
// WHEN an xform submits successfully
mFakeGlobalEventBus.post(new SubmitXformSucceededEvent());
// THEN the controller hides the submission dialog
// TODO: When the handler UI updating hack in PatientChartController is removed, this can
// also be removed.
mFakeHandler.runUntilEmpty();
verify(mMockUi).showFormSubmissionDialog(false);
}
@Override protected void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
mFakeCrudEventBus = new FakeEventBus();
mFakeGlobalEventBus = new FakeEventBus();
mFakeHandler = new FakeHandler();
mController = new PatientChartController(
mMockAppModel,
mFakeGlobalEventBus,
mFakeCrudEventBus,
mMockUi,
PATIENT_UUID_1,
mMockOdkResultSender,
mMockChartHelper,
null,
mMockSyncManager,
mFakeHandler);
}
private final class FakeHandler implements MinimalHandler {
private final ArrayDeque<Runnable> mTasks = new ArrayDeque<>();
@Override public void post(Runnable runnable) {
mTasks.add(runnable);
}
public void runUntilEmpty() {
while (!mTasks.isEmpty()) {
Runnable runnable = mTasks.pop();
runnable.run();
}
}
}
}