package org.edx.mobile.view;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.View;
import android.widget.ListView;
import org.edx.mobile.R;
import org.edx.mobile.http.OkHttpUtil;
import org.edx.mobile.model.api.EnrolledCoursesResponse;
import org.edx.mobile.model.course.BlockPath;
import org.edx.mobile.model.course.BlockType;
import org.edx.mobile.model.course.CourseComponent;
import org.edx.mobile.model.course.IBlock;
import org.junit.Test;
import org.junit.runners.Parameterized.Parameters;
import org.robolectric.Robolectric;
import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowActivity;
import org.robolectric.util.ActivityController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import static org.assertj.android.api.Assertions.assertThat;
import static org.assertj.core.api.Java6Assertions.assertThat;
import static org.junit.Assert.*;
public class CourseOutlineActivityTest extends CourseBaseActivityTest {
/**
* Method for defining the subclass of {@link CourseOutlineActivity} that
* is being tested. Should be overridden by subclasses.
*
* @return The {@link CourseOutlineActivity} subclass that is being tested
*/
@Override
protected Class<? extends CourseOutlineActivity> getActivityClass() {
return CourseOutlineActivity.class;
}
/**
* Provide both true and false values as the {@link #provideCourseId}
* parameter in order to have both states tested.
*
* @return The sets of parameters containing true and false values
*/
@Parameters(name = "{index}: provide course id = {0}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] { { false }, { true } });
}
/**
* {@inheritDoc}
*/
@Override
protected boolean appliesPrevTransitionOnRestart() {
return true;
}
/**
* {@inheritDoc}
*/
@Test
@Override
public void initializeTest() {
super.initializeTest();
initialize(getIntent());
}
/**
* Generic method for running the initialization tests and returning the
* controller.
*
* @param intent The {@link Intent} to start the {@link CourseOutlineActivity} with
* @return The {@link ActivityController} instance used to initialize the
* {@link CourseOutlineActivity}
*/
private ActivityController<? extends CourseOutlineActivity> initialize(Intent intent) {
ActivityController<? extends CourseOutlineActivity> controller =
Robolectric.buildActivity(getActivityClass()).withIntent(intent);
CourseOutlineActivity activity = controller.create(null).postCreate(null).get();
// TODO: Write a comprehensive and isolated test suite for the Fragment
Fragment fragment = activity.getSupportFragmentManager()
.findFragmentByTag(CourseOutlineFragment.TAG);
assertNotNull(fragment);
assertThat(fragment).isInstanceOf(CourseOutlineFragment.class);
assertTrue(fragment.getRetainInstance());
Bundle args = fragment.getArguments();
assertNotNull(args);
Bundle data = intent.getBundleExtra(Router.EXTRA_BUNDLE);
assertEquals(data.getSerializable(Router.EXTRA_COURSE_DATA),
args.getSerializable(Router.EXTRA_COURSE_DATA));
if (provideCourseId) {
assertEquals(data.getString(Router.EXTRA_COURSE_COMPONENT_ID),
args.getString(Router.EXTRA_COURSE_COMPONENT_ID));
}
return controller;
}
// Since Robolectric doesn't simulate actual Activity navigation, we
// can only test forward navigation, and only up to one level. This
// blocks us from testing the back stack restructuring upon switching
// to a different section from CourseUnitNavigationActivityTest.
/**
* Testing navigation to a section
*/
@Test
public void sectionNavigationTest() {
Intent intent = getIntent();
Bundle extras = intent.getBundleExtra(Router.EXTRA_BUNDLE);
EnrolledCoursesResponse courseData = (EnrolledCoursesResponse)
extras.getSerializable(Router.EXTRA_COURSE_DATA);
assertNotNull(courseData);
CourseComponent courseComponent;
try {
courseComponent = serviceManager.getCourseStructure(
courseData.getCourse().getId(),
OkHttpUtil.REQUEST_CACHE_TYPE.IGNORE_CACHE);
} catch (Exception e) {
throw new RuntimeException(e);
}
int subsectionRowIndex = -1;
String subsectionId = null;
CourseComponent subsectionUnit = null;
List<IBlock> sections = courseComponent.getChildren();
sectionIteration: for (@SuppressWarnings("unused") IBlock section : sections) {
subsectionRowIndex++;
for (IBlock subsection : section.getChildren()) {
subsectionRowIndex++;
if (((CourseComponent) subsection).isContainer()) {
subsectionId = subsection.getId();
List<CourseComponent> leafComponents = new ArrayList<>();
courseComponent.fetchAllLeafComponents(leafComponents,
EnumSet.allOf(BlockType.class));
subsectionUnit = leafComponents.get(0);
break sectionIteration;
}
}
}
assertNotNull(subsectionId);
extras.putString(Router.EXTRA_COURSE_COMPONENT_ID, courseComponent.getId());
ActivityController<? extends CourseOutlineActivity> controller = initialize(intent);
CourseOutlineActivity activity = controller.get();
Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag(
CourseOutlineFragment.TAG);
assertThat(fragment).isInstanceOf(CourseOutlineFragment.class);
CourseOutlineFragment courseOutlineFragment = (CourseOutlineFragment) fragment;
clickRow(controller, courseOutlineFragment, subsectionRowIndex);
Intent newIntent = assertNextStartedActivity(activity, CourseOutlineActivity.class);
Bundle newData = newIntent.getBundleExtra(Router.EXTRA_BUNDLE);
assertNotNull(newData);
assertEquals(courseData, newData.getSerializable(Router.EXTRA_COURSE_DATA));
assertEquals(subsectionId, newData.getString(Router.EXTRA_COURSE_COMPONENT_ID));
// Back stack reconstruction upon receiving a specific path
Intent resultData = new Intent();
resultData.putExtra(Router.EXTRA_COURSE_COMPONENT_ID, subsectionUnit.getId());
courseOutlineFragment.onActivityResult(
CourseOutlineFragment.REQUEST_SHOW_COURSE_UNIT_DETAIL,
Activity.RESULT_OK, resultData);
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
BlockPath outlinePath = courseComponent.getPath();
BlockPath leafPath = subsectionUnit.getPath();
int outlinePathSize = outlinePath.getPath().size();
for (int i = outlinePathSize + 1;; i += 2) {
newIntent = shadowActivity.getNextStartedActivity();
CourseComponent nextComp = leafPath.get(i);
if (nextComp == null || !nextComp.isContainer()) {
assertNull(newIntent);
break;
}
assertNotNull(newIntent);
assertThat(newIntent).hasComponent(activity, CourseOutlineActivity.class);
newData = newIntent.getBundleExtra(Router.EXTRA_BUNDLE);
assertNotNull(newData);
assertEquals(courseData, newData.getSerializable(Router.EXTRA_COURSE_DATA));
assertEquals(nextComp.getId(), newData.getString(Router.EXTRA_COURSE_COMPONENT_ID));
}
}
/**
* Testing navigation to a unit
*/
@Test
public void unitNavigationTest() {
Intent intent = getIntent();
Bundle extras = intent.getBundleExtra(Router.EXTRA_BUNDLE);
EnrolledCoursesResponse courseData = (EnrolledCoursesResponse)
extras.getSerializable(Router.EXTRA_COURSE_DATA);
assertNotNull(courseData);
CourseComponent courseComponent;
try {
courseComponent = serviceManager.getCourseStructure(
courseData.getCourse().getId(),
OkHttpUtil.REQUEST_CACHE_TYPE.IGNORE_CACHE);
} catch (Exception e) {
throw new RuntimeException(e);
}
List<CourseComponent> leafComponents = new ArrayList<>();
courseComponent.fetchAllLeafComponents(leafComponents,
EnumSet.allOf(BlockType.class));
CourseComponent courseUnit = leafComponents.get(0);
CourseComponent lastUnit = leafComponents.get(leafComponents.size() - 1);
assertNotEquals(lastUnit, courseUnit);
courseComponent = courseUnit.getParent();
if (courseUnit.getPath().getPath().size() % 2 > 0) {
courseComponent = courseComponent.getParent();
}
int subsectionRowIndex = -1;
List<IBlock> sections = courseComponent.getChildren();
sectionIteration: for (@SuppressWarnings("unused") IBlock section : sections) {
subsectionRowIndex++;
if (courseUnit.equals(section)) {
break;
}
for (@SuppressWarnings("unused") IBlock subsection : section.getChildren()) {
subsectionRowIndex++;
if (courseUnit.equals(subsection)) {
break sectionIteration;
}
}
}
extras.putString(Router.EXTRA_COURSE_COMPONENT_ID, courseComponent.getId());
ActivityController<? extends CourseOutlineActivity> controller = initialize(intent);
CourseOutlineActivity activity = controller.get();
Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag(
CourseOutlineFragment.TAG);
assertThat(fragment).isInstanceOf(CourseOutlineFragment.class);
CourseOutlineFragment courseOutlineFragment = (CourseOutlineFragment) fragment;
clickRow(controller, courseOutlineFragment, subsectionRowIndex);
Intent newIntent = assertNextStartedActivity(activity, CourseUnitNavigationActivity.class);
Bundle newData = newIntent.getBundleExtra(Router.EXTRA_BUNDLE);
assertNotNull(newData);
assertEquals(courseData, newData.getSerializable(Router.EXTRA_COURSE_DATA));
assertEquals(courseUnit.getId(), newData.getSerializable(
Router.EXTRA_COURSE_COMPONENT_ID));
// Test the back stack reconstruction upon receiving a specific path
// Should not perform any action if it receives a unit selection from itself
Intent resultData = new Intent();
resultData.putExtra(Router.EXTRA_COURSE_COMPONENT_ID, courseUnit.getId());
courseOutlineFragment.onActivityResult(
CourseOutlineFragment.REQUEST_SHOW_COURSE_UNIT_DETAIL,
Activity.RESULT_OK, resultData);
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
assertNull(shadowActivity.getNextStartedActivity());
assertFalse(shadowActivity.isFinishing());
// Should finish itself to start the new navigation back stack if it receives
// a unit selection from another section
resultData.putExtra(Router.EXTRA_COURSE_COMPONENT_ID, lastUnit.getId());
courseOutlineFragment.onActivityResult(
CourseOutlineFragment.REQUEST_SHOW_COURSE_UNIT_DETAIL,
Activity.RESULT_OK, resultData);
assertNull(shadowActivity.getNextStartedActivity());
assertTrue(shadowActivity.isFinishing());
}
/**
* Generic method for clicking on a list row provided an index with
* appropriate assertions
*
* @param controller The {link ActivityController} controlling the
* {@link CourseOutlineActivity}
* @param courseOutlineFragment The {@link CourseOutlineFragment} instance
* @param rowIndex The row index
*/
private void clickRow(ActivityController<? extends CourseOutlineActivity> controller,
CourseOutlineFragment courseOutlineFragment, int rowIndex) {
controller.resume().postResume().visible();
View fragmentView = courseOutlineFragment.getView();
assertNotNull(fragmentView);
View outlineList = fragmentView.findViewById(R.id.outline_list);
assertNotNull(outlineList);
assertThat(outlineList).isInstanceOf(ListView.class);
ListView listView = (ListView) outlineList;
listView.performItemClick(listView.getChildAt(rowIndex),
rowIndex, listView.getItemIdAtPosition(rowIndex));
}
}