From 4e9c61c31be60e1fc9e35df7bbc2400f0a77ec2c Mon Sep 17 00:00:00 2001 From: Pier-Paolo Mammi Date: Fri, 15 May 2026 15:29:17 +0200 Subject: [PATCH] add selenium automation tools --- tools/selenium/.gitignore | 3 + tools/selenium/export-module.js | 108 ++++++++++++++++++ tools/selenium/package-lock.json | 188 +++++++++++++++++++++++++++++++ tools/selenium/package.json | 12 ++ 4 files changed, 311 insertions(+) create mode 100644 tools/selenium/.gitignore create mode 100644 tools/selenium/export-module.js create mode 100644 tools/selenium/package-lock.json create mode 100644 tools/selenium/package.json diff --git a/tools/selenium/.gitignore b/tools/selenium/.gitignore new file mode 100644 index 0000000..63ed5e1 --- /dev/null +++ b/tools/selenium/.gitignore @@ -0,0 +1,3 @@ +# Selenium +node_modules/ +downloads/ diff --git a/tools/selenium/export-module.js b/tools/selenium/export-module.js new file mode 100644 index 0000000..059d73c --- /dev/null +++ b/tools/selenium/export-module.js @@ -0,0 +1,108 @@ +const { Builder, Browser, By, until } = require('selenium-webdriver'); +const { Options } = require('selenium-webdriver/chrome'); +const path = require('path'); + +(async function exportElixFormsModule() { + // Override with environment variables, if they exist + const username = process.env.ELIXFORMS_USERNAME || null; + const password = process.env.ELIXFORMS_PASSWORD || null; + const moduleName = process.env.ELIXFORMS_MODULE_TAG || null; + // Validate that all required environment variables are set + if (!username || !password || !moduleName) { + console.error('Please set ELIXFORMS_USERNAME, ELIXFORMS_PASSWORD, and ELIXFORMS_MODULE_TAG using one of:\n- an .env file in the project root\n- specify an .env file with `npx env-cmd --file <.env_path> -- `\n- pass them as environment variables'); + process.exit(1); + } + + const downloadDir = path.resolve(__dirname, 'downloads'); + + // Configure Chrome options + let options = new Options(); + options.setUserPreferences({ + 'download.default_directory': downloadDir, // Set download folder + 'download.prompt_for_download': false, // No prompt + 'download.directory_upgrade': true, + 'safebrowsing.enabled': true // Avoid blocking + }); + + let driver = await new Builder() + .forBrowser(Browser.CHROME) + .setChromeOptions(options) + .build(); + + const originalWindow = await driver.getWindowHandle(); + + // Implicit timeouts + await driver.manage().setTimeouts({ implicit: 3000 }); + + await driver.get('https://console-unipr.elixforms.it/backoffice/'); + + await driver + .findElement(By.id('username')) + .sendKeys(username); // 'Automation_User' + + await driver + .findElement(By.id('password')) + .sendKeys(password); // 'n_HH9shL#VPeiiT^%;:t5/,jY' + + await driver + .findElement(By.name('INSERT_BTN00000')) + .click(); + + // Wait for "Benvenuto" (quit if not found, likely login failed) + await driver + .wait(until.elementLocated(By.id('title_0')), 10000) + .catch(() => { + console.error('Login failed or "Benvenuto" not found'); + driver.quit(); + process.exit(1); + }); + + await driver + .navigate() + .to('https://console-unipr.elixforms.it/rwe2/admin_console.jsp'); + + let moduleSearchTextbox = await driver.findElement(By.id('TITLE__TAG__CATTAG')); + await moduleSearchTextbox.clear(); + await moduleSearchTextbox.sendKeys(moduleName); + await moduleSearchTextbox.sendKeys('\n'); + + // Needed to wait for the search results to load before clicking the export button + await driver.sleep(2000); + + await driver + .findElement(By.xpath('//div[contains(@class, "item")][1]')) + .findElement(By.xpath('.//input[starts-with(@name, "INSERT_BTN_USER_")]')) + .click() + + await driver + .findElement(By.linkText('Esporta il modulo')) + .click(); + + // Wait for the new tab to open and switch to it + await driver.wait(async () => (await driver.getAllWindowHandles()).length === 2, 5000); + const windows = await driver.getAllWindowHandles(); + windows.forEach(async handle => { + if (handle !== originalWindow) { + await driver.switchTo().window(handle); + } + }); + + // Wait for the new tab to finish loading content + let moduleProtocol = await driver.findElement(By.id('moduleProtocol')); + if (!await moduleProtocol.isSelected()) { + await moduleProtocol.click(); + } + + await driver + .findElement(By.id('download')) + .click(); + + let downloadLink = await driver.wait(until.elementLocated(By.linkText('Scarica il modulo'))); + let href = await downloadLink.getAttribute('href'); + downloadLink.click(); + + // Wait for the download to complete (this is a simple approach, adjust as needed) + await driver.sleep(5000); + + await driver.quit(); +})(); diff --git a/tools/selenium/package-lock.json b/tools/selenium/package-lock.json new file mode 100644 index 0000000..4ad9107 --- /dev/null +++ b/tools/selenium/package-lock.json @@ -0,0 +1,188 @@ +{ + "name": "selenium-export-module", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "selenium-export-module", + "version": "0.0.1", + "dependencies": { + "dotenv": "^17.4.2", + "selenium-webdriver": "^4.44.0" + } + }, + "node_modules/@bazel/runfiles": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@bazel/runfiles/-/runfiles-6.5.0.tgz", + "integrity": "sha512-RzahvqTkfpY2jsDxo8YItPX+/iZ6hbiikw1YhE0bA9EKBR5Og8Pa6FHn9PO9M0zaXRVsr0GFQLKbB/0rzy9SzA==", + "license": "Apache-2.0" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/selenium-webdriver": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.44.0.tgz", + "integrity": "sha512-7RbYoKK0zET+KMVak11UDCtKvNulOU6gFZp8HI5GN9K8+BhqrliIJU/FP6QADrvRAXFMr3wHxfE3JHOcAxO3GQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/SeleniumHQ" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/selenium" + } + ], + "license": "Apache-2.0", + "dependencies": { + "@bazel/runfiles": "^6.5.0", + "jszip": "^3.10.1", + "tmp": "^0.2.5", + "ws": "^8.20.1" + }, + "engines": { + "node": ">= 20.0.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/ws": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", + "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/tools/selenium/package.json b/tools/selenium/package.json new file mode 100644 index 0000000..bca03ee --- /dev/null +++ b/tools/selenium/package.json @@ -0,0 +1,12 @@ +{ + "name": "selenium-export-module", + "version": "0.0.1", + "description": "Export elixForms module into .elx JSON file using Selenium", + "scripts": { + "export": "node ./export-module.js" + }, + "dependencies": { + "dotenv": "^17.4.2", + "selenium-webdriver": "^4.44.0" + } +}