Skip to content

Commit

Permalink
Merge pull request #3 from sergio-sastre/#issue1/not_working_from_api26+
Browse files Browse the repository at this point in the history
#issue1/not working from api26+
  • Loading branch information
sergio-sastre authored Sep 5, 2021
2 parents 3c4de2a + 73898ca commit 9ab6900
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 36 deletions.
50 changes: 47 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# FontSizeRule
A Junit4 TestRule to be used together with its `org.junit.runners.Parameterized`. It simulates changing the font size on a device/emulator, as it would be done by going to "Settings > display > Font size"
# FontSizeActivityScenario and FontSizeTestRule
An ActivityScenario and a Junit4 TestRule to be used together with its `org.junit.runners.Parameterized`. It simulates changing the font size on a device/emulator, as it would be done by going to "Settings > display > Font size"

This helps to write snapshot tests that can catch visual regresion bugs like this one

Expand Down Expand Up @@ -32,5 +32,49 @@ dependencies {
}
```

or if you want to try the `FontSizeActivityScenario`:
```groovy
dependencies {
androidTestImplementation 'com.github.sergio-sastre:FontSizeTestRule:v1.1.0-SNAPSHOT'
}
```

## What to keep in mind
Prefer to use FontSizeActivityScenario over FontSizeTestRule. That is because Google deprecated
`resources.updateConfiguration(resources.configuration, metrics)` on **API 25+**, and the test rule is based on that.
Therefore, the current implementation of the testRule does not work on devices/emulators running 25+.
In order to solve this, from 25+, the rule will execute the corresponding adb shell command to change the font size:
<br/>
`"settings put system font_scale ${fontScale.value}`
</br>
However, this has the drawback that the font size change does not happen immediately, making your tests run more slow.


This is overcome by using `FontSizeActivityScenario.launchWith(fontScale)`, currently available on
'com.github.sergio-sastre:FontSizeTestRule:v1.1.0-SNAPSHOT'. The only inconvenient is that you cannot snapshot-test your
own activities with it. That is because in order to use `resources.updateConfiguration(resources.configuration, metrics)` replacement,
we need to override `attachBaseContext()` in the activity.

*Summary*
| **FontSizeActivityScenario** | **FontSizeTestRule** |
|----------|:-------------:|
|Any API | < API 25, otherwise more slow |
| **Cannot** snapshot-test *Activities* | **Can** snapshot-test any View|

## Usage
To use `FontSizeActivityScenario` you need to add the following activities to your `debug/manifest`
```xml
<application
...
<activity android:name="sergio.sastre.fontsize.SmallFontSizeActivity"></activity>
<activity android:name="sergio.sastre.fontsize.NormalFontSizeActivity"></activity>
<activity android:name="sergio.sastre.fontsize.LargeFontSizeActivity"></activity>
<activity android:name="sergio.sastre.fontsize.HugeFontSizeActivity"></activity>
</application>
```

## Code Samples
You can find test samples of how to use FontScaleTestRule in the [Road to Effective Snapshot Testing](https://github.com/sergio-sastre/RoadToEffectiveSnapshotTesting) repo, in the [DeleteDialogTest.kt](https://github.com/sergio-sastre/RoadToEffectiveSnapshotTesting/blob/master/app/src/androidTest/java/com/example/road/to/effective/snapshot/testing/parameterized/DeleteDialogTest.kt) file.
You can find test samples of how to use them in the [Road to Effective Snapshot Testing](https://github.com/sergio-sastre/RoadToEffectiveSnapshotTesting) repo
1. FontActivityScenario -> TBD
2. FontScaleTestRule -> in the [DeleteDialogTest.kt](https://github.com/sergio-sastre/RoadToEffectiveSnapshotTesting/blob/master/app/src/androidTest/java/com/example/road/to/effective/snapshot/testing/parameterized/DeleteDialogTest.kt) file.

1 change: 1 addition & 0 deletions rules/fontsize/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies {
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.test.ext:junit:1.1.3'
implementation 'androidx.appcompat:appcompat:1.3.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package sergio.sastre.fontsize

import android.content.Context
import android.content.res.Configuration
import androidx.appcompat.app.AppCompatActivity
import androidx.test.core.app.ActivityScenario

object FontSizeActivityScenario {
fun launchWith(fontScale: FontScale): ActivityScenario<out MockFontSizeActivity> {
val clazz = when (fontScale){
FontScale.SMALL -> SmallFontSizeActivity::class.java
FontScale.NORMAL -> NormalFontSizeActivity::class.java
FontScale.LARGE -> LargeFontSizeActivity::class.java
FontScale.HUGE -> HugeFontSizeActivity::class.java
}
return ActivityScenario.launch(clazz)
}
}

abstract class MockFontSizeActivity : AppCompatActivity() {
abstract val fontScale: FontScale

override fun attachBaseContext(newBase: Context?) {
val newConfig = Configuration(newBase!!.resources.configuration)
newConfig.fontScale = fontScale.value.toFloat()
applyOverrideConfiguration(newConfig)
super.attachBaseContext(newBase)
}
}

class SmallFontSizeActivity : MockFontSizeActivity() {
override val fontScale: FontScale
get() = FontScale.SMALL
}

class NormalFontSizeActivity : MockFontSizeActivity() {
override val fontScale: FontScale
get() = FontScale.NORMAL
}

class LargeFontSizeActivity : MockFontSizeActivity() {
override val fontScale: FontScale
get() = FontScale.LARGE
}

class HugeFontSizeActivity : MockFontSizeActivity() {
override val fontScale: FontScale
get() = FontScale.HUGE
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package sergio.sastre.fontsize
package sergio.sastre.fontsize.testrule

/**
* Taken from : https://github.com/novoda/espresso-support
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package sergio.sastre.fontsize
package sergio.sastre.fontsize.testrule

import androidx.test.platform.app.InstrumentationRegistry
import sergio.sastre.fontsize.FontScale

object FontScaleRules {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package sergio.sastre.fontsize.testrule

/**
* Concept taken from : https://github.com/novoda/espresso-support
*/
import android.content.res.Resources
import android.os.Build
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import sergio.sastre.fontsize.FontScale

class FontScaleSetting internal constructor(private val resources: Resources) {

fun get(): FontScale {
return FontScale.from(resources.configuration.fontScale)
}

fun set(scale: FontScale) {
try {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N){
changeFontScaleFromApi25(scale)
} else {
changeFontScalePreApi25(scale)
}

} catch (e: Exception) {
throw saveFontScaleError(scale)
}
}

private fun changeFontScalePreApi25(scale: FontScale){
resources.configuration.fontScale = java.lang.Float.parseFloat(scale.value)
val metrics = Resources.getSystem().displayMetrics
metrics.scaledDensity = resources.configuration.fontScale * metrics.density
resources.updateConfiguration(resources.configuration, metrics)
}

private fun changeFontScaleFromApi25(scale: FontScale){
getInstrumentation().uiAutomation.executeShellCommand("settings put system font_scale " + scale.value)
}

private fun saveFontScaleError(scale: FontScale): RuntimeException {
return RuntimeException("Unable to save font size " + scale.name)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package sergio.sastre.fontsize
package sergio.sastre.fontsize.testrule

import android.os.SystemClock

Expand All @@ -8,6 +8,7 @@ import org.junit.runner.Description
import org.junit.runners.model.Statement

import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import sergio.sastre.fontsize.FontScale

class FontScaleTestRule(private val fontScaleSetting: FontScaleSetting, private val fontScale: FontScale) : TestWatcher(), TestRule {
private var previousScale: Float = 0.toFloat()
Expand Down

0 comments on commit 9ab6900

Please sign in to comment.