-
Notifications
You must be signed in to change notification settings - Fork 392
feat: add ability to switch themes on iOS and Android: setDarkMode & toggleDarkMode #2507
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Adde docs PR to document new feature - mobile-dev-inc/maestro-docs#133 |
@markrickert does this handle specifying the device? |
@morganick On iOS, it should pass through the device id and if you don't pass one, it'll use "booted" as a default. I'm unsure how android handles this. |
@markrickert Yeah, I saw the |
@morganick I believe i've addressed the problem. I was using the regular |
"setDarkMode command takes either: \n" + | ||
"\t1. enabled: To enable dark mode\n" + | ||
"\t2. disabled: To disable dark mode\n" + | ||
"\t3. value: To set dark mode to a specific value (enabled or disabled) \n" + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was a bit confused about this line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was copying the airplane mode function as it allows this syntax:
- setDarkMode enabled
or
- setDarkMode
value: enabled
label: "Something"
I'm definitely open to suggestions on how to improve this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah I think the syntax makes sense. The 3rd line of the error message is what was confusing me - seems redundant?
1. enabled: To enable dark mode
2. disabled: To disable dark mode
3. value: To set dark mode to a specific value (enabled or disabled)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh ok I think I understand now. Maybe it would be clearer to show those two styles of syntax as examples in the error message
|
@@ -515,6 +515,16 @@ class IOSDriver( | |||
LOGGER.warn("Airplane mode is not available on iOS simulators") | |||
} | |||
|
|||
override fun isDarkModeEnabled(): Boolean { | |||
val deviceId = iosDevice.deviceId ?: "booted" | |||
return XCRunnerCLIUtils.isDarkModeEnabled(deviceId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to do this via xcuitest? We're trying to move away from simctl commands since they won't work for physical devices
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll look into this tomorrow to see if there's a way to do it. Not sure at the moment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Leland-Takamine I found this reference already inside the repo's source code, but i have no idea how i would go about using it 🤷 https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCUIDevice.h#L18-L22
Edit: Also found this SO answer that indicates we might be able to just do:
if #available(iOS 15.0, *) {
XCUIDevice.shared.appearance = .dark
}
But this is my first time ever looking at XCUIDevice so i have no idea how i'd implement that 😬
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @markrickert thanks for contribution, I think this is would be a new endpoint in our XCUITest driver here:
You can check any one handler there for example. On implementation we would receive the appearence and then as you shared:
switch requestBody.appearance.lowercased() {
case "dark":
XCUIDevice.shared.appearance = .dark
case "light":
XCUIDevice.shared.appearance = .light
default: return AppError(type: .precondition, message: "Invalid appearance value. Use 'dark' or 'light'").httpResponse
}
And this endpoint would be reached from XCTest client here in maestro: https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-ios/src/main/java/ios/xctest/XCTestIOSDevice.kt
@@ -478,6 +482,25 @@ data class YamlFluentCommand( | |||
) | |||
) | |||
|
|||
setDarkMode != null -> listOf( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder are there cases where it is useful to set appearance mid flow execution?
Is this more useful as a command or flow based or upload level config?
value: enabled | ||
label: "Turn on dark mode for testing" | ||
- toggleDarkMode: | ||
label: "Toggle dark mode for testing" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the difference between these two commands?
Can't I toggle with reverting the value passed in setDarkMode command? 🤔
|
||
// Then | ||
// No test failures | ||
driver.assertNoInteraction() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like incomplete we do want to assert the appearance event here though right?
} | ||
|
||
override fun evaluateScripts(jsEngine: JsEngine): Command { | ||
return this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be allowing setting this variable with js? Not sure. Like runtime through JS. If yes then we might want to handle this.
Proposed changes
This is a new feature that adds the ability to set the
light
/dark
mode on iOS and Android platforms.It adds the following new functionality:
setDarkMode
- possible values:enabled
/disabled
toggleDarkMode
- swaps between light/dark modeIt achieves this by running commands for iOS/Android:
setDarkMode: enabled
xcrun simctl ui booted appearance dark
adb shell 'cmd uimode night yes'
setDarkMode: disabled
xcrun simctl ui booted appearance light
adb shell 'cmd uimode night no'
Possible enhancements
There's a way to change the macOS theme and toggle between light/dark mode from the terminal but it requires you use applescript on the commandline. This would be for the web implementation to toggle back and forth since there's no current web implementation of this feature.
Testing
./gradlew :maestro-test:test && ./gradlew test
passes 🟢I then "ignited" an app that has a default maestro test in it called
FavoritePodcast.yaml
and replaced the contents with the following, adding calls tosetDarkMode
andtoggleDarkMode
Then to test this, i ran the associated application on the iOS simulator and from this projects' directory i ran:
Here is the resulting video showing the interface swapping back and forth from light to dark:
maestro-record.mp4
Issues fixed
n/a