UNPKG

gpii-windows

Version:

Components of the GPII personalization infrastructure for use on Microsoft's "Windows" ™

329 lines (294 loc) 10.7 kB
/* * Tests for AppBar * * Copyright 2020 Raising the Floor - International * * Licensed under the New BSD license. You may not use this file except in * compliance with this License. * * The R&D leading to these results received funding from the * Department of Education - Grant H421A150005 (GPII-APCP). However, * these results do not necessarily represent the policy of the * Department of Education, and you should not assume endorsement by the * Federal Government. * * You may obtain a copy of the License at * https://github.com/GPII/universal/blob/master/LICENSE.txt */ "use strict"; var fluid = require("gpii-universal"), kettle = fluid.require("kettle"), ffi = fluid.require("ffi-napi"); kettle.loadTestingSupport(); var jqUnit = fluid.require("node-jqunit"); var gpii = fluid.registerNamespace("gpii"); require("../src/appBar.js"); fluid.registerNamespace("gpii.tests.windows.appBar"); fluid.defaults("gpii.tests.appBar.tests", { gradeNames: ["fluid.test.testEnvironment"], components: { appBar: { type: "gpii.windows.appBar", options: { invokers: { getNativeWindowHandle: "gpii.tests.appBar.getNativeWindowHandle({that})", hookWindowMessage: "fluid.identity", unhookWindowMessage: "fluid.identity", isReady: { funcName: "fluid.identity", args: ["{that}.ready"] } }, listeners: { "onCreate.test_createWindow": { priority: "first", func: "gpii.tests.appBar.createWindow", args: ["{that}"] }, "onDestroy.test_destroyWindow": { func: "gpii.tests.appBar.destroyWindow", args: ["{that}"] } } } }, tester: { type: "gpii.tests.appBar.testCaseHolder" } } }); fluid.defaults("gpii.tests.appBar.testCaseHolder", { gradeNames: ["fluid.test.testCaseHolder"], modules: [{ name: "Task bar position", tests: [{ expect: 1, name: "Test getTaskbarPos", sequence: [{ funcName: "gpii.tests.appBar.testTaskBarPos" }] }] }, { name: "Messages", tests: [{ expect: 1, name: "checking messages are being listened to", sequence: [{ func: "jqUnit.assertDeepEq", args: [ "The expected messages should be listened for", "{that}.options.testData.expectedMessages", "{appBar}.hookedMessages" ] }] }] }, { name: "Enable app bar", tests: [{ expect: 1, name: "checking the window is in the correct position", sequence: [{ func: "{appBar}.enable" }, { funcName: "gpii.windows.user32.SetWindowPos", args: ["{appBar}.hwnd", 0, 0, 0, 200, 200, 1] }, { task: "gpii.windows.waitForCondition", args: ["{appBar}.isReady"], resolve: "fluid.identity" }, { funcName: "gpii.tests.appBar.assertAppBarPos", args: ["{appBar}.hwnd"] }] }] }, { name: "Disable app bar", tests: [{ expect: 1, name: "checking the window is in the correct position", sequence: [{ func: "{appBar}.disable" }, { funcName: "jqUnit.assertDeepEq", args: [ "Work area should have been restored", "{that}.options.testData.originalWorkArea", "@expand:gpii.tests.appBar.getWorkArea()" ] }] }] }], testData: { expectedMessages: [ "@expand:gpii.windows.messages.registeredMessage(WM_GPII_APPBAR)", gpii.windows.API_constants.WM_WINDOWPOSCHANGING, gpii.windows.API_constants.WM_WINDOWPOSCHANGED, gpii.windows.API_constants.WM_ACTIVATE ], originalWorkArea: "@expand:gpii.tests.appBar.getWorkArea()" } }); /** * Tests that getTaskbarPos works. */ gpii.tests.appBar.testTaskBarPos = function () { jqUnit.expect(3); var desktop = gpii.windows.display.getDesktopSize(); var tests = { left: { left: 0, top: 0, width: 100, height: desktop.height }, top: { left: 0, top: 0, width: desktop.width, height: 100 }, right: { left: desktop.width - 100, top: 0, width: 100, height: desktop.height }, bottom: { left: 0, top: desktop.height - 100, width: desktop.width, height: 100 } }; fluid.each(tests, function (rect, expect) { var result = gpii.windows.appBar.getTaskbarPos(rect); jqUnit.assert("getTaskbarPos should return the expected result", expect, result); }); }; /** * Checks that the app bar window is in the correct position - in a gap between the bottom of the work area and the top * of the task bar. * * @param {Number} hwnd The app bar window handle. */ gpii.tests.appBar.assertAppBarPos = function (hwnd) { jqUnit.expect(1); // Check that the window is above the task bar var rect = gpii.windows.getWindowRect(hwnd); var screenBottom = gpii.windows.appBar.getWorkAreaBottom(); jqUnit.assertTrue("Window should be above taskbar", rect.bottom <= screenBottom); // Check that the window is below the work area var workArea = gpii.tests.appBar.getWorkArea(); jqUnit.assertTrue("Window should be beneath the work area", rect.top > workArea.bottom); }; /** * Gets the work area. * @return {Rect} The work area rectangle. */ gpii.tests.appBar.getWorkArea = function () { var workArea = new gpii.windows.RECT(); var success = gpii.windows.spi.systemParametersInfoWPtr.SystemParametersInfoW( gpii.windows.spi.actions.SPI_GETWORKAREA, 0, workArea.ref(), 0); if (!success) { jqUnit.fail(gpii.windows.win32errorText("SystemParametersInfoW(SPI_GETWORKAREA)", success)); } return { left: workArea.left, top: workArea.top, right: workArea.right, bottom: workArea.bottom }; }; /** * Implements electron's getNativeWindowHandle() - returns the window handle in a 32 bit buffer. * @param {Component} that The gpii.windows.appBar instance. * @return {Buffer} The window handle. */ gpii.tests.appBar.getNativeWindowHandle = function (that) { var buf = Buffer.alloc(4); buf.writeUInt32LE(that.hwnd); return buf; }; /** * Destroys the test window. * @param {Component} that The gpii.windows.appBar instance. */ gpii.tests.appBar.destroyWindow = function (that) { if (that.hwnd) { gpii.windows.user32.DestroyWindow(that.messageWindow); that.hwnd = null; } }; /** * Creates a window, for testing the app bar functionality. * (taken from gpii.windows.messages.createMessageWindow) * * @param {Component} that The gpii.windows.appBar instance */ gpii.tests.appBar.createWindow = function (that) { if (that.hwnd) { fluid.fail("Test window already created."); } var className = gpii.windows.stringToWideChar("gpii-appbar-test"); // Create the Window Class for the window var cls = new gpii.windows.WNDCLASSW(); cls.ref().fill(0); cls.lpszClassName = className; // Create a pointer to the window procedure function. var callback = ffi.Callback(gpii.windows.types.HANDLE, [gpii.windows.types.HANDLE, gpii.windows.types.UINT, gpii.windows.types.UINT, gpii.windows.types.PVOID], function (hwnd, msg, wParam, lParam) { if (msg === that.WM_GPII_APPBAR && wParam === gpii.windows.API_constants.ABN_POSCHANGED) { that.ready = true; } // Pass the parameters as buffers, like Electron does. var wp = Buffer.alloc(4); var lp = Buffer.alloc(4); wp.writeUInt32LE(wParam); lp.writeUInt32LE(lParam.address()); gpii.windows.appBar.windowMessage(that, hwnd, msg, wp, lp); return gpii.windows.user32.DefWindowProcW(hwnd, msg, wParam, lParam); }); cls.lpfnWndProc = callback; // Keep a reference to the function pointer, otherwise the GC will free it. Because 'callback' points to the // function that windows calls whenever there is a window message, the buffers need to be kept alive for as long // as GPII is running. // Additionally, this value also contains the "cif" buffer (internal to ffi-napi) which also needs to be // referenced. See GPII-3445. gpii.tests.appBar.windowProcPointer = function () { callback(); }; var result = gpii.windows.user32.RegisterClassW(cls.ref()); if (!result) { fluid.fail("RegisterClass failed"); } // Start the message loop (needs to be done before creating the window, in order to process WM_CREATE) if (!process.versions.electron) { that.hwnd = 1; var loop = function () { // sizeof(MSG) = 48 on 64-bit, 28 on 32-bit. var msg = Buffer.alloc(process.arch === "x64" ? 48 : 28); // Unable to use GetMessage because it blocks, and can't call via .async because it needs to be in the same // thread as the window. while (gpii.windows.user32.PeekMessageW(msg, 0, 0, 0, 1)) { gpii.windows.user32.TranslateMessage(msg); gpii.windows.user32.DispatchMessageW(msg); } if (that.hwnd) { setTimeout(loop, 200); } }; loop(); } // Create the Window. that.hwnd = gpii.windows.user32.CreateWindowExW( gpii.windows.API_constants.WS_EX_TOOLWINDOW, className, className, gpii.windows.API_constants.WS_VISIBLE, 0, 0, 200, 200, 0, 0, 0, 0); if (!that.hwnd) { fluid.fail(gpii.windows.win32errorText("CreateWindowEx failed", that.hwnd)); } }; module.exports = kettle.test.bootstrap("gpii.tests.appBar.tests");