Kore/app/src/androidTest/java/org/xbmc/kore/tests/ui/music/SlideUpPanelTests.java

666 lines
24 KiB
Java

/*
* Copyright 2017 Martijn Brekhof. All rights reserved.
*
* 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 org.xbmc.kore.tests.ui.music;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.SystemClock;
import android.support.test.rule.ActivityTestRule;
import android.support.v7.preference.PreferenceManager;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
import org.junit.Rule;
import org.junit.Test;
import org.xbmc.kore.R;
import org.xbmc.kore.Settings;
import org.xbmc.kore.testhelpers.Utils;
import org.xbmc.kore.testhelpers.action.ViewActions;
import org.xbmc.kore.tests.ui.AbstractTestClass;
import org.xbmc.kore.testutils.tcpserver.handlers.PlayerHandler;
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.response.methods.Application;
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.response.methods.Player;
import org.xbmc.kore.testutils.tcpserver.handlers.jsonrpc.response.methods.Playlist;
import org.xbmc.kore.ui.sections.audio.MusicActivity;
import org.xbmc.kore.ui.widgets.HighlightButton;
import org.xbmc.kore.ui.widgets.RepeatModeButton;
import java.util.concurrent.TimeoutException;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.Espresso.pressBack;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.xbmc.kore.testhelpers.EspressoTestUtils.clickAdapterViewItem;
import static org.xbmc.kore.testhelpers.EspressoTestUtils.rotateDevice;
import static org.xbmc.kore.testhelpers.EspressoTestUtils.waitForPanelState;
import static org.xbmc.kore.testhelpers.Matchers.withHighlightState;
import static org.xbmc.kore.testhelpers.Matchers.withProgress;
import static org.xbmc.kore.testutils.TestUtils.createMusicItem;
import static org.xbmc.kore.testutils.TestUtils.createMusicVideoItem;
import static org.xbmc.kore.testutils.TestUtils.createVideoItem;
public class SlideUpPanelTests extends AbstractTestClass<MusicActivity> {
@Rule
public ActivityTestRule<MusicActivity> musicActivityActivityTestRule =
new ActivityTestRule<>(MusicActivity.class);
@Override
protected ActivityTestRule<MusicActivity> getActivityTestRule() {
return musicActivityActivityTestRule;
}
@Override
protected void setSharedPreferences(Context context) {
}
@Override
public void setUp() throws Throwable {
super.setUp();
getPlaylistHandler().reset();
getPlaylistHandler().addItemToPlaylist(Playlist.playlistID.AUDIO, createMusicItem(0, 0), true);
getPlaylistHandler().addItemToPlaylist(Playlist.playlistID.VIDEO, createVideoItem(0, 1), false);
getPlaylistHandler().addItemToPlaylist(Playlist.playlistID.VIDEO, createMusicVideoItem(0, 2), false);
getPlayerHandler().reset();
getPlayerHandler().setPlaylists(getPlaylistHandler().getPlaylists());
getPlayerHandler().startPlay(Playlist.playlistID.AUDIO, 0);
waitForPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
}
/**
* Test if panel title is correctly set
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Result: panel title should show current playing media item
*/
@Test
public void panelTitleTest() {
Player.GetItem item = getPlayerHandler().getMediaItem();
onView(withId(R.id.npp_title)).check(matches(withText(item.getTitle())));
}
/**
* Test if panel buttons are correctly set for music items
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Result: panel should show next, play, and previous buttons
*/
@Test
public void panelButtonsMusicTest() {
onView(withId(R.id.npp_next)).check(matches(isDisplayed()));
onView(withId(R.id.npp_previous)).check(matches(isDisplayed()));
onView(withId(R.id.npp_play)).check(matches(isDisplayed()));
}
/**
* Test if panel buttons are correctly set for movie items
*
* UI interaction flow tested:
* 1. Start playing a movie item
* 2. Result: panel should show play button
*/
@Test
public void panelButtonsMoviesTest() {
getPlayerHandler().startPlay(Playlist.playlistID.VIDEO, 0);
Player.GetItem item = getPlayerHandler().getMediaItem();
final String title = item.getTitle();
onView(isRoot()).perform(ViewActions.waitForView(
R.id.npp_title, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
return title.contentEquals(((TextView) v).getText());
}
}, 10000));
onView(withId(R.id.npp_next)).check(matches(not(isDisplayed())));
onView(withId(R.id.npp_previous)).check(matches(not(isDisplayed())));
onView(withId(R.id.npp_play)).check(matches(isDisplayed()));
}
/**
* Test if panel buttons are correctly set for music video items
*
* UI interaction flow tested:
* 1. Start playing a music video item
* 2. Result: panel should show next, play, and previous buttons
*/
@Test
public void panelButtonsMusicVideoTest() {
getPlayerHandler().startPlay(Playlist.playlistID.VIDEO, 1);
Player.GetItem item = getPlayerHandler().getMediaItem();
final String title = item.getTitle();
onView(isRoot()).perform(ViewActions.waitForView(
R.id.npp_title, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
return title.contentEquals(((TextView) v).getText());
}
}, 10000));
onView(withId(R.id.npp_next)).check(matches(isDisplayed()));
onView(withId(R.id.npp_previous)).check(matches(isDisplayed()));
onView(withId(R.id.npp_play)).check(matches(isDisplayed()));
}
/**
* Test if shuffle button state is correctly set
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Expand panel
* 3. Click on shuffle button
* 4. Result: shuffle button should be highlighted
*/
@Test
public void panelButtonsShuffleTest() {
expandPanel();
onView(withId(R.id.npp_shuffle)).perform(click());
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_shuffle, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
return ((HighlightButton) v).isHighlighted();
}
}, 10000));
}
/**
* Test if repeat button state is correctly set
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Expand panel
* 3. Click on repeat button
* 4. Result: repeat button should be highlighted and show single item repeat mode
* 5. Click on repeat button
* 6. Result: repeat button should be highlighted and show repeat playlist mode
* 7. Click on repeat button
* 8. Result: repeat button should not be highlighted
*/
@Test
public void panelButtonsRepeatModes() {
expandPanel();
//Initial state should be OFF
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_repeat, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
return ((RepeatModeButton) v).getMode() == RepeatModeButton.MODE.OFF;
}
}, 10000));
// Test if repeat mode is set to ONE after first click
onView(withId(R.id.npp_repeat)).perform(click());
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_repeat, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
return ((RepeatModeButton) v).getMode() == RepeatModeButton.MODE.ONE;
}
}, 10000));
// Test if repeat mode is set to ALL after second click
onView(withId(R.id.npp_repeat)).perform(click());
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_repeat, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
return ((RepeatModeButton) v).getMode() == RepeatModeButton.MODE.ALL;
}
}, 10000));
// Test if repeat mode is set to OFF after third click
onView(withId(R.id.npp_repeat)).perform(click());
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_repeat, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
return ((RepeatModeButton) v).getMode() == RepeatModeButton.MODE.OFF;
}
}, 10000));
}
/**
* Test if panel collapsed state is restored on configuration changes
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Rotate device
* 3. Result: panel state should be collapsed
*/
@Test
public void keepCollapsedOnRotate() {
rotateDevice(getActivity());
waitForPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
}
/**
* Test if panel expanded state is restored on configuration changes
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Expand panel
* 3. Rotate device
* 4. Result: panel state should be expanded
*/
@Test
public void keepExpandedOnRotate() {
expandPanel();
rotateDevice(getActivity());
waitForPanelState(SlidingUpPanelLayout.PanelState.EXPANDED);
}
/**
* Test if repeat button state is restored on configuration changes
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Expand panel
* 3. Click on repeat button
* 4. Rotate device
* 5. Result: repeat button state should be restored to state in step 2
*/
@Test
public void restoreRepeatButtonStateOnRotate() {
expandPanel();
onView(withId(R.id.npp_repeat)).perform(click());
rotateDevice(getActivity());
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_repeat, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
return ((RepeatModeButton) v).getMode() == RepeatModeButton.MODE.ONE;
}
}, 10000));
}
/**
* Test if shuffle button state is correctly set
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Expand panel
* 3. Click on shuffle button
* 4. Result: shuffle button state should be set to shuffle
*/
@Test
public void setShuffleButtonState() {
expandPanel();
onView(withId(R.id.npp_shuffle)).perform(click()); //Set state to shuffled
onView(withId(R.id.npp_shuffle)).check(matches(withHighlightState(true)));
}
/**
* Test if shuffle button state is restored on configuration changes
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Expand panel
* 3. Click on shuffle button
* 4. Rotate device
* 5. Result: shuffle button state should be restored to state in step 2
*/
@Test
public void restoreShuffleButtonStateOnRotate() {
expandPanel();
onView(withId(R.id.npp_shuffle)).perform(click()); //Set state to shuffled
rotateDevice(getActivityTestRule().getActivity());
//Using waitForView as we need to wait for the rotate to finish
onView(isRoot()).perform(ViewActions.waitForView(R.id.npp_shuffle, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
return ((HighlightButton) v).isHighlighted();
}
}, 10000));
}
/**
* Test if volume is correctly set at start
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Set volume at server
* 3. Expand panel
* 4. Result: Volume indicator should show the same volume level as set at the server
*/
@Test
public void setVolume() {
final int volume = 16;
getApplicationHandler().setVolume(volume, true);
assertTrue(getApplicationHandler().getVolume() == volume);
expandPanel();
onView(withId(R.id.vli_seek_bar)).check(matches(withProgress(volume)));
onView(withId(R.id.vli_volume_text)).check(matches(withText(String.valueOf(volume))));
}
/**
* Test if changing volume through the volume slider, updates the volume indicator correctly
* and sends the volume change to the server
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Expand panel
* 3. Set volume using slider
* 4. Result: Volume indicator should show volume level and server should be set to new volume level
*/
@Test
public void changeVolume() throws TimeoutException {
final int volume = 16;
expandPanel();
onView(withId(R.id.vli_seek_bar)).perform(ViewActions.slideSeekBar(volume));
onView(withId(R.id.vli_seek_bar)).check(matches(withProgress(volume)));
onView(withId(R.id.vli_volume_text)).check(matches(withText(String.valueOf(volume))));
getConnectionHandlerManager().waitForMethodHandled(Application.SetVolume.METHOD_NAME, 10000);
assertTrue("applicationHandler volume: "+ getApplicationHandler().getVolume()
+ " != " + volume, getApplicationHandler().getVolume() == volume);
}
/**
* Test if changing volume through the volume slider, updates the volume indicator correctly
* and sends the volume change to the server
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Expand panel
* 3. Set volume using slider
* 4. Result: Volume indicator should show volume level and server should be set to new volume level
*/
@Test
public void restoreVolumeIndicatorOnRotate() throws TimeoutException {
final int volume = 16;
expandPanel();
onView(withId(R.id.vli_seek_bar)).perform(ViewActions.slideSeekBar(volume));
rotateDevice(getActivity());
assertTrue("applicationHandler volume: "+ getApplicationHandler().getVolume()
+ " != " + volume, getApplicationHandler().getVolume() == volume);
onView(isRoot()).perform(ViewActions.waitForView(R.id.vli_seek_bar, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
return ((SeekBar) v).getProgress() == volume;
}
}, 10000));
onView(withId(R.id.vli_volume_text)).check(matches(withText(String.valueOf(volume))));
}
/**
* Test if setting progression correctly updates the media progress indicator
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Pause playback
* 3. Expand panel
* 4. Set progression
* 5. Result: Media progression indicator should be correctly updated and progression change
* should be sent to the server.
*/
@Test
public void setProgression() {
final int progress = 16;
final String progressText = "0:16";
expandPanel();
onView(withId(R.id.npp_play)).perform(click()); //Pause playback
onView(withId(R.id.mpi_seek_bar)).perform(ViewActions.slideSeekBar(progress));
onView(withId(R.id.mpi_progress)).check(matches(withText(progressText)));
assertTrue(getPlayerHandler().getTimeElapsed() == progress);
}
/**
* Test if progression is correctly restored after device configuration change
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Pause playback
* 3. Expand panel
* 4. Set progression
* 5. Rotate device
* 6. Result: Progression should be correctly same as before rotating the device.
*/
@Test
public void restoreProgressOnRotate() {
final int progress = 16;
final String progressText = "0:16";
expandPanel();
onView(withId(R.id.npp_play)).perform(click()); //Pause playback
onView(withId(R.id.mpi_seek_bar)).perform(ViewActions.slideSeekBar(progress));
rotateDevice(getActivity());
assertEquals(getPlayerHandler().getTimeElapsed(), progress);
onView(withId(R.id.mpi_progress)).check(matches(withProgress(progressText)));
onView(withId(R.id.mpi_seek_bar)).check(matches(withProgress(progress)));
}
/**
* Kodi resumes playback when progression changes.
* Test if changing progression when player is paused caused
* progression to start updating again
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Expand panel
* 3. Pause playback
* 4. Set progression
* 5. Start playback at server (that's what Kodi does)
* 6. Result: Playback should start at paused position
*/
@Test
public void pauseSetProgressionPlay() {
expandPanel();
onView(withId(R.id.npp_play)).perform(click()); //Pause playback
onView(withId(R.id.mpi_seek_bar)).perform(ViewActions.slideSeekBar(16));
getPlayerHandler().startPlay();
SeekBar seekBar = (SeekBar) getActivity().findViewById(R.id.mpi_seek_bar);
final int progress = seekBar.getProgress();
onView(isRoot()).perform(ViewActions.waitForView(
R.id.mpi_seek_bar, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
return ((SeekBar) v).getProgress() > progress;
}
}, 10000));
}
/**
* Test if panel's progressionbar progresses when playing media
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Result: Progression should be progressing
*/
@Test
public void progressionUpdaterStartedAfterPlay() {
expandPanel();
SeekBar seekBar = (SeekBar) getActivity().findViewById(R.id.mpi_seek_bar);
final int progress = seekBar.getProgress();
onView(isRoot()).perform(ViewActions.waitForView(
R.id.mpi_seek_bar, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
return ((SeekBar) v).getProgress() > progress;
}
}, 10000));
}
/**
* Test if panel's progression is maintained when starting a new activity
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Expand panel
* 3. Set progression
* 4. Switch to movies (new activity)
* 5. Result: Progression should continue from step 3
*/
@Test
public void continueProgressionAfterSwitchingActivity() throws Throwable {
final int progress = 24;
expandPanel();
onView(withId(R.id.mpi_seek_bar)).perform(ViewActions.slideSeekBar(progress));
Utils.openDrawer(getActivityTestRule());
clickAdapterViewItem(2, R.id.navigation_drawer); //select movie activity
waitForPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
expandPanel();
onView(isRoot()).perform(ViewActions.waitForView(R.id.mpi_seek_bar, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
int seekBarProgress = ((SeekBar) v).getProgress();
return (seekBarProgress > progress) && (seekBarProgress < (progress + 4));
}
}, 10000));
}
/**
* Test if pause button pauses playback
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Pause playback
* 3. Result: Server should stop playing and progressbar should pause
*/
@Test
public void pausePlayback() {
onView(withId(R.id.npp_play)).perform(click());
assertSame(getPlayerHandler().getPlayState(), PlayerHandler.PLAY_STATE.PAUSED);
expandPanel();
final int progress = ((SeekBar) getActivity().findViewById(R.id.mpi_seek_bar)).getProgress();
SystemClock.sleep(1000); //wait one second to check if progression has indeed paused
onView(isRoot()).perform(ViewActions.waitForView(R.id.mpi_seek_bar, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
int seekBarProgress = ((SeekBar) v).getProgress();
return seekBarProgress == progress;
}
}, 10000));
}
/**
* Test if panel is not displayed when user disables the panel
* through the preference screen
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Disable showing panel in settings
* 3. Result: Panel should not show
*/
@Test
public void disableShowingPanelInPreferences() throws Throwable {
Utils.openDrawer(getActivityTestRule());
clickAdapterViewItem(10, R.id.navigation_drawer); //Show preference screen
SharedPreferences.Editor edit = PreferenceManager.getDefaultSharedPreferences(getActivity()).edit();
edit.putBoolean(Settings.KEY_PREF_SHOW_NOW_PLAYING_PANEL, false);
edit.apply();
pressBack();
waitForPanelState(SlidingUpPanelLayout.PanelState.HIDDEN);
}
/**
* Test if panel is displayed when user enables the panel
* through the preference screen
*
* UI interaction flow tested:
* 1. Start playing a music item
* 2. Disable showing panel in settings
* 3. Show Music screen
* 4. Enable showing panel in settings
* 4. Return to Music screen
* 5. Result: Panel should show
*/
@Test
public void showPanelWhenUserEnablesPanel() throws Throwable {
Utils.openDrawer(getActivityTestRule());
clickAdapterViewItem(10, R.id.navigation_drawer); //Show preference screen
SharedPreferences.Editor edit = PreferenceManager.getDefaultSharedPreferences(getActivity()).edit();
edit.putBoolean(Settings.KEY_PREF_SHOW_NOW_PLAYING_PANEL, false);
edit.apply();
pressBack();
Utils.openDrawer(getActivityTestRule());
clickAdapterViewItem(10, R.id.navigation_drawer); //Show preference screen
edit.putBoolean(Settings.KEY_PREF_SHOW_NOW_PLAYING_PANEL, true);
edit.apply();
pressBack();
waitForPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
}
private void expandPanel() {
int tries = 10;
while (tries-- > 0) {
try {
onView(withId(R.id.npp_title)).perform(click());
onView(isRoot()).perform(ViewActions.waitForView(R.id.now_playing_panel, new ViewActions.CheckStatus() {
@Override
public boolean check(View v) {
return ((SlidingUpPanelLayout) v).getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED;
}
}, 1000));
return;
} catch (Exception e) {
//Either the click event did not work or the panel did not expand.
//Let's try again.
}
}
}
}