mirror of
https://github.com/GAM-team/GAM.git
synced 2026-07-04 04:41:35 +00:00
Refactor SSD app automation with PowerShell integration
Refactor Simply Sign Desktop app automation script to use native PowerShell for keystrokes and screenshots. Remove deprecated methods and improve error handling.
This commit is contained in:
@@ -1,141 +1,115 @@
|
|||||||
// Node.js script that implements an Appium client which will launch
|
// Node.js script to launch Simply Sign Desktop app and log a user in
|
||||||
// Simply Sign Desktop app and log a user in. Once logged in it should
|
// using native Windows keystrokes and native PowerShell screenshots.
|
||||||
// be possible to use tools like signtool.exe to sign Windows EXE/MSI files
|
|
||||||
// with the Certum certificate.
|
|
||||||
|
|
||||||
import { Key, remote } from 'webdriverio';
|
import { exec, execSync } from 'child_process';
|
||||||
import { exec } from 'child_process';
|
|
||||||
import { TOTP } from 'totp-generator';
|
import { TOTP } from 'totp-generator';
|
||||||
|
|
||||||
async function screenshot(driver, filename) {
|
|
||||||
// uncomment to save .png screenshots
|
|
||||||
await driver.saveScreenshot(filename);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function sleep(ms) {
|
function sleep(ms) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function executeCommand(command) {
|
// Native PowerShell Keystroke Sender
|
||||||
try {
|
function sendKeys(keys) {
|
||||||
let { stdout, stderr } = await exec(command);
|
const script = `$wshell = New-Object -ComObject wscript.shell; $wshell.SendKeys('${keys}')`;
|
||||||
return stdout;
|
execSync(`powershell -Command "${script}"`);
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error executing command: ${command}`);
|
|
||||||
console.error(`Error details: ${error}`);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function performKeys(driver, keysArray) {
|
// NEW: Native PowerShell Screen Capture
|
||||||
// WinAppDriver doesn't support W3C Actions for keys.
|
function takeScreenshot(filename) {
|
||||||
// We are forced to use the deprecated method.
|
const psScript = `
|
||||||
await driver.sendKeys(keysArray);
|
Add-Type -AssemblyName System.Windows.Forms;
|
||||||
|
Add-Type -AssemblyName System.Drawing;
|
||||||
|
$Screen = [System.Windows.Forms.SystemInformation]::VirtualScreen;
|
||||||
|
$bitmap = New-Object System.Drawing.Bitmap $Screen.Width, $Screen.Height;
|
||||||
|
$graphic = [System.Drawing.Graphics]::FromImage($bitmap);
|
||||||
|
$graphic.CopyFromScreen($Screen.Left, $Screen.Top, 0, 0, $bitmap.Size);
|
||||||
|
$bitmap.Save('${filename}');
|
||||||
|
`;
|
||||||
|
try {
|
||||||
|
execSync(`powershell -Command "${psScript}"`);
|
||||||
|
console.log(`Saved screenshot: ${filename}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to save screenshot ${filename}:`, err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runSSD() {
|
async function runSSD() {
|
||||||
const opts = {
|
console.log('Launching SimplySign Desktop...');
|
||||||
port: 4723,
|
|
||||||
logLevel: "silent",
|
|
||||||
capabilities: {
|
|
||||||
platformName: "Windows",
|
|
||||||
"appium:app": "C:\\Program Files\\Certum\\SimplySign Desktop\\SimplySignDesktop.exe",
|
|
||||||
"appium:automationName": "Windows",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let driver;
|
// Launch the application.
|
||||||
try {
|
exec('"C:\\Program Files\\Certum\\SimplySign Desktop\\SimplySignDesktop.exe"', (error) => {
|
||||||
driver = await remote(opts);
|
if (error) console.error(`exec error: ${error}`);
|
||||||
|
});
|
||||||
|
|
||||||
// Github Actions Win ARM64 is stuck on a OOB screen that steals focus
|
// 1. Handle ARM64 Out-Of-Box experience
|
||||||
// These enter / escapes should dismiss it.
|
|
||||||
const runner_arch = process.env.RUNNER_ARCH;
|
const runner_arch = process.env.RUNNER_ARCH;
|
||||||
if (runner_arch === "ARM64") {
|
if (runner_arch === "ARM64") {
|
||||||
console.log('Running on ARM64...');
|
console.log('Running on ARM64...');
|
||||||
await sleep(3000); // Pause execution for 3 seconds
|
await sleep(3000);
|
||||||
await screenshot(driver, 'oob1.png');
|
takeScreenshot('oob1.png');
|
||||||
await performKeys(driver, [Key.Enter]);
|
sendKeys('{ENTER}');
|
||||||
await sleep(3000); // Pause execution for 3 seconds
|
|
||||||
await screenshot(driver, 'oob2.png');
|
await sleep(3000);
|
||||||
await performKeys(driver, [Key.Enter]);
|
takeScreenshot('oob2.png');
|
||||||
await sleep(3000); // Pause execution for 3 seconds
|
sendKeys('{ENTER}');
|
||||||
await screenshot(driver, 'oob3.png');
|
|
||||||
await performKeys(driver, [Key.Escape]);
|
await sleep(3000);
|
||||||
await screenshot(driver, 'oob6.png');
|
takeScreenshot('oob3.png');
|
||||||
|
sendKeys('{ESC}');
|
||||||
|
takeScreenshot('oob6.png');
|
||||||
|
|
||||||
|
// Re-execute SSD to open login dialog
|
||||||
|
exec('"C:\\Program Files\\Certum\\SimplySign Desktop\\SimplySignDesktop.exe"');
|
||||||
} else {
|
} else {
|
||||||
console.log('NOT running on ARM64');
|
console.log('NOT running on ARM64');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute SSD again to open login dialog
|
|
||||||
exec('"C:\\Program Files\\Certum\\SimplySign Desktop\\SimplySignDesktop.exe"', (error, stdout, stderr) => {
|
|
||||||
if (error) {
|
|
||||||
console.error(`exec error: ${error}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await sleep(3000);
|
await sleep(3000);
|
||||||
|
|
||||||
// Login
|
// 2. Login Flow
|
||||||
const windows = await driver.getWindowHandles();
|
takeScreenshot('login01.png');
|
||||||
const login_window = windows[0]
|
console.log('Typing credentials...');
|
||||||
await driver.switchWindow(login_window);
|
|
||||||
await screenshot(driver, 'login01.png');
|
|
||||||
const id_value = 'jay0lee@gmail.com';
|
|
||||||
const id_arr = [...id_value];
|
|
||||||
|
|
||||||
// Using the new helper for string arrays
|
// Type Email
|
||||||
await performKeys(driver, id_arr);
|
sendKeys('jay0lee@gmail.com');
|
||||||
await screenshot(driver, 'login02.png');
|
await sleep(500);
|
||||||
|
takeScreenshot('login02.png');
|
||||||
|
|
||||||
await performKeys(driver, [Key.Tab]);
|
// Tab to next field
|
||||||
|
sendKeys('{TAB}');
|
||||||
|
await sleep(500);
|
||||||
|
|
||||||
console.log('Our secret is ' + process.env.TOTP_SECRET.length + ' characters.');
|
// Generate and type TOTP
|
||||||
// We wait until the last possible second to generate
|
console.log(`Our secret is ${process.env.TOTP_SECRET.length} characters.`);
|
||||||
// our TOTP to ensure it's still valid.
|
|
||||||
const { otp } = await TOTP.generate(process.env.TOTP_SECRET, {algorithm: 'SHA-256'});
|
const { otp } = await TOTP.generate(process.env.TOTP_SECRET, {algorithm: 'SHA-256'});
|
||||||
console.log('Our token is ' + otp.length + ' characters.');
|
console.log(`Our token is ${otp.length} characters.`);
|
||||||
const otp_arr = [...otp];
|
|
||||||
|
|
||||||
// Using the new helper for the OTP string array
|
sendKeys(otp);
|
||||||
await performKeys(driver, otp_arr);
|
await sleep(500);
|
||||||
await screenshot(driver, 'login03.png');
|
takeScreenshot('login03.png');
|
||||||
|
|
||||||
await performKeys(driver, [Key.Enter]);
|
// Submit
|
||||||
|
sendKeys('{ENTER}');
|
||||||
|
console.log('Login sequence complete.');
|
||||||
|
|
||||||
// TODO: it's expected that on successful login the window
|
// Original screenshot cascade to monitor the window closing
|
||||||
// will close and these screenshots will error out. Figure
|
takeScreenshot('login04.png');
|
||||||
// out how to handle that gracefully.
|
|
||||||
await screenshot(driver, 'login04.png');
|
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
await screenshot(driver, 'login05.png');
|
takeScreenshot('login05.png');
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
await screenshot(driver, 'login06.png');
|
takeScreenshot('login06.png');
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
await screenshot(driver, 'login07.png');
|
takeScreenshot('login07.png');
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
await screenshot(driver, 'login08.png');
|
takeScreenshot('login08.png');
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
await screenshot(driver, 'login09.png');
|
takeScreenshot('login09.png');
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
await screenshot(driver, 'login10.png');
|
takeScreenshot('login10.png');
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
await screenshot(driver, 'login11.png');
|
takeScreenshot('login11.png');
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
await screenshot(driver, 'login12.png');
|
takeScreenshot('login12.png');
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
//console.error("Error during Appium run:");
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTENTIONAL Keep driver open so tray icon for Certum doesn't close
|
|
||||||
// finally {
|
|
||||||
// if (driver) {
|
|
||||||
// await driver.deleteSession(); // Close the Appium session
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runSSD();
|
runSSD();
|
||||||
|
|||||||
Reference in New Issue
Block a user