Compare commits
No commits in common. "testing" and "master" have entirely different histories.
@ -1,10 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
services:
|
|
||||||
selfoss:
|
|
||||||
container_name: selfoss
|
|
||||||
image: rsprta/selfoss
|
|
||||||
network_mode: "host"
|
|
||||||
ports:
|
|
||||||
- "8888:8888"
|
|
||||||
|
|
||||||
|
|
@ -19,6 +19,6 @@ jobs:
|
|||||||
- name: Setup Android SDK
|
- name: Setup Android SDK
|
||||||
uses: android-actions/setup-android@v3
|
uses: android-actions/setup-android@v3
|
||||||
- name: Configure gradle...
|
- name: Configure gradle...
|
||||||
run: mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true" >> ~/.gradle/gradle.properties
|
run: mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties
|
||||||
- name: Build and test
|
- name: Build and test
|
||||||
run: ./gradlew build --stacktrace
|
run: ./gradlew build --stacktrace
|
@ -1,45 +0,0 @@
|
|||||||
name: Check master code
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- testing
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
checkout:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Fetch tags
|
|
||||||
run: git fetch --tags -p
|
|
||||||
- uses: KengoTODA/actions-setup-docker-compose@v1
|
|
||||||
with:
|
|
||||||
version: "2.23.3"
|
|
||||||
- name: run selfoss
|
|
||||||
run: |
|
|
||||||
docker compose -f .gitea/workflows/assets/docker-compose.yml up -d
|
|
||||||
- uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: '17'
|
|
||||||
cache: gradle
|
|
||||||
- uses: gradle/actions/setup-gradle@v3
|
|
||||||
- uses: android-actions/setup-android@v3
|
|
||||||
- name: Configure gradle...
|
|
||||||
run: mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true" >> ~/.gradle/gradle.properties
|
|
||||||
- name: Robolectric tests
|
|
||||||
run: |
|
|
||||||
./gradlew :androidApp:testGithubConfigDebugUnitTest --tests "bou.amine.apps.readerforselfossv2.android.tests.robolectric.*"
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
if: failure()
|
|
||||||
with:
|
|
||||||
name: robot-tests
|
|
||||||
path: androidApp/build/test-results/testGithubConfigDebugUnitTest
|
|
||||||
retention-days: 1
|
|
||||||
overwrite: true
|
|
||||||
include-hidden-files: true
|
|
||||||
- name: Clean
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
docker compose -f .gitea/workflows/assets/docker-compose.yml stop
|
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -321,6 +321,3 @@ fabric.properties
|
|||||||
|
|
||||||
|
|
||||||
crowdin.properties
|
crowdin.properties
|
||||||
|
|
||||||
.kotlin/
|
|
||||||
build-cache/
|
|
@ -1,10 +1,3 @@
|
|||||||
**v124123421
|
|
||||||
|
|
||||||
- fix: Trying to fix the serialization issue.
|
|
||||||
- Changelog for v124113311
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v124113311
|
**v124113311
|
||||||
|
|
||||||
- chore: update versions. (#165)
|
- chore: update versions. (#165)
|
||||||
|
1
androidApp/.gitignore
vendored
1
androidApp/.gitignore
vendored
@ -1,2 +1 @@
|
|||||||
/build
|
/build
|
||||||
.kotlin/
|
|
@ -84,7 +84,6 @@ android {
|
|||||||
|
|
||||||
// tests
|
// tests
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
testInstrumentationRunnerArguments["clearPackageData"] = "true"
|
|
||||||
}
|
}
|
||||||
packaging {
|
packaging {
|
||||||
resources {
|
resources {
|
||||||
@ -108,16 +107,6 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
namespace = "bou.amine.apps.readerforselfossv2.android"
|
namespace = "bou.amine.apps.readerforselfossv2.android"
|
||||||
testOptions {
|
|
||||||
animationsDisabled = true
|
|
||||||
execution = "ANDROIDX_TEST_ORCHESTRATOR"
|
|
||||||
unitTests {
|
|
||||||
isIncludeAndroidResources = true
|
|
||||||
all {
|
|
||||||
it.systemProperty("robolectric.logging.enabled", true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,15 +184,6 @@ dependencies {
|
|||||||
testImplementation("io.mockk:mockk:1.12.0")
|
testImplementation("io.mockk:mockk:1.12.0")
|
||||||
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
|
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
|
||||||
androidTestImplementation("androidx.test:runner:1.6.2")
|
|
||||||
androidTestImplementation("androidx.test:rules:1.6.1")
|
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
|
|
||||||
implementation("androidx.test.espresso:espresso-idling-resource:3.6.1")
|
|
||||||
androidTestImplementation("androidx.test.ext:junit-ktx:1.2.1")
|
|
||||||
androidTestUtil("androidx.test:orchestrator:1.5.1")
|
|
||||||
testImplementation("org.robolectric:robolectric:4.14.1")
|
|
||||||
testImplementation("org.mockito:mockito-core:5.14.2")
|
|
||||||
|
|
||||||
|
|
||||||
implementation("ch.acra:acra-http:$acraVersion")
|
implementation("ch.acra:acra-http:$acraVersion")
|
||||||
implementation("ch.acra:acra-toast:$acraVersion")
|
implementation("ch.acra:acra-toast:$acraVersion")
|
||||||
|
@ -1,90 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.annotation.ArrayRes
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.action.ViewActions.replaceText
|
|
||||||
import androidx.test.espresso.action.ViewActions.typeTextIntoFocusedView
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isChecked
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isNotChecked
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
|
||||||
|
|
||||||
fun performLogin(someUrl: String? = null) {
|
|
||||||
onView(withId(R.id.urlView)).perform(click()).perform(
|
|
||||||
typeTextIntoFocusedView(
|
|
||||||
if (!someUrl.isNullOrEmpty()) someUrl else "http://10.0.2.2:8888"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
|
||||||
Thread.sleep(10000)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loginAndInitHome() {
|
|
||||||
|
|
||||||
performLogin()
|
|
||||||
onView(withText(R.string.gdpr_dialog_title)).check(matches(isDisplayed()))
|
|
||||||
onView(withText("OK")).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changeAndCancelSetting(oldValue: String, newValue: String, openSettingItem: () -> Unit) {
|
|
||||||
openSettingItem()
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit)
|
|
||||||
).perform(replaceText(newValue))
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.button2)
|
|
||||||
).perform(click())
|
|
||||||
openSettingItem()
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit)
|
|
||||||
).check(matches(withText(oldValue)))
|
|
||||||
onView(
|
|
||||||
withText(newValue)
|
|
||||||
).check(doesNotExist())
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.button2)
|
|
||||||
).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changeAndSaveSetting(oldValue: String, newValue: String, openSettingItem: () -> Unit) {
|
|
||||||
openSettingItem()
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit)
|
|
||||||
).perform(replaceText(newValue))
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.button1)
|
|
||||||
).perform(click())
|
|
||||||
openSettingItem()
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit)
|
|
||||||
).check(matches(withText(newValue)))
|
|
||||||
if (oldValue.isNotEmpty()) {
|
|
||||||
onView(
|
|
||||||
withText(oldValue)
|
|
||||||
).check(doesNotExist())
|
|
||||||
}
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.button2)
|
|
||||||
).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testPreferencesFromArray(
|
|
||||||
context: Context,
|
|
||||||
@ArrayRes arrayRes: Int,
|
|
||||||
openSettingItem: () -> Unit
|
|
||||||
) {
|
|
||||||
openSettingItem()
|
|
||||||
context.resources.getStringArray(arrayRes).forEach { res ->
|
|
||||||
onView(withText(res)).check(matches(allOf(isDisplayed(), isNotChecked())))
|
|
||||||
onView(withText(res)).perform(click())
|
|
||||||
onView(withText(res)).check(doesNotExist())
|
|
||||||
openSettingItem()
|
|
||||||
onView(withText(res)).check(matches(allOf(isDisplayed(), isChecked())))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
|
||||||
import androidx.test.espresso.action.ViewActions
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isClickable
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isFocused
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isSelected
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import org.hamcrest.CoreMatchers.not
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class HomeActivityTest {
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun init() {
|
|
||||||
loginAndInitHome()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMenu() {
|
|
||||||
onView(withId(R.id.action_search)).check(matches(isDisplayed())).check(
|
|
||||||
matches(
|
|
||||||
isClickable()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withId(R.id.action_filter)).check(matches(isDisplayed())).check(
|
|
||||||
matches(
|
|
||||||
isClickable()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext<Context>()
|
|
||||||
)
|
|
||||||
onView(withText(R.string.readAll)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.menu_home_sources)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.title_activity_settings)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.menu_home_refresh)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.issue_tracker_link)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.action_disconnect)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMenuActions() {
|
|
||||||
onView(withId(R.id.action_search)).perform(click())
|
|
||||||
onView(
|
|
||||||
withId(R.id.search_src_text)
|
|
||||||
).check(matches(isFocused()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
|
|
||||||
onView(withId(R.id.action_filter)).perform(click())
|
|
||||||
onView(
|
|
||||||
withText(R.string.filter_item_sources)
|
|
||||||
).check(matches(isDisplayed()))
|
|
||||||
onView(
|
|
||||||
withText(R.string.filter_item_tags)
|
|
||||||
).check(matches(isDisplayed()))
|
|
||||||
onView(
|
|
||||||
withId(R.id.floatingActionButton2)
|
|
||||||
).check(matches(isDisplayed()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext<Context>()
|
|
||||||
)
|
|
||||||
onView(withText(R.string.readAll)).perform(click())
|
|
||||||
onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext<Context>()
|
|
||||||
)
|
|
||||||
|
|
||||||
onView(withText(R.string.menu_home_sources)).perform(click())
|
|
||||||
onView(withId(R.id.fab)).check(matches(isDisplayed()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext<Context>()
|
|
||||||
)
|
|
||||||
|
|
||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
|
||||||
onView(withText(R.string.pref_header_general)).check(matches(isDisplayed()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext<Context>()
|
|
||||||
)
|
|
||||||
|
|
||||||
onView(withText(R.string.menu_home_refresh)).perform(click())
|
|
||||||
onView(withText(R.string.refresh_dialog_message)).check(matches(isDisplayed()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext<Context>()
|
|
||||||
)
|
|
||||||
|
|
||||||
/*onView(withText(R.string.issue_tracker_link)).perform(click())
|
|
||||||
onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext<Context>()
|
|
||||||
)*/
|
|
||||||
|
|
||||||
onView(withText(R.string.action_disconnect)).perform(click())
|
|
||||||
onView(withText(R.string.confirm_disconnect_title)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testEmptyView() {
|
|
||||||
onView(withId(R.id.emptyText)).check(matches(isDisplayed()))
|
|
||||||
onView(
|
|
||||||
hasBottombarItemText(R.string.tab_new)
|
|
||||||
).check(matches(isDisplayed())).check(matches(isSelected()))
|
|
||||||
onView(
|
|
||||||
hasBottombarItemText(R.string.tab_read)
|
|
||||||
).check(matches(isDisplayed())).check(matches(not(isSelected())))
|
|
||||||
onView(
|
|
||||||
hasBottombarItemText(R.string.tab_favs)
|
|
||||||
).check(matches(isDisplayed())).check(matches(not(isSelected())))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.IdlingRegistry
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isClickable
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isNotChecked
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class LoginActivityTest {
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
|
|
||||||
private fun getActivity(): Activity? {
|
|
||||||
var activity: Activity? = null
|
|
||||||
activityRule.scenario.onActivity {
|
|
||||||
activity = it
|
|
||||||
}
|
|
||||||
return activity
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun registerIdlingResource() {
|
|
||||||
IdlingRegistry.getInstance()
|
|
||||||
.register(CountingIdlingResourceSingleton.countingIdlingResource)
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun unregisterIdlingResource() {
|
|
||||||
IdlingRegistry.getInstance()
|
|
||||||
.unregister(CountingIdlingResourceSingleton.countingIdlingResource)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun viewIsInitialized() {
|
|
||||||
onView(withId(R.id.urlView)).check(matches(isDisplayed()))
|
|
||||||
onView(withId(R.id.selfSigned)).check(matches(isDisplayed())).check(matches(isNotChecked()))
|
|
||||||
.check(
|
|
||||||
matches(isClickable())
|
|
||||||
)
|
|
||||||
onView(withId(R.id.withLogin)).check(matches(isDisplayed()))
|
|
||||||
.check(matches(isNotChecked())).check(
|
|
||||||
matches(isClickable())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun urlError() {
|
|
||||||
performLogin("172.17.0.1:8888")
|
|
||||||
onView(withId(R.id.urlView)).perform(click())
|
|
||||||
onView(withId(R.id.urlView)).check(matches(withError(R.string.wrong_infos)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun multiError() {
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
|
||||||
onView(withText(R.string.warning_wrong_url)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun connect() {
|
|
||||||
performLogin()
|
|
||||||
onView(withText(R.string.gdpr_dialog_title)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,172 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
|
||||||
import androidx.test.espresso.action.ViewActions
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.action.ViewActions.replaceText
|
|
||||||
import androidx.test.espresso.action.ViewActions.typeTextIntoFocusedView
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isChecked
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isEnabled
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isFocused
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
|
||||||
import org.hamcrest.CoreMatchers.not
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class SettingsActivityGeneralTest {
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun init() {
|
|
||||||
loginAndInitHome()
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext()
|
|
||||||
)
|
|
||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
|
||||||
onView(withText(R.string.pref_header_general)).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testGeneral() {
|
|
||||||
onView(withText(R.string.pref_api_items_number_title)).check(matches(isDisplayed()))
|
|
||||||
onView(
|
|
||||||
withSettingsCheckboxWidget(R.string.pref_general_infinite_loading_title)
|
|
||||||
).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(), not(isChecked())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withText(R.string.pref_general_category_links)).check(matches(isDisplayed()))
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_article_viewer_title)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(), isChecked()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.reader_static_bar_title)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(), not(isChecked())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.reader_static_bar_title)).check(
|
|
||||||
matches(
|
|
||||||
isEnabled()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withText(R.string.pref_general_category_displaying)).check(matches(isDisplayed()))
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_card_view_title)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(), not(isChecked())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.card_height_title)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(), not(isChecked())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(
|
|
||||||
matches(
|
|
||||||
not(isEnabled())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.switch_unread_count_title)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(), isChecked()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.display_all_counts_title)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(), not(isChecked())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testGeneralActionsNumberItems() {
|
|
||||||
onView(withText(R.string.pref_api_items_number_title)).perform(click())
|
|
||||||
onView(withId(android.R.id.edit)).check(matches(isFocused()))
|
|
||||||
|
|
||||||
// Value check
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit)
|
|
||||||
).perform(replaceText("AVC"))
|
|
||||||
.check(matches(withText("")))
|
|
||||||
// TODO: should check message error. Not working for api level 30+
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit)
|
|
||||||
).perform(replaceText("-1"))
|
|
||||||
.check(matches(withText("")))
|
|
||||||
// TODO: should check message error. Not working for api level 30+
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit)
|
|
||||||
).perform(replaceText("300"))
|
|
||||||
.check(matches(withText("")))
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit)
|
|
||||||
).perform(typeTextIntoFocusedView("300"))
|
|
||||||
.check(matches(withText("30")))
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit)
|
|
||||||
).perform(replaceText("10"))
|
|
||||||
.check(matches(withText("10")))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
|
|
||||||
// Value saving
|
|
||||||
changeAndCancelSetting("20", "10") {
|
|
||||||
onView(withText(R.string.pref_api_items_number_title)).perform(click())
|
|
||||||
}
|
|
||||||
changeAndSaveSetting("20", "10") {
|
|
||||||
onView(withText(R.string.pref_api_items_number_title)).perform(click())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testGeneralActionsCheckboxes() {
|
|
||||||
// article viewer settings
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.reader_static_bar_title)).check(
|
|
||||||
matches(
|
|
||||||
isEnabled()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_article_viewer_title)).perform(click())
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.reader_static_bar_title)).check(
|
|
||||||
matches(
|
|
||||||
not(isEnabled())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(matches(not(isEnabled())))
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_card_view_title)).perform(click())
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(matches(isEnabled()))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,169 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isChecked
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isEnabled
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isNotEnabled
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
|
||||||
import org.hamcrest.CoreMatchers.not
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class SettingsActivityOfflineTest {
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
|
|
||||||
lateinit var context: Context
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun init() {
|
|
||||||
activityRule.scenario.onActivity { activity ->
|
|
||||||
context = activity.window.context
|
|
||||||
}
|
|
||||||
loginAndInitHome()
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext()
|
|
||||||
)
|
|
||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
|
||||||
onView(withText(R.string.pref_header_offline)).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testOffline() {
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_periodic_refresh)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(), not(isChecked())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_items_caching)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(), not(isChecked())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_items_caching)).check(
|
|
||||||
matches(
|
|
||||||
isEnabled()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).check(
|
|
||||||
matches(
|
|
||||||
allOf(isNotEnabled(), isDisplayed())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_refresh_when_charging)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(), not(isChecked())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
|
||||||
matches(
|
|
||||||
isNotEnabled()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_notify_new_items)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(), not(isChecked())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
|
||||||
matches(
|
|
||||||
isNotEnabled()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_update_sources)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(), isChecked()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testOfflineActions() {
|
|
||||||
onView(withText(R.string.pref_switch_items_caching_off)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.pref_switch_items_caching)).perform(click())
|
|
||||||
onView(withText(R.string.pref_switch_items_caching_on)).check(matches(isDisplayed()))
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_items_caching)).check(
|
|
||||||
matches(
|
|
||||||
isEnabled()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).check(
|
|
||||||
matches(
|
|
||||||
isNotEnabled()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
|
||||||
matches(
|
|
||||||
isNotEnabled()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
|
||||||
matches(
|
|
||||||
isNotEnabled()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
onView(withText(R.string.pref_switch_periodic_refresh_off)).check(
|
|
||||||
matches(
|
|
||||||
isDisplayed()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_periodic_refresh)).perform(click())
|
|
||||||
onView(withText(R.string.pref_switch_periodic_refresh_on)).check(
|
|
||||||
matches(
|
|
||||||
isDisplayed()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_periodic_refresh_minutes_title)).check(
|
|
||||||
matches(
|
|
||||||
isEnabled()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
|
||||||
matches(
|
|
||||||
isEnabled()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
|
||||||
matches(
|
|
||||||
isEnabled()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
changeAndCancelSetting("360", "123") {
|
|
||||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).perform(click())
|
|
||||||
}
|
|
||||||
changeAndSaveSetting("360", "123") {
|
|
||||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).perform(click())
|
|
||||||
}
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).perform(click())
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).perform(click())
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_update_sources)).perform(click())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isChecked
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
|
||||||
import org.hamcrest.CoreMatchers.not
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class SettingsActivityReaderTest {
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
|
|
||||||
lateinit var context: Context
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun init() {
|
|
||||||
activityRule.scenario.onActivity { activity ->
|
|
||||||
context = activity.window.context
|
|
||||||
}
|
|
||||||
loginAndInitHome()
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext()
|
|
||||||
)
|
|
||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
|
||||||
onView(withText(R.string.pref_header_viewer)).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testReader() {
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_actions_pager_scroll)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(), not(
|
|
||||||
isChecked()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withText(R.string.pref_content_reader_font_size)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.settings_reader_font)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testReaderActions() {
|
|
||||||
onView(withText(R.string.pref_switch_actions_pager_scroll_off)).check(
|
|
||||||
matches(
|
|
||||||
isDisplayed()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_actions_pager_scroll)).perform(click())
|
|
||||||
onView(withText(R.string.pref_switch_actions_pager_scroll_on)).check(
|
|
||||||
matches(
|
|
||||||
isDisplayed()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
onView(withText(R.string.pref_content_reader_font_size)).perform(click())
|
|
||||||
changeAndCancelSetting("16", "10") {
|
|
||||||
onView(withText(R.string.pref_content_reader_font_size)).perform(click())
|
|
||||||
}
|
|
||||||
changeAndSaveSetting("16", "10") {
|
|
||||||
onView(withText(R.string.pref_content_reader_font_size)).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
testPreferencesFromArray(context, R.array.preloaded_fonts_values) {
|
|
||||||
onView(withText(R.string.settings_reader_font)).perform(click())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isSelected
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
|
||||||
import org.hamcrest.CoreMatchers.not
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class SettingsActivityTest {
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
lateinit var context: Context
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun init() {
|
|
||||||
activityRule.scenario.onActivity { activity ->
|
|
||||||
context = activity.window.context
|
|
||||||
}
|
|
||||||
loginAndInitHome()
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext<Context>()
|
|
||||||
)
|
|
||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAllSettings() {
|
|
||||||
|
|
||||||
onView(withText(R.string.pref_header_general)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.pref_header_viewer)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.pref_header_offline)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.pref_header_theme)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.pref_header_links)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.pref_switch_disable_acra)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
not(isSelected())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onView(withText(R.string.action_about)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testThemes() {
|
|
||||||
testPreferencesFromArray(context, R.array.ModeTitles) {
|
|
||||||
onView(withText(R.string.pref_header_theme)).perform(click())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testExperimentail() {
|
|
||||||
onView(withText(R.string.pref_header_experimental)).perform(click())
|
|
||||||
changeAndCancelSetting("", "10") {
|
|
||||||
onView(withText(R.string.pref_api_timeout)).perform(click())
|
|
||||||
}
|
|
||||||
changeAndSaveSetting("", "10") {
|
|
||||||
onView(withText(R.string.pref_api_timeout)).perform(click())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testBugReports() {
|
|
||||||
onView(withText(R.string.pref_switch_disable_acra)).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testLinks() {
|
|
||||||
onView(withText(R.string.pref_header_links)).perform(click())
|
|
||||||
onView(withText(R.string.issue_tracker_link)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.issue_tracker_summary)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.source_code)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.translation)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAbout() {
|
|
||||||
onView(withText(R.string.action_about)).perform(click())
|
|
||||||
onView(withText("ACRA")).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.action.ViewActions.scrollCompletelyTo
|
|
||||||
import androidx.test.espresso.action.ViewActions.typeTextIntoFocusedView
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class SourcesActivityTest {
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
|
|
||||||
lateinit var sourceName: String
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun init() {
|
|
||||||
sourceName = UUID.randomUUID().toString().substring(0, 15)
|
|
||||||
|
|
||||||
loginAndInitHome()
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext<Context>()
|
|
||||||
)
|
|
||||||
onView(withText(R.string.menu_home_sources))
|
|
||||||
.perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addSource() {
|
|
||||||
onView(withId(R.id.fab))
|
|
||||||
.perform(click())
|
|
||||||
onView(withId(R.id.nameInput))
|
|
||||||
.perform(click()).perform(typeTextIntoFocusedView(sourceName))
|
|
||||||
onView(withId(R.id.sourceUri))
|
|
||||||
.perform(click())
|
|
||||||
.perform(typeTextIntoFocusedView("https://lorem-rss.herokuapp.com/feed?unit=year&interval=1&length=10"))
|
|
||||||
onView(withId(R.id.tags))
|
|
||||||
.perform(click()).perform(typeTextIntoFocusedView("tag1,tag2,tag3"))
|
|
||||||
onView(withId(R.id.spoutsSpinner))
|
|
||||||
.perform(click())
|
|
||||||
onView(withText("RSS Feed"))
|
|
||||||
.perform(scrollCompletelyTo())
|
|
||||||
.perform(click())
|
|
||||||
onView(withId(R.id.saveBtn))
|
|
||||||
.perform(click())
|
|
||||||
onView(withText(sourceName)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,101 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.RelativeLayout
|
|
||||||
import androidx.annotation.DrawableRes
|
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
|
||||||
import androidx.test.espresso.Root
|
|
||||||
import androidx.test.espresso.matcher.RootMatchers.isPlatformPopup
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.hasSibling
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withChild
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withClassName
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withParent
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withResourceName
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
|
||||||
import org.hamcrest.Description
|
|
||||||
import org.hamcrest.Matcher
|
|
||||||
import org.hamcrest.Matchers
|
|
||||||
import org.hamcrest.TypeSafeMatcher
|
|
||||||
|
|
||||||
|
|
||||||
fun withError(@StringRes id: Int): TypeSafeMatcher<View?> {
|
|
||||||
return object : TypeSafeMatcher<View?>() {
|
|
||||||
override fun matchesSafely(view: View?): Boolean {
|
|
||||||
if (view == null) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
val context = view.context
|
|
||||||
if (view !is EditText) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (view.error == null) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return view.error.toString() == context.getString(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun describeTo(description: Description?) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isPopupWindow(): Matcher<Root> {
|
|
||||||
return isPlatformPopup()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun withDrawable(@DrawableRes id: Int) = object : TypeSafeMatcher<View>() {
|
|
||||||
override fun describeTo(description: Description) {
|
|
||||||
description.appendText("ImageView with drawable same as drawable with id $id")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun matchesSafely(view: View): Boolean {
|
|
||||||
val context = view.context
|
|
||||||
val expectedBitmap = context.getDrawable(id)!!.toBitmap()
|
|
||||||
try {
|
|
||||||
return view is ImageView && view.drawable.toBitmap().sameAs(expectedBitmap)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hasBottombarItemText(@StringRes id: Int): Matcher<View>? {
|
|
||||||
return allOf(
|
|
||||||
withResourceName("fixed_bottom_navigation_icon"),
|
|
||||||
withParent(
|
|
||||||
allOf(
|
|
||||||
withResourceName("fixed_bottom_navigation_icon_container"),
|
|
||||||
hasSibling(withText(id))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun withSettingsCheckboxWidget(@StringRes id: Int): Matcher<View>? {
|
|
||||||
return allOf(
|
|
||||||
withId(android.R.id.switch_widget),
|
|
||||||
withParent(
|
|
||||||
withSettingsCheckboxFrame(id)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun withSettingsCheckboxFrame(@StringRes id: Int): Matcher<View>? {
|
|
||||||
return allOf(
|
|
||||||
withId(android.R.id.widget_frame),
|
|
||||||
hasSibling(
|
|
||||||
allOf(
|
|
||||||
withClassName(Matchers.equalTo(RelativeLayout::class.java.name)),
|
|
||||||
withChild(
|
|
||||||
withText(id)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
@ -29,7 +29,6 @@ import bou.amine.apps.readerforselfossv2.android.background.LoadingWorker
|
|||||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityHomeBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityHomeBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.fragments.FilterSheetFragment
|
import bou.amine.apps.readerforselfossv2.android.fragments.FilterSheetFragment
|
||||||
import bou.amine.apps.readerforselfossv2.android.settings.SettingsActivity
|
import bou.amine.apps.readerforselfossv2.android.settings.SettingsActivity
|
||||||
import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow
|
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge
|
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
@ -85,8 +84,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
repository.offlineOverride = intent.getBooleanExtra("startOffline", false)
|
repository.offlineOverride = intent.getBooleanExtra("startOffline", false)
|
||||||
|
|
||||||
if (fromTabShortcut) {
|
if (fromTabShortcut) {
|
||||||
elementsShown =
|
elementsShown = ItemType.fromInt(intent.getIntExtra("shortcutTab", ItemType.UNREAD.position))
|
||||||
ItemType.fromInt(intent.getIntExtra("shortcutTab", ItemType.UNREAD.position))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setContentView(view)
|
setContentView(view)
|
||||||
@ -98,10 +96,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
handleSwipeRefreshLayout()
|
handleSwipeRefreshLayout()
|
||||||
|
|
||||||
if (appSettingsService.isItemCachingEnabled()) {
|
if (appSettingsService.isItemCachingEnabled()) {
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
repository.tryToCacheItemsAndGetNewOnes()
|
repository.tryToCacheItemsAndGetNewOnes()
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,11 +111,9 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||||
repository.offlineOverride = false
|
repository.offlineOverride = false
|
||||||
lastFetchDone = false
|
lastFetchDone = false
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
getElementsAccordingToTab()
|
getElementsAccordingToTab()
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,10 +274,9 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
handleGDPRDialog(appSettingsService.settings.getBoolean("GDPR_shown", false))
|
handleGDPRDialog(appSettingsService.settings.getBoolean("GDPR_shown", false))
|
||||||
|
|
||||||
handleRecurringTask()
|
handleRecurringTask()
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
repository.handleDBActions()
|
repository.handleDBActions()
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getElementsAccordingToTab()
|
getElementsAccordingToTab()
|
||||||
@ -322,7 +315,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
)
|
)
|
||||||
binding.recyclerView.layoutManager = layoutManager
|
binding.recyclerView.layoutManager = layoutManager
|
||||||
}
|
}
|
||||||
|
|
||||||
is GridLayoutManager ->
|
is GridLayoutManager ->
|
||||||
if (appSettingsService.isCardViewEnabled()) {
|
if (appSettingsService.isCardViewEnabled()) {
|
||||||
layoutManager =
|
layoutManager =
|
||||||
@ -334,7 +326,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
|
StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
|
||||||
binding.recyclerView.layoutManager = layoutManager
|
binding.recyclerView.layoutManager = layoutManager
|
||||||
}
|
}
|
||||||
|
|
||||||
else ->
|
else ->
|
||||||
if (currentManager == null) {
|
if (currentManager == null) {
|
||||||
if (!appSettingsService.isCardViewEnabled()) {
|
if (!appSettingsService.isCardViewEnabled()) {
|
||||||
@ -371,14 +362,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
} else {
|
} else {
|
||||||
layoutManager.scrollToPositionWithOffset(0, 0)
|
layoutManager.scrollToPositionWithOffset(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
is GridLayoutManager ->
|
is GridLayoutManager ->
|
||||||
if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
|
if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
|
||||||
getElementsAccordingToTab()
|
getElementsAccordingToTab()
|
||||||
} else {
|
} else {
|
||||||
layoutManager.scrollToPositionWithOffset(0, 0)
|
layoutManager.scrollToPositionWithOffset(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -431,7 +420,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
manager.findLastCompletelyVisibleItemPositions(
|
manager.findLastCompletelyVisibleItemPositions(
|
||||||
null,
|
null,
|
||||||
).last()
|
).last()
|
||||||
|
|
||||||
is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition()
|
is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition()
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
@ -460,7 +448,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
appendResults: Boolean,
|
appendResults: Boolean,
|
||||||
itemType: ItemType,
|
itemType: ItemType,
|
||||||
) {
|
) {
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
binding.swipeRefreshLayout.isRefreshing = true
|
binding.swipeRefreshLayout.isRefreshing = true
|
||||||
repository.displayedItems = itemType
|
repository.displayedItems = itemType
|
||||||
@ -472,7 +459,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
}
|
}
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
handleListResult()
|
handleListResult()
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,10 +469,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
when (oldManager) {
|
when (oldManager) {
|
||||||
is StaggeredGridLayoutManager ->
|
is StaggeredGridLayoutManager ->
|
||||||
oldManager.findFirstCompletelyVisibleItemPositions(null).last()
|
oldManager.findFirstCompletelyVisibleItemPositions(null).last()
|
||||||
|
|
||||||
is GridLayoutManager ->
|
is GridLayoutManager ->
|
||||||
oldManager.findFirstCompletelyVisibleItemPosition()
|
oldManager.findFirstCompletelyVisibleItemPosition()
|
||||||
|
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -527,10 +511,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
private fun reloadBadges() {
|
private fun reloadBadges() {
|
||||||
if (appSettingsService.isDisplayUnreadCountEnabled() || appSettingsService.isDisplayAllCountEnabled()) {
|
if (appSettingsService.isDisplayUnreadCountEnabled() || appSettingsService.isDisplayAllCountEnabled()) {
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
repository.reloadBadges()
|
repository.reloadBadges()
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -566,7 +548,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
}
|
}
|
||||||
|
|
||||||
val searchItem = menu.findItem(R.id.action_search)
|
val searchItem = menu.findItem(R.id.action_search)
|
||||||
val searchView = searchItem.actionView as SearchView
|
val searchView = searchItem.getActionView() as SearchView
|
||||||
searchView.setOnQueryTextListener(this)
|
searchView.setOnQueryTextListener(this)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -589,22 +571,18 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.issue_tracker -> {
|
R.id.issue_tracker -> {
|
||||||
val browserIntent =
|
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
|
||||||
Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
|
|
||||||
startActivity(browserIntent)
|
startActivity(browserIntent)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_filter -> {
|
R.id.action_filter -> {
|
||||||
val filterSheetFragment = FilterSheetFragment()
|
val filterSheetFragment = FilterSheetFragment()
|
||||||
filterSheetFragment.show(supportFragmentManager, FilterSheetFragment.TAG)
|
filterSheetFragment.show(supportFragmentManager, FilterSheetFragment.TAG)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.refresh -> {
|
R.id.refresh -> {
|
||||||
needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) {
|
needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) {
|
||||||
Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show()
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
val updatedRemote = repository.updateRemote()
|
val updatedRemote = repository.updateRemote()
|
||||||
if (updatedRemote) {
|
if (updatedRemote) {
|
||||||
@ -621,17 +599,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
Toast.LENGTH_SHORT,
|
Toast.LENGTH_SHORT,
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.readAll -> {
|
R.id.readAll -> {
|
||||||
if (elementsShown == ItemType.UNREAD) {
|
if (elementsShown == ItemType.UNREAD) {
|
||||||
needsConfirmation(R.string.readAll, R.string.markall_dialog_message) {
|
needsConfirmation(R.string.readAll, R.string.markall_dialog_message) {
|
||||||
binding.swipeRefreshLayout.isRefreshing = true
|
binding.swipeRefreshLayout.isRefreshing = true
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
val success = repository.markAllAsRead(items)
|
val success = repository.markAllAsRead(items)
|
||||||
if (success) {
|
if (success) {
|
||||||
@ -652,19 +628,13 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
}
|
}
|
||||||
handleListResult()
|
handleListResult()
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_disconnect -> {
|
R.id.action_disconnect -> {
|
||||||
needsConfirmation(
|
needsConfirmation(R.string.confirm_disconnect_title, R.string.confirm_disconnect_description) {
|
||||||
R.string.confirm_disconnect_title,
|
|
||||||
R.string.confirm_disconnect_description
|
|
||||||
) {
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
repository.logout()
|
repository.logout()
|
||||||
}
|
}
|
||||||
@ -674,17 +644,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_settings -> {
|
R.id.action_settings -> {
|
||||||
settingsLauncher.launch(Intent(this, SettingsActivity::class.java))
|
settingsLauncher.launch(Intent(this, SettingsActivity::class.java))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_sources -> {
|
R.id.action_sources -> {
|
||||||
startActivity(Intent(this, SourcesActivity::class.java))
|
startActivity(Intent(this, SourcesActivity::class.java))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> return super.onOptionsItemSelected(item)
|
else -> return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -711,21 +678,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
val backgroundWork =
|
val backgroundWork =
|
||||||
PeriodicWorkRequestBuilder<LoadingWorker>(
|
PeriodicWorkRequestBuilder<LoadingWorker>(appSettingsService.getRefreshMinutes(), TimeUnit.MINUTES)
|
||||||
appSettingsService.getRefreshMinutes(),
|
|
||||||
TimeUnit.MINUTES
|
|
||||||
)
|
|
||||||
.setConstraints(myConstraints)
|
.setConstraints(myConstraints)
|
||||||
.addTag("selfoss-loading")
|
.addTag("selfoss-loading")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
WorkManager.getInstance(
|
WorkManager.getInstance(
|
||||||
baseContext,
|
baseContext,
|
||||||
).enqueueUniquePeriodicWork(
|
).enqueueUniquePeriodicWork("selfoss-loading", ExistingPeriodicWorkPolicy.KEEP, backgroundWork)
|
||||||
"selfoss-loading",
|
|
||||||
ExistingPeriodicWorkPolicy.KEEP,
|
|
||||||
backgroundWork
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,7 +17,6 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
|
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
@ -103,14 +102,9 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun goToMain() {
|
private fun goToMain() {
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
repository.updateApiInformation()
|
repository.updateApiInformation()
|
||||||
ACRA.errorReporter.putCustomData(
|
ACRA.errorReporter.putCustomData("SELFOSS_API_VERSION", appSettingsService.getApiVersion().toString())
|
||||||
"SELFOSS_API_VERSION",
|
|
||||||
appSettingsService.getApiVersion().toString()
|
|
||||||
)
|
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
val intent = Intent(this, HomeActivity::class.java)
|
val intent = Intent(this, HomeActivity::class.java)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
@ -145,7 +139,6 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
|||||||
|
|
||||||
repository.refreshLoginInformation(url, login, password)
|
repository.refreshLoginInformation(url, login, password)
|
||||||
|
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
try {
|
try {
|
||||||
repository.updateApiInformation()
|
repository.updateApiInformation()
|
||||||
@ -172,7 +165,6 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
|||||||
preferenceError()
|
preferenceError()
|
||||||
}
|
}
|
||||||
showProgress(false)
|
showProgress(false)
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,24 +261,19 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
|||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.issue_tracker -> {
|
R.id.issue_tracker -> {
|
||||||
val browserIntent =
|
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
|
||||||
Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
|
|
||||||
startActivity(browserIntent)
|
startActivity(browserIntent)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.about -> {
|
R.id.about -> {
|
||||||
LibsBuilder()
|
LibsBuilder()
|
||||||
.withAboutIconShown(true)
|
.withAboutIconShown(true)
|
||||||
.withAboutVersionShown(true)
|
.withAboutVersionShown(true)
|
||||||
.withAboutSpecial2("Bug reports")
|
.withAboutSpecial2("Bug reports").withAboutSpecial2Description(AppSettingsService.trackerUrl)
|
||||||
.withAboutSpecial2Description(AppSettingsService.trackerUrl)
|
.withAboutSpecial1("Project Page").withAboutSpecial1Description(AppSettingsService.sourceUrl)
|
||||||
.withAboutSpecial1("Project Page")
|
|
||||||
.withAboutSpecial1Description(AppSettingsService.sourceUrl)
|
|
||||||
.start(this)
|
.start(this)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import androidx.lifecycle.LifecycleOwner
|
|||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
import androidx.multidex.MultiDexApplication
|
import androidx.multidex.MultiDexApplication
|
||||||
import bou.amine.apps.readerforselfossv2.DI.networkModule
|
import bou.amine.apps.readerforselfossv2.DI.networkModule
|
||||||
import bou.amine.apps.readerforselfossv2.android.testing.TestingHelper
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
|
import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
|
||||||
import bou.amine.apps.readerforselfossv2.dao.DriverFactory
|
import bou.amine.apps.readerforselfossv2.dao.DriverFactory
|
||||||
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
|
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
|
||||||
@ -30,27 +29,23 @@ import org.acra.config.toast
|
|||||||
import org.acra.data.StringFormat
|
import org.acra.data.StringFormat
|
||||||
import org.acra.ktx.initAcra
|
import org.acra.ktx.initAcra
|
||||||
import org.acra.sender.HttpSender
|
import org.acra.sender.HttpSender
|
||||||
import org.kodein.di.DI
|
import org.kodein.di.*
|
||||||
import org.kodein.di.DIAware
|
|
||||||
import org.kodein.di.bind
|
|
||||||
import org.kodein.di.instance
|
|
||||||
import org.kodein.di.singleton
|
|
||||||
|
|
||||||
class MyApp : MultiDexApplication(), DIAware {
|
class MyApp : MultiDexApplication(), DIAware {
|
||||||
override val di by DI.lazy {
|
override val di by DI.lazy {
|
||||||
bind<AppSettingsService>() with singleton { AppSettingsService(ACRA.isACRASenderServiceProcess() || TestingHelper().isUnitTest()) }
|
bind<AppSettingsService>() with singleton { AppSettingsService(ACRA.isACRASenderServiceProcess()) }
|
||||||
import(networkModule)
|
import(networkModule)
|
||||||
bind<DriverFactory>() with singleton { DriverFactory(applicationContext) }
|
bind<DriverFactory>() with singleton { DriverFactory(applicationContext) }
|
||||||
bind<ReaderForSelfossDB>() with singleton { ReaderForSelfossDB(driverFactory.createDriver()) }
|
bind<ReaderForSelfossDB>() with singleton { ReaderForSelfossDB(driverFactory.createDriver()) }
|
||||||
bind<Repository>() with
|
bind<Repository>() with
|
||||||
singleton {
|
singleton {
|
||||||
Repository(
|
Repository(
|
||||||
instance(),
|
instance(),
|
||||||
instance(),
|
instance(),
|
||||||
isConnectionAvailable,
|
isConnectionAvailable,
|
||||||
instance(),
|
instance(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
bind<ConnectivityStatus>() with singleton { ConnectivityStatus(applicationContext) }
|
bind<ConnectivityStatus>() with singleton { ConnectivityStatus(applicationContext) }
|
||||||
bind<AppViewModel>() with singleton { AppViewModel(repository = instance()) }
|
bind<AppViewModel>() with singleton { AppViewModel(repository = instance()) }
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import bou.amine.apps.readerforselfossv2.android.adapters.SourcesListAdapter
|
import bou.amine.apps.readerforselfossv2.android.adapters.SourcesListAdapter
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton
|
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -37,8 +36,7 @@ class SourcesActivity : AppCompatActivity(), DIAware {
|
|||||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||||
|
|
||||||
binding.fab.rippleColor = resources.getColor(R.color.colorAccentDark)
|
binding.fab.rippleColor = resources.getColor(R.color.colorAccentDark)
|
||||||
binding.fab.backgroundTintList =
|
binding.fab.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.colorAccent))
|
||||||
ColorStateList.valueOf(resources.getColor(R.color.colorAccent))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
@ -55,7 +53,6 @@ class SourcesActivity : AppCompatActivity(), DIAware {
|
|||||||
binding.recyclerView.setHasFixedSize(true)
|
binding.recyclerView.setHasFixedSize(true)
|
||||||
binding.recyclerView.layoutManager = mLayoutManager
|
binding.recyclerView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
val response = repository.getSourcesDetails()
|
val response = repository.getSourcesDetails()
|
||||||
if (response.isNotEmpty()) {
|
if (response.isNotEmpty()) {
|
||||||
@ -74,7 +71,6 @@ class SourcesActivity : AppCompatActivity(), DIAware {
|
|||||||
Toast.LENGTH_SHORT,
|
Toast.LENGTH_SHORT,
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.fab.setOnClickListener {
|
binding.fab.setOnClickListener {
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.testing
|
|
||||||
|
|
||||||
import androidx.test.espresso.idling.CountingIdlingResource
|
|
||||||
|
|
||||||
object CountingIdlingResourceSingleton {
|
|
||||||
|
|
||||||
private const val RESOURCE = "GLOBAL"
|
|
||||||
|
|
||||||
@JvmField
|
|
||||||
val countingIdlingResource = CountingIdlingResource(RESOURCE)
|
|
||||||
|
|
||||||
fun increment() {
|
|
||||||
countingIdlingResource.increment()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decrement() {
|
|
||||||
if (!countingIdlingResource.isIdleNow) {
|
|
||||||
countingIdlingResource.decrement()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.testing
|
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
|
|
||||||
|
|
||||||
class TestingHelper {
|
|
||||||
fun isUnitTest(): Boolean {
|
|
||||||
var device = Build.DEVICE
|
|
||||||
var product = Build.PRODUCT
|
|
||||||
if (device == null) {
|
|
||||||
device = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if (product == null) {
|
|
||||||
product = ""
|
|
||||||
}
|
|
||||||
return device == "robolectric" && product == "robolectric"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +1,11 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.repository
|
package bou.amine.apps.readerforselfossv2.repository
|
||||||
|
|
||||||
import bou.amine.apps.readerforselfossv2.utils.toParsedDate
|
import bou.amine.apps.readerforselfossv2.utils.DateUtils
|
||||||
|
import junit.framework.TestCase.assertEquals
|
||||||
import kotlinx.datetime.LocalDateTime
|
import kotlinx.datetime.LocalDateTime
|
||||||
import kotlinx.datetime.TimeZone
|
import kotlinx.datetime.TimeZone
|
||||||
import kotlinx.datetime.toInstant
|
import kotlinx.datetime.toInstant
|
||||||
import kotlin.test.Test
|
import org.junit.Test
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
class DatesTest {
|
class DatesTest {
|
||||||
private val newVersionDateVariant = "2022-12-24T17:00:08+00"
|
private val newVersionDateVariant = "2022-12-24T17:00:08+00"
|
||||||
@ -17,7 +17,7 @@ class DatesTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun new_version_date_should_be_parsed() {
|
fun new_version_date_should_be_parsed() {
|
||||||
val date = newVersionDate.toParsedDate()
|
val date = DateUtils.parseDate(newVersionDate)
|
||||||
val expected =
|
val expected =
|
||||||
LocalDateTime(2013, 4, 7, 13, 43, 0, 0).toInstant(TimeZone.currentSystemDefault())
|
LocalDateTime(2013, 4, 7, 13, 43, 0, 0).toInstant(TimeZone.currentSystemDefault())
|
||||||
.toEpochMilliseconds()
|
.toEpochMilliseconds()
|
||||||
@ -26,7 +26,7 @@ class DatesTest {
|
|||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
fun new_version_date2_should_be_parsed() {
|
fun new_version_date2_should_be_parsed() {
|
||||||
val date = newVersionDate2.toParsedDate()
|
val date = DateUtils.parseDate(newVersionDate2)
|
||||||
val expected =
|
val expected =
|
||||||
LocalDateTime(2013, 4, 7, 13, 43, 0, 0).toInstant(TimeZone.currentSystemDefault())
|
LocalDateTime(2013, 4, 7, 13, 43, 0, 0).toInstant(TimeZone.currentSystemDefault())
|
||||||
.toEpochMilliseconds()
|
.toEpochMilliseconds()
|
||||||
@ -36,7 +36,7 @@ class DatesTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun old_version_date_should_be_parsed() {
|
fun old_version_date_should_be_parsed() {
|
||||||
val date = oldVersionDate.toParsedDate()
|
val date = DateUtils.parseDate(oldVersionDate)
|
||||||
val expected =
|
val expected =
|
||||||
LocalDateTime(2013, 5, 7, 13, 46, 0, 0).toInstant(TimeZone.currentSystemDefault())
|
LocalDateTime(2013, 5, 7, 13, 46, 0, 0).toInstant(TimeZone.currentSystemDefault())
|
||||||
.toEpochMilliseconds()
|
.toEpochMilliseconds()
|
||||||
@ -46,7 +46,7 @@ class DatesTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun old_version_variant_date_should_be_parsed() {
|
fun old_version_variant_date_should_be_parsed() {
|
||||||
val date = oldVersionDateVariant.toParsedDate()
|
val date = DateUtils.parseDate(oldVersionDateVariant)
|
||||||
val expected =
|
val expected =
|
||||||
LocalDateTime(2021, 3, 21, 10, 32, 0, 0).toInstant(TimeZone.currentSystemDefault())
|
LocalDateTime(2021, 3, 21, 10, 32, 0, 0).toInstant(TimeZone.currentSystemDefault())
|
||||||
.toEpochMilliseconds()
|
.toEpochMilliseconds()
|
||||||
@ -56,7 +56,7 @@ class DatesTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun new_version_variant_date_should_be_parsed() {
|
fun new_version_variant_date_should_be_parsed() {
|
||||||
val date = newVersionDateVariant.toParsedDate()
|
val date = DateUtils.parseDate(newVersionDateVariant)
|
||||||
val expected =
|
val expected =
|
||||||
LocalDateTime(2022, 12, 24, 17, 0, 8, 0).toInstant(TimeZone.currentSystemDefault())
|
LocalDateTime(2022, 12, 24, 17, 0, 8, 0).toInstant(TimeZone.currentSystemDefault())
|
||||||
.toEpochMilliseconds()
|
.toEpochMilliseconds()
|
@ -1,81 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.tests.robolectric
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.widget.Button
|
|
||||||
import android.widget.EditText
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.HomeActivity
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.LoginActivity
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
|
||||||
import com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.robolectric.Robolectric
|
|
||||||
import org.robolectric.RuntimeEnvironment
|
|
||||||
import org.robolectric.Shadows.shadowOf
|
|
||||||
|
|
||||||
|
|
||||||
@RunWith(RobotElectriqueRunnerclass::class)
|
|
||||||
class LoginActivityTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun login_shouldDisplay() {
|
|
||||||
Robolectric.buildActivity(LoginActivity::class.java).use { controller ->
|
|
||||||
controller.setup() // Moves the Activity to the RESUMED state
|
|
||||||
|
|
||||||
val activity = controller.get()
|
|
||||||
assert(activity.findViewById<EditText>(R.id.urlView).isVisible)
|
|
||||||
assert(activity.findViewById<SwitchMaterial>(R.id.selfSigned).isVisible)
|
|
||||||
assert(activity.findViewById<SwitchMaterial>(R.id.selfSigned).isChecked.not())
|
|
||||||
assert(activity.findViewById<SwitchMaterial>(R.id.withLogin).isVisible)
|
|
||||||
assert(activity.findViewById<SwitchMaterial>(R.id.withLogin).isChecked.not())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun urlError() {
|
|
||||||
Robolectric.buildActivity(LoginActivity::class.java).use { controller ->
|
|
||||||
controller.setup() // Moves the Activity to the RESUMED state
|
|
||||||
val activity = controller.get()
|
|
||||||
|
|
||||||
val urlView = activity.findViewById<EditText>(R.id.urlView)
|
|
||||||
urlView.setText("172.17.0.1:8888")
|
|
||||||
|
|
||||||
activity.findViewById<Button>(R.id.signInButton).performClick()
|
|
||||||
|
|
||||||
urlView.performClick()
|
|
||||||
assertEquals(activity.getString(R.string.login_url_problem), urlView.error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun multiError() {
|
|
||||||
Robolectric.buildActivity(LoginActivity::class.java).use { controller ->
|
|
||||||
controller.setup() // Moves the Activity to the RESUMED state
|
|
||||||
val activity = controller.get()
|
|
||||||
|
|
||||||
val signInButton = activity.findViewById<Button>(R.id.signInButton)
|
|
||||||
repeat(3) { signInButton.performClick() }
|
|
||||||
|
|
||||||
// Vérifie que l'avertissement est affiché
|
|
||||||
assertEquals(activity.getString(R.string.text_wrong_url), dialogMessage())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun connect() {
|
|
||||||
Robolectric.buildActivity(LoginActivity::class.java).use { controller ->
|
|
||||||
controller.setup() // Moves the Activity to the RESUMED state
|
|
||||||
val activity = controller.get()
|
|
||||||
val signInButton = activity.findViewById<Button>(R.id.signInButton)
|
|
||||||
val urlView = activity.findViewById<EditText>(R.id.urlView)
|
|
||||||
urlView.setText("http://10.0.2.2:8888")
|
|
||||||
signInButton.performClick()
|
|
||||||
|
|
||||||
val expectedIntent = Intent(activity, HomeActivity::class.java)
|
|
||||||
val actual = shadowOf(RuntimeEnvironment.application).nextStartedActivity
|
|
||||||
assertEquals(expectedIntent.component, actual.component)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.tests.robolectric
|
|
||||||
|
|
||||||
import org.robolectric.RobolectricTestRunner
|
|
||||||
import org.robolectric.annotation.Config
|
|
||||||
|
|
||||||
class RobotElectriqueRunnerclass(testClass: Class<*>?) :
|
|
||||||
RobolectricTestRunner(testClass) {
|
|
||||||
|
|
||||||
override fun buildGlobalConfig(): Config {
|
|
||||||
return Config.Builder().setSdk(25, 30, 33).build()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.tests.robolectric
|
|
||||||
|
|
||||||
import android.widget.TextView
|
|
||||||
import org.robolectric.shadows.ShadowDialog
|
|
||||||
|
|
||||||
fun dialogMessage(): String {
|
|
||||||
val latestDialog = ShadowDialog.getLatestDialog()
|
|
||||||
return latestDialog.findViewById<TextView>(android.R.id.message)?.text.toString()
|
|
||||||
}
|
|
@ -7,10 +7,10 @@ buildscript {
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
//trick: for the same plugin versions in all sub-modules
|
//trick: for the same plugin versions in all sub-modules
|
||||||
id("com.android.application").version("8.7.3").apply(false)
|
id("com.android.application").version("8.7.2").apply(false)
|
||||||
id("com.android.library").version("8.7.3").apply(false)
|
id("com.android.library").version("8.7.2").apply(false)
|
||||||
id("org.jetbrains.kotlin.android").version("2.1.0").apply(false)
|
id("org.jetbrains.kotlin.android").version("1.9.10").apply(false)
|
||||||
kotlin("multiplatform").version("2.1.0").apply(false)
|
kotlin("multiplatform").version("1.9.10").apply(false)
|
||||||
id("com.mikepenz.aboutlibraries.plugin").version("10.5.1").apply(false)
|
id("com.mikepenz.aboutlibraries.plugin").version("10.5.1").apply(false)
|
||||||
id("org.jetbrains.kotlinx.kover").version("0.6.1").apply(true)
|
id("org.jetbrains.kotlinx.kover").version("0.6.1").apply(true)
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ allprojects {
|
|||||||
|
|
||||||
|
|
||||||
tasks.register("clean", Delete::class) {
|
tasks.register("clean", Delete::class) {
|
||||||
delete(layout.buildDirectory)
|
delete(rootProject.buildDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
koverMerged {
|
koverMerged {
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
**v124123421**
|
|
||||||
|
|
||||||
- fix: Trying to fix the serialization issue.
|
|
||||||
- Changelog for v124113311
|
|
@ -26,4 +26,3 @@ org.gradle.parallel=true
|
|||||||
org.gradle.caching=true
|
org.gradle.caching=true
|
||||||
ignoreGitVersion=false
|
ignoreGitVersion=false
|
||||||
kotlin.native.cacheKind.iosX64=none
|
kotlin.native.cacheKind.iosX64=none
|
||||||
org.gradle.configureondemand=true
|
|
@ -173,7 +173,7 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "export JAVA_HOME=/Users/amine/.sdkman/candidates/java/17.0.8.1-jbr\ncd \"$SRCROOT/..\"\n./gradlew :shared:embedAndSignAppleFrameworkForXcode --stacktrace\n";
|
shellScript = "cd \"$SRCROOT/..\"\n./gradlew :shared:embedAndSignAppleFrameworkForXcode\n";
|
||||||
};
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
@ -52,9 +52,6 @@ kotlin {
|
|||||||
|
|
||||||
// Sql
|
// Sql
|
||||||
implementation(SqlDelight.runtime)
|
implementation(SqlDelight.runtime)
|
||||||
|
|
||||||
// Sql
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val commonTest by getting {
|
val commonTest by getting {
|
||||||
@ -83,6 +80,10 @@ kotlin {
|
|||||||
val iosArm64Main by getting
|
val iosArm64Main by getting
|
||||||
// val iosSimulatorArm64Main by getting
|
// val iosSimulatorArm64Main by getting
|
||||||
val iosMain by creating {
|
val iosMain by creating {
|
||||||
|
dependsOn(commonMain)
|
||||||
|
iosX64Main.dependsOn(this)
|
||||||
|
iosArm64Main.dependsOn(this)
|
||||||
|
// iosSimulatorArm64Main.dependsOn(this)
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(SqlDelight.native)
|
implementation(SqlDelight.native)
|
||||||
@ -93,6 +94,10 @@ kotlin {
|
|||||||
val iosArm64Test by getting
|
val iosArm64Test by getting
|
||||||
// val iosSimulatorArm64Test by getting
|
// val iosSimulatorArm64Test by getting
|
||||||
val iosTest by creating {
|
val iosTest by creating {
|
||||||
|
dependsOn(commonTest)
|
||||||
|
iosX64Test.dependsOn(this)
|
||||||
|
iosArm64Test.dependsOn(this)
|
||||||
|
// iosSimulatorArm64Test.dependsOn(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,33 @@ import kotlinx.datetime.*
|
|||||||
|
|
||||||
actual class DateUtils {
|
actual class DateUtils {
|
||||||
actual companion object {
|
actual companion object {
|
||||||
|
// Possible formats are
|
||||||
|
// yyyy-mm-dd hh:mm:ss format
|
||||||
|
private val oldVersionFormat = "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(.()\\d*)?".toRegex()
|
||||||
|
|
||||||
|
// yyyy-MM-dd'T'HH:mm:ss[.SSS]XXX (RFC3339)
|
||||||
|
private val newVersionFormat = "(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2})[+-](\\d{2}(:\\d{2})?)?".toRegex()
|
||||||
|
|
||||||
|
// TODO: do not fix any more issues here. Move everything to plateform specific code.
|
||||||
|
actual fun parseDate(dateString: String): Long {
|
||||||
|
var isoDateString: String =
|
||||||
|
try {
|
||||||
|
if (dateString.matches(oldVersionFormat)) {
|
||||||
|
dateString.replace(" ", "T")
|
||||||
|
} else if (dateString.matches(newVersionFormat)) {
|
||||||
|
newVersionFormat.find(dateString)?.groups?.get(1)?.value ?: throw Exception("Couldn't parse $dateString")
|
||||||
|
} else {
|
||||||
|
throw Exception("Unrecognized format for $dateString")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw Exception("parseDate failed for $dateString", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return LocalDateTime.parse(isoDateString).toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds()
|
||||||
|
}
|
||||||
|
|
||||||
actual fun parseRelativeDate(dateString: String): String {
|
actual fun parseRelativeDate(dateString: String): String {
|
||||||
val date = dateString.toParsedDate()
|
val date = parseDate(dateString)
|
||||||
|
|
||||||
return " " +
|
return " " +
|
||||||
DateUtils.getRelativeTimeSpanString(
|
DateUtils.getRelativeTimeSpanString(
|
||||||
|
@ -74,7 +74,7 @@ class Repository(
|
|||||||
dbItems = dbItems.filter { it.sourcetitle == sourceFilter.value!!.title }
|
dbItems = dbItems.filter { it.sourcetitle == sourceFilter.value!!.title }
|
||||||
}
|
}
|
||||||
val itemsList = ArrayList(dbItems.map { it.toView() })
|
val itemsList = ArrayList(dbItems.map { it.toView() })
|
||||||
itemsList.sortByDescending { it.datetime.toParsedDate() }
|
itemsList.sortByDescending { DateUtils.parseDate(it.datetime) }
|
||||||
fetchedItems =
|
fetchedItems =
|
||||||
StatusAndData.succes(
|
StatusAndData.succes(
|
||||||
itemsList,
|
itemsList,
|
||||||
|
@ -1,35 +1,9 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.utils
|
package bou.amine.apps.readerforselfossv2.utils
|
||||||
|
|
||||||
import kotlinx.datetime.LocalDateTime
|
|
||||||
import kotlinx.datetime.TimeZone
|
|
||||||
import kotlinx.datetime.toInstant
|
|
||||||
|
|
||||||
fun String.toParsedDate(): Long {
|
|
||||||
// Possible formats are
|
|
||||||
// yyyy-mm-dd hh:mm:ss format
|
|
||||||
val oldVersionFormat = "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(.()\\d*)?".toRegex()
|
|
||||||
|
|
||||||
// yyyy-MM-dd'T'HH:mm:ss[.SSS]XXX (RFC3339)
|
|
||||||
val newVersionFormat = "(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2})[+-](\\d{2}(:\\d{2})?)?".toRegex()
|
|
||||||
|
|
||||||
val isoDateString: String =
|
|
||||||
try {
|
|
||||||
if (this.matches(oldVersionFormat)) {
|
|
||||||
this.replace(" ", "T")
|
|
||||||
} else if (this.matches(newVersionFormat)) {
|
|
||||||
newVersionFormat.find(this)?.groups?.get(1)?.value ?: throw Exception("Couldn't parse $this")
|
|
||||||
} else {
|
|
||||||
throw Exception("Unrecognized format for $this")
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw Exception("parseDate failed for $this", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
return LocalDateTime.parse(isoDateString).toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds()
|
|
||||||
}
|
|
||||||
|
|
||||||
expect class DateUtils() {
|
expect class DateUtils() {
|
||||||
companion object {
|
companion object {
|
||||||
|
fun parseDate(dateString: String): Long
|
||||||
|
|
||||||
fun parseRelativeDate(dateString: String): String
|
fun parseRelativeDate(dateString: String): String
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.utils
|
|
||||||
|
|
||||||
actual class DateUtils actual constructor() {
|
|
||||||
actual companion object {
|
|
||||||
actual fun parseRelativeDate(dateString: String): String {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user