Skip to main content
Glama
Rotate.test.ts11.8 kB
import { assert } from "chai"; import { Rotate } from "../../../src/features/action/Rotate"; import { AdbUtils } from "../../../src/utils/android-cmdline-tools/adb"; import { ObserveScreen } from "../../../src/features/observe/ObserveScreen"; import { Window } from "../../../src/features/observe/Window"; import { AwaitIdle } from "../../../src/features/observe/AwaitIdle"; import { ExecResult, ObserveResult } from "../../../src/models"; import sinon from "sinon"; describe("Rotate", () => { let rotate: Rotate; let mockAdb: sinon.SinonStubbedInstance<AdbUtils>; let mockObserveScreen: sinon.SinonStubbedInstance<ObserveScreen>; let mockWindow: sinon.SinonStubbedInstance<Window>; let mockAwaitIdle: sinon.SinonStubbedInstance<AwaitIdle>; beforeEach(() => { // Create stubs for dependencies mockAdb = sinon.createStubInstance(AdbUtils); mockObserveScreen = sinon.createStubInstance(ObserveScreen); mockWindow = sinon.createStubInstance(Window); mockAwaitIdle = sinon.createStubInstance(AwaitIdle); // Stub the constructors sinon.stub(AdbUtils.prototype, "executeCommand").callsFake(mockAdb.executeCommand); sinon.stub(ObserveScreen.prototype, "execute").callsFake(mockObserveScreen.execute); sinon.stub(ObserveScreen.prototype, "getMostRecentCachedObserveResult").callsFake(mockObserveScreen.getMostRecentCachedObserveResult); sinon.stub(Window.prototype, "getCachedActiveWindow").callsFake(mockWindow.getCachedActiveWindow); sinon.stub(Window.prototype, "getActive").callsFake(mockWindow.getActive); sinon.stub(AwaitIdle.prototype, "initializeUiStabilityTracking").callsFake(mockAwaitIdle.initializeUiStabilityTracking); sinon.stub(AwaitIdle.prototype, "waitForUiStability").callsFake(mockAwaitIdle.waitForUiStability); sinon.stub(AwaitIdle.prototype, "waitForUiStabilityWithState").callsFake(mockAwaitIdle.waitForUiStabilityWithState); sinon.stub(AwaitIdle.prototype, "waitForRotation").callsFake(mockAwaitIdle.waitForRotation); // Set up default mock responses mockWindow.getCachedActiveWindow.resolves(null); mockWindow.getActive.resolves({ appId: "com.test.app", activityName: "MainActivity", layoutSeqSum: 123 }); mockAwaitIdle.initializeUiStabilityTracking.resolves(); mockAwaitIdle.waitForUiStability.resolves(); mockAwaitIdle.waitForUiStabilityWithState.resolves(); mockAwaitIdle.waitForRotation.resolves(); // Set up default observe screen responses with valid viewHierarchy const defaultObserveResult = createMockObserveResult(); mockObserveScreen.getMostRecentCachedObserveResult.resolves(defaultObserveResult); mockObserveScreen.execute.resolves(defaultObserveResult); rotate = new Rotate("test-device"); }); afterEach(() => { sinon.restore(); }); // Helper function to create mock ExecResult const createMockExecResult = (stdout: string = ""): ExecResult => ({ stdout, stderr: "", toString: () => stdout, trim: () => stdout.trim(), includes: (searchString: string) => stdout.includes(searchString) }); // Helper function to create mock ObserveResult const createMockObserveResult = (): ObserveResult => ({ timestamp: Date.now(), screenSize: { width: 1080, height: 1920 }, systemInsets: { top: 0, bottom: 0, left: 0, right: 0 }, viewHierarchy: { hierarchy: { node: { $: {} } } } }); describe("getCurrentOrientation", () => { it("should return portrait for user_rotation 0", async () => { mockAdb.executeCommand.resolves(createMockExecResult("0")); const orientation = await rotate.getCurrentOrientation(); assert.equal(orientation, "portrait"); sinon.assert.calledWith(mockAdb.executeCommand, "shell settings get system user_rotation"); }); it("should return landscape for user_rotation 1", async () => { mockAdb.executeCommand.resolves(createMockExecResult("1")); const orientation = await rotate.getCurrentOrientation(); assert.equal(orientation, "landscape"); }); it("should return portrait for user_rotation 2", async () => { mockAdb.executeCommand.resolves(createMockExecResult("2")); const orientation = await rotate.getCurrentOrientation(); assert.equal(orientation, "portrait"); }); it("should return landscape for user_rotation 3", async () => { mockAdb.executeCommand.resolves(createMockExecResult("3")); const orientation = await rotate.getCurrentOrientation(); assert.equal(orientation, "landscape"); }); it("should return portrait as default when ADB command fails", async () => { mockAdb.executeCommand.rejects(new Error("ADB command failed")); const orientation = await rotate.getCurrentOrientation(); assert.equal(orientation, "portrait"); }); }); describe("isOrientationLocked", () => { it("should return true when accelerometer_rotation is 0", async () => { mockAdb.executeCommand.resolves(createMockExecResult("0")); const isLocked = await rotate.isOrientationLocked(); assert.isTrue(isLocked); sinon.assert.calledWith(mockAdb.executeCommand, "shell settings get system accelerometer_rotation"); }); it("should return false when accelerometer_rotation is 1", async () => { mockAdb.executeCommand.resolves(createMockExecResult("1")); const isLocked = await rotate.isOrientationLocked(); assert.isFalse(isLocked); }); it("should return false as default when ADB command fails", async () => { mockAdb.executeCommand.rejects(new Error("ADB command failed")); const isLocked = await rotate.isOrientationLocked(); assert.isFalse(isLocked); }); }); describe("execute", () => { it("should skip rotation when already in desired orientation", async () => { // Mock getting current orientation as portrait mockAdb.executeCommand.withArgs("shell settings get system user_rotation").resolves(createMockExecResult("0")); const mockObservation = createMockObserveResult(); mockObserveScreen.execute.resolves(mockObservation); const result = await rotate.execute("portrait"); assert.isTrue(result.success); assert.equal(result.orientation, "portrait"); assert.isFalse(result.rotationPerformed); assert.include(result.message || "", "already in portrait orientation"); assert.isDefined(result.observation); // Should only call to get current orientation, not to set it sinon.assert.calledWith(mockAdb.executeCommand, "shell settings get system user_rotation"); sinon.assert.neverCalledWith(mockAdb.executeCommand, "shell settings put system user_rotation 0"); }); it("should perform rotation when orientation differs", async () => { // Mock getting current orientation as portrait initially mockAdb.executeCommand.withArgs("shell settings get system user_rotation") .onFirstCall().resolves(createMockExecResult("0")) .onSecondCall().resolves(createMockExecResult("1")); // After rotation, return landscape // Mock getting orientation lock status as unlocked mockAdb.executeCommand.withArgs("shell settings get system accelerometer_rotation").resolves(createMockExecResult("1")); // Mock successful unlock mockAdb.executeCommand.withArgs("shell settings put system accelerometer_rotation 0").resolves(createMockExecResult()); // Mock successful rotation mockAdb.executeCommand.withArgs("shell settings put system user_rotation 1").resolves(createMockExecResult()); // Mock successful restore lock mockAdb.executeCommand.withArgs("shell settings put system accelerometer_rotation 1").resolves(createMockExecResult()); const mockObservation = createMockObserveResult(); mockObserveScreen.execute.resolves(mockObservation); const result = await rotate.execute("landscape"); assert.isTrue(result.success); assert.equal(result.orientation, "landscape"); assert.equal(result.currentOrientation, "portrait"); assert.equal(result.previousOrientation, "portrait"); assert.isTrue(result.rotationPerformed); assert.include(result.message || "", "Successfully rotated from portrait to landscape"); // Verify rotation commands were called sinon.assert.calledWith(mockAdb.executeCommand, "shell settings put system accelerometer_rotation 0"); sinon.assert.calledWith(mockAdb.executeCommand, "shell settings put system user_rotation 1"); }); it("should handle locked orientation by temporarily unlocking", async () => { // Mock getting current orientation as landscape initially mockAdb.executeCommand.withArgs("shell settings get system user_rotation") .onFirstCall().resolves(createMockExecResult("1")) .onSecondCall().resolves(createMockExecResult("0")); // After rotation, return portrait // Mock getting orientation lock status as locked mockAdb.executeCommand.withArgs("shell settings get system accelerometer_rotation").resolves(createMockExecResult("0")); // Mock successful unlock mockAdb.executeCommand.withArgs("shell settings put system accelerometer_rotation 1").resolves(createMockExecResult()); // Mock successful rotation mockAdb.executeCommand.withArgs("shell settings put system user_rotation 0").resolves(createMockExecResult()); // Mock successful restore lock mockAdb.executeCommand.withArgs("shell settings put system accelerometer_rotation 0").resolves(createMockExecResult()); const mockObservation = createMockObserveResult(); mockObserveScreen.execute.resolves(mockObservation); // The implementation should attempt the rotation and handle errors gracefully await rotate.execute("portrait"); // Verify that unlock and restore lock commands were called even if rotation failed const unlockCalls = mockAdb.executeCommand.getCalls().filter(call => call.args[0] === "shell settings put system accelerometer_rotation 1" ); const restoreLockCalls = mockAdb.executeCommand.getCalls().filter(call => call.args[0] === "shell settings put system accelerometer_rotation 0" ); assert.isAtLeast(unlockCalls.length, 1); assert.isAtLeast(restoreLockCalls.length, 1); }); }); describe("constructor", () => { it("should work with non-null deviceId", () => { const rotateInstance = new Rotate("test-device"); assert.isDefined(rotateInstance); }); it("should work with custom AdbUtils", () => { const customAdb = new AdbUtils("custom-device"); const rotateInstance = new Rotate("test-device", customAdb); assert.isDefined(rotateInstance); }); }); describe("edge cases", () => { it("should handle whitespace in ADB output", async () => { mockAdb.executeCommand.withArgs("shell settings get system user_rotation").resolves(createMockExecResult(" 1 \n")); const orientation = await rotate.getCurrentOrientation(); assert.equal(orientation, "landscape"); }); it("should handle non-numeric ADB output", async () => { mockAdb.executeCommand.withArgs("shell settings get system user_rotation").resolves(createMockExecResult("not-a-number")); const orientation = await rotate.getCurrentOrientation(); assert.equal(orientation, "portrait"); // Should default to portrait }); it("should handle empty ADB output", async () => { mockAdb.executeCommand.withArgs("shell settings get system user_rotation").resolves(createMockExecResult("")); const orientation = await rotate.getCurrentOrientation(); assert.equal(orientation, "portrait"); // Should default to portrait }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zillow/auto-mobile'

If you have feedback or need assistance with the MCP directory API, please join our Discord server