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
|
||||||
|
function sendKeys(keys) {
|
||||||
|
const script = `$wshell = New-Object -ComObject wscript.shell; $wshell.SendKeys('${keys}')`;
|
||||||
|
execSync(`powershell -Command "${script}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: Native PowerShell Screen Capture
|
||||||
|
function takeScreenshot(filename) {
|
||||||
|
const psScript = `
|
||||||
|
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 {
|
try {
|
||||||
let { stdout, stderr } = await exec(command);
|
execSync(`powershell -Command "${psScript}"`);
|
||||||
return stdout;
|
console.log(`Saved screenshot: ${filename}`);
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error(`Error executing command: ${command}`);
|
console.error(`Failed to save screenshot ${filename}:`, err.message);
|
||||||
console.error(`Error details: ${error}`);
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function performKeys(driver, keysArray) {
|
|
||||||
// WinAppDriver doesn't support W3C Actions for keys.
|
|
||||||
// We are forced to use the deprecated method.
|
|
||||||
await driver.sendKeys(keysArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 screenshot(driver, 'oob1.png');
|
|
||||||
await performKeys(driver, [Key.Enter]);
|
|
||||||
await sleep(3000); // Pause execution for 3 seconds
|
|
||||||
await screenshot(driver, 'oob2.png');
|
|
||||||
await performKeys(driver, [Key.Enter]);
|
|
||||||
await sleep(3000); // Pause execution for 3 seconds
|
|
||||||
await screenshot(driver, 'oob3.png');
|
|
||||||
await performKeys(driver, [Key.Escape]);
|
|
||||||
await screenshot(driver, 'oob6.png');
|
|
||||||
} else {
|
|
||||||
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);
|
||||||
|
takeScreenshot('oob1.png');
|
||||||
|
sendKeys('{ENTER}');
|
||||||
|
|
||||||
// Login
|
await sleep(3000);
|
||||||
const windows = await driver.getWindowHandles();
|
takeScreenshot('oob2.png');
|
||||||
const login_window = windows[0]
|
sendKeys('{ENTER}');
|
||||||
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
|
await sleep(3000);
|
||||||
await performKeys(driver, id_arr);
|
takeScreenshot('oob3.png');
|
||||||
await screenshot(driver, 'login02.png');
|
sendKeys('{ESC}');
|
||||||
|
takeScreenshot('oob6.png');
|
||||||
|
|
||||||
await performKeys(driver, [Key.Tab]);
|
// Re-execute SSD to open login dialog
|
||||||
|
exec('"C:\\Program Files\\Certum\\SimplySign Desktop\\SimplySignDesktop.exe"');
|
||||||
console.log('Our secret is ' + process.env.TOTP_SECRET.length + ' characters.');
|
} else {
|
||||||
// We wait until the last possible second to generate
|
console.log('NOT running on ARM64');
|
||||||
// our TOTP to ensure it's still valid.
|
|
||||||
const { otp } = await TOTP.generate(process.env.TOTP_SECRET, {algorithm: 'SHA-256'});
|
|
||||||
console.log('Our token is ' + otp.length + ' characters.');
|
|
||||||
const otp_arr = [...otp];
|
|
||||||
|
|
||||||
// Using the new helper for the OTP string array
|
|
||||||
await performKeys(driver, otp_arr);
|
|
||||||
await screenshot(driver, 'login03.png');
|
|
||||||
|
|
||||||
await performKeys(driver, [Key.Enter]);
|
|
||||||
|
|
||||||
// TODO: it's expected that on successful login the window
|
|
||||||
// will close and these screenshots will error out. Figure
|
|
||||||
// out how to handle that gracefully.
|
|
||||||
await screenshot(driver, 'login04.png');
|
|
||||||
await sleep(500);
|
|
||||||
await screenshot(driver, 'login05.png');
|
|
||||||
await sleep(500);
|
|
||||||
await screenshot(driver, 'login06.png');
|
|
||||||
await sleep(500);
|
|
||||||
await screenshot(driver, 'login07.png');
|
|
||||||
await sleep(500);
|
|
||||||
await screenshot(driver, 'login08.png');
|
|
||||||
await sleep(500);
|
|
||||||
await screenshot(driver, 'login09.png');
|
|
||||||
await sleep(500);
|
|
||||||
await screenshot(driver, 'login10.png');
|
|
||||||
await sleep(500);
|
|
||||||
await screenshot(driver, 'login11.png');
|
|
||||||
await sleep(500);
|
|
||||||
await screenshot(driver, '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
|
await sleep(3000);
|
||||||
// finally {
|
|
||||||
// if (driver) {
|
// 2. Login Flow
|
||||||
// await driver.deleteSession(); // Close the Appium session
|
takeScreenshot('login01.png');
|
||||||
// }
|
console.log('Typing credentials...');
|
||||||
//}
|
|
||||||
|
// Type Email
|
||||||
|
sendKeys('jay0lee@gmail.com');
|
||||||
|
await sleep(500);
|
||||||
|
takeScreenshot('login02.png');
|
||||||
|
|
||||||
|
// Tab to next field
|
||||||
|
sendKeys('{TAB}');
|
||||||
|
await sleep(500);
|
||||||
|
|
||||||
|
// Generate and type TOTP
|
||||||
|
console.log(`Our secret is ${process.env.TOTP_SECRET.length} characters.`);
|
||||||
|
const { otp } = await TOTP.generate(process.env.TOTP_SECRET, {algorithm: 'SHA-256'});
|
||||||
|
console.log(`Our token is ${otp.length} characters.`);
|
||||||
|
|
||||||
|
sendKeys(otp);
|
||||||
|
await sleep(500);
|
||||||
|
takeScreenshot('login03.png');
|
||||||
|
|
||||||
|
// Submit
|
||||||
|
sendKeys('{ENTER}');
|
||||||
|
console.log('Login sequence complete.');
|
||||||
|
|
||||||
|
// Original screenshot cascade to monitor the window closing
|
||||||
|
takeScreenshot('login04.png');
|
||||||
|
await sleep(500);
|
||||||
|
takeScreenshot('login05.png');
|
||||||
|
await sleep(500);
|
||||||
|
takeScreenshot('login06.png');
|
||||||
|
await sleep(500);
|
||||||
|
takeScreenshot('login07.png');
|
||||||
|
await sleep(500);
|
||||||
|
takeScreenshot('login08.png');
|
||||||
|
await sleep(500);
|
||||||
|
takeScreenshot('login09.png');
|
||||||
|
await sleep(500);
|
||||||
|
takeScreenshot('login10.png');
|
||||||
|
await sleep(500);
|
||||||
|
takeScreenshot('login11.png');
|
||||||
|
await sleep(500);
|
||||||
|
takeScreenshot('login12.png');
|
||||||
}
|
}
|
||||||
|
|
||||||
runSSD();
|
runSSD();
|
||||||
|
|||||||
Reference in New Issue
Block a user