diff --git a/testing/marionette/client/marionette/marionette.py b/testing/marionette/client/marionette/marionette.py index 5b012a7ec48..50bcb6aad5b 100644 --- a/testing/marionette/client/marionette/marionette.py +++ b/testing/marionette/client/marionette/marionette.py @@ -9,13 +9,14 @@ import sys import time import traceback -from client import MarionetteClient from application_cache import ApplicationCache -from keys import Keys -from errors import * +from client import MarionetteClient from emulator import Emulator -import geckoinstance +from emulator_screen import EmulatorScreen +from errors import * +from keys import Keys +import geckoinstance class HTMLElement(object): """ @@ -421,6 +422,12 @@ class Marionette(object): TIMEOUT_SEARCH = 'implicit' TIMEOUT_SCRIPT = 'script' TIMEOUT_PAGE = 'page load' + SCREEN_ORIENTATIONS = {"portrait": EmulatorScreen.SO_PORTRAIT_PRIMARY, + "landscape": EmulatorScreen.SO_LANDSCAPE_PRIMARY, + "portrait-primary": EmulatorScreen.SO_PORTRAIT_PRIMARY, + "landscape-primary": EmulatorScreen.SO_LANDSCAPE_PRIMARY, + "portrait-secondary": EmulatorScreen.SO_PORTRAIT_SECONDARY, + "landscape-secondary": EmulatorScreen.SO_LANDSCAPE_SECONDARY} def __init__(self, host='localhost', port=2828, app=None, app_args=None, bin=None, profile=None, emulator=None, sdcard=None, emulatorBinary=None, @@ -592,7 +599,7 @@ class Marionette(object): status = response['error'].get('status', 500) message = response['error'].get('message') stacktrace = response['error'].get('stacktrace') - # status numbers come from + # status numbers come from # http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes if status == ErrorCodes.NO_SUCH_ELEMENT: raise NoSuchElementException(message=message, status=status, stacktrace=stacktrace) @@ -1280,3 +1287,33 @@ class Marionette(object): if highlights is not None: lights = [highlight.id for highlight in highlights if highlights] return self._send_message("screenShot", 'value', id=element, highlights=lights) + + @property + def orientation(self): + """Get the current browser orientation. + + Will return one of the valid primary orientation values + portrait-primary, landscape-primary, portrait-secondary, or + landscape-secondary. + + """ + return self._send_message("getScreenOrientation", "value") + + def set_orientation(self, orientation): + """Set the current browser orientation. + + The supplied orientation should be given as one of the valid + orientation values. If the orientation is unknown, an error + will be raised. + + Valid orientations are "portrait" and "landscape", which fall + back to "portrait-primary" and "landscape-primary" + respectively, and "portrait-secondary" as well as + "landscape-secondary". + + :param orientation: The orientation to lock the screen in. + + """ + self._send_message("setScreenOrientation", "ok", orientation=orientation) + if self.emulator: + self.emulator.screen.orientation = self.SCREEN_ORIENTATIONS[orientation.lower()] diff --git a/testing/marionette/client/marionette/tests/unit/test_screen_orientation.py b/testing/marionette/client/marionette/tests/unit/test_screen_orientation.py new file mode 100644 index 00000000000..bdb76ef5b89 --- /dev/null +++ b/testing/marionette/client/marionette/tests/unit/test_screen_orientation.py @@ -0,0 +1,90 @@ +# -*- fill-column: 100; comment-column: 100; -*- + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from emulator_screen import EmulatorScreen +from marionette import MarionetteException +from marionette_test import MarionetteTestCase + +default_orientation = "portrait-primary" +unknown_orientation = "Unknown screen orientation: %s" + +class TestScreenOrientation(MarionetteTestCase): + def tearDown(self): + self.marionette.set_orientation(default_orientation) + self.assertEqual(self.marionette.orientation, default_orientation, "invalid state") + super(MarionetteTestCase, self).tearDown() + + def test_set_orientation_to_portrait_primary(self): + self.marionette.set_orientation("portrait-primary") + new_orientation = self.marionette.orientation + self.assertEqual(new_orientation, "portrait-primary") + + if self.marionette.emulator: + emulator_orientation = self.marionette.emulator.screen.orientation + self.assertEqual(emulator_orientation, EmulatorScreen.SO_PORTRAIT_PRIMARY) + + def test_set_orientation_to_landscape_primary(self): + self.marionette.set_orientation("landscape-primary") + new_orientation = self.marionette.orientation + self.assertEqual(new_orientation, "landscape-primary") + + if self.marionette.emulator: + emulator_orientation = self.marionette.emulator.screen.orientation + self.assertEqual(emulator_orientation, EmulatorScreen.SO_LANDSCAPE_PRIMARY) + + def test_set_orientation_to_portrait_secondary(self): + self.marionette.set_orientation("portrait-secondary") + new_orientation = self.marionette.orientation + self.assertEqual(new_orientation, "portrait-secondary") + + if self.marionette.emulator: + emulator_orientation = self.marionette.emulator.screen.orientation + self.assertEqual(emulator_orientation, EmulatorScreen.SO_PORTRAIT_SECONDARY) + + def test_set_orientation_to_landscape_secondary(self): + self.marionette.set_orientation("landscape-secondary") + new_orientation = self.marionette.orientation + self.assertEqual(new_orientation, "landscape-secondary") + + if self.marionette.emulator: + emulator_orientation = self.marionette.emulator.screen.orientation + self.assertEqual(emulator_orientation, EmulatorScreen.SO_LANDSCAPE_SECONDARY) + + def test_set_orientation_to_shorthand_portrait(self): + # Set orientation to something other than portrait-primary first, since the default is + # portrait-primary. + self.marionette.set_orientation("landscape-primary") + self.assertEqual(self.marionette.orientation, "landscape-primary", "invalid state") + + self.marionette.set_orientation("portrait") + new_orientation = self.marionette.orientation + self.assertEqual(new_orientation, "portrait-primary") + + if self.marionette.emulator: + emulator_orientation = self.marionette.emulator.screen.orientation + self.assertEqual(emulator_orientation, EmulatorScreen.SO_PORTRAIT_PRIMARY) + + def test_set_orientation_to_shorthand_landscape(self): + self.marionette.set_orientation("landscape") + new_orientation = self.marionette.orientation + self.assertEqual(new_orientation, "landscape-primary") + + if self.marionette.emulator: + emulator_orientation = self.marionette.emulator.screen.orientation + self.assertEqual(emulator_orientation, EmulatorScreen.SO_LANDSCAPE_PRIMARY) + + def test_set_orientation_with_mixed_casing(self): + self.marionette.set_orientation("lAnDsCaPe") + new_orientation = self.marionette.orientation + self.assertEqual(new_orientation, "landscape-primary") + + def test_set_invalid_orientation(self): + with self.assertRaisesRegexp(MarionetteException, unknown_orientation % "cheese"): + self.marionette.set_orientation("cheese") + + def test_set_null_orientation(self): + with self.assertRaisesRegexp(MarionetteException, unknown_orientation % "null"): + self.marionette.set_orientation(None) diff --git a/testing/marionette/client/marionette/tests/unit/unit-tests.ini b/testing/marionette/client/marionette/tests/unit/unit-tests.ini index 3d80f9a37a3..9283e403ff2 100644 --- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini +++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini @@ -97,3 +97,5 @@ b2g = false [test_getactiveframe_oop.py] [test_submit.py] [test_chrome_async_finish.js] +[test_screen_orientation.py] +browser = false diff --git a/testing/marionette/marionette-server.js b/testing/marionette/marionette-server.js index 5c2c4abebee..e35a725c84f 100644 --- a/testing/marionette/marionette-server.js +++ b/testing/marionette/marionette-server.js @@ -2161,6 +2161,52 @@ MarionetteServerConnection.prototype = { this.command_id); }, + /** + * Get the current browser orientation. + * + * Will return one of the valid primary orientation values + * portrait-primary, landscape-primary, portrait-secondary, or + * landscape-secondary. + */ + getScreenOrientation: function MDA_getScreenOrientation(aRequest) { + this.command_id = this.getCommandId(); + let curWindow = this.getCurrentWindow(); + let or = curWindow.screen.mozOrientation; + this.sendResponse(or, this.command_id); + }, + + /** + * Set the current browser orientation. + * + * The supplied orientation should be given as one of the valid + * orientation values. If the orientation is unknown, an error will + * be raised. + * + * Valid orientations are "portrait" and "landscape", which fall + * back to "portrait-primary" and "landscape-primary" respectively, + * and "portrait-secondary" as well as "landscape-secondary". + */ + setScreenOrientation: function MDA_setScreenOrientation(aRequest) { + const ors = ["portrait", "landscape", + "portrait-primary", "landscape-primary", + "portrait-secondary", "landscape-secondary"]; + + this.command_id = this.getCommandId(); + let or = String(aRequest.parameters.orientation); + + let mozOr = or.toLowerCase(); + if (ors.indexOf(mozOr) < 0) { + this.sendError("Unknown screen orientation: " + or, 500, null, this.command_id); + return; + } + + let curWindow = this.getCurrentWindow(); + if (!curWindow.screen.mozLockOrientation(mozOr)) { + this.sendError("Unable to set screen orientation: " + or, 500, null, this.command_id); + } + this.sendOk(this.command_id); + }, + /** * Helper function to convert an outerWindowID into a UID that Marionette * tracks. @@ -2338,7 +2384,9 @@ MarionetteServerConnection.prototype.requestTypes = { "getAllCookies": MarionetteServerConnection.prototype.getAllCookies, "deleteAllCookies": MarionetteServerConnection.prototype.deleteAllCookies, "deleteCookie": MarionetteServerConnection.prototype.deleteCookie, - "getActiveElement": MarionetteServerConnection.prototype.getActiveElement + "getActiveElement": MarionetteServerConnection.prototype.getActiveElement, + "getScreenOrientation": MarionetteServerConnection.prototype.getScreenOrientation, + "setScreenOrientation": MarionetteServerConnection.prototype.setScreenOrientation }; /**