Skip to content

Commit

Permalink
Added tests and both CJS/ESM and browser compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
LukeSavefrogs committed Feb 13, 2022
1 parent 659d032 commit d62c384
Show file tree
Hide file tree
Showing 11 changed files with 1,622 additions and 136 deletions.
2 changes: 2 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
./tests/
1,373 changes: 1,373 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 13 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@
"name": "seleniumenginejs",
"version": "1.0.2",
"description": "Selenium-like methods useful for automating SPAs (Single Page Application) where content is changed dynamically.",
"main": "src/SeleniumEngine.js",
"type": "module",
"main": "./src/SeleniumEngine.cjs",
"exports": {
"require": "./src/SeleniumEngine.cjs",
"import": "./esm/wrapper.mjs"
},
"scripts": {
"test": "echo \"Warning: No test specified\""
"release": "np",
"test": "npx ava"
},
"repository": {
"type": "git",
Expand All @@ -24,6 +30,10 @@
},
"homepage": "https://github.com/LukeSavefrogs/SeleniumEngineJS#readme",
"dependencies": {
"np": "^6.5.0"
},
"devDependencies": {
"np": "^6.5.0",
"ava": "^4.0.1",
"puppeteer": "^13.3.1"
}
}
162 changes: 162 additions & 0 deletions src/SeleniumEngine.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* -------------------------------------- Selenium-Like Methods --------------------------------------
*
* Contiene delle Utility generalizzate specifiche per la manipolazione della DOM e la sua interazione
*
* Ricostruzione delle API di Selenium (con sintassi simile a Selenium IDE)
*
*
* Proprieta:
* - waitUntil (fn, ms):
* Base di partenza per la costruzione di tutti gli altri waitUntil*. Mette in pausa il codice di una
* funzione async fino a che la condizione specificata dalla funzione `fn` non si avvera
* Accetta un parametro OPZIONALE `ms` che indica il Timeout in ms oltre il quale restituisce errore
*
* - sleep (ms):
* Mette in pausa il codice di una funzione async per il numero di `ms` passati come parametro
*
*
* Utilizzo:
* (async () => {
* console.log("Operation 1");
* await SeleniumEngine.sleep(2000);
* console.log("Operation 2");
* })()
*
*
*
* --------------------------------------------------------------------------------------------------------
*/
const SeleniumEngine = {
/**
*
* @param {Function} testCondition Condition that must be met in order to the `waitUntil` function to return
* @param {Number} timeout_ms Timeout in milliseconds after which the `waitUntil` function will fail
* @param {Number} checkInterval_ms The interval in milliseconds at which the `waitUntil` function will check the condition
* @returns
*/
waitUntil: function (testCondition, timeout_ms = 30000, checkInterval_ms = 1000) {
let start_ts = performance.now();

return new Promise((resolve, reject) => {
let timer = window.setInterval(() => {
let elapsed_time = parseInt(performance.now() - start_ts);

// Se il timeout è un numero
if (
!!timeout_ms &&
!isNaN(timeout_ms) &&
timeout_ms > 0 &&
elapsed_time >= timeout_ms
) {
clearInterval(timer);
reject(
new Error(`Timeout of ${timeout_ms} ms exceeded (${elapsed_time} real)`)
);
}

if (testCondition()) {
clearInterval(timer);
resolve({
msg:
`The specified condition was met before the ${timeout_ms} ms timeout`,
time: elapsed_time,
given_timeout: timeout_ms,
});
}
}, checkInterval_ms);
});
},

/**
*
* @param {String} cssSelector The css selector for the element
* @param {Number} timeout_ms Timeout in ms
*
* @example
* // Will wait until an element with id="test" is present
* await SeleniumEngine.waitForElementPresent("#test", 2000)
*/
waitForElementPresent: function (cssSelector, timeout_ms = 30000) {
if (!cssSelector.trim()) throw new Error("Please specify a css selector");

/*
Può anche essere definito così:
return this.waitUntil(() => !!document.querySelector(cssSelector), timeout_ms);
Ritorno il valore di un'altra Promise per poter customizzare meglio le risposte
*/
return new Promise((resolve, reject) => {
this.waitUntil(() => !!document.querySelector(cssSelector), timeout_ms)
.then((result) =>
resolve({
msg: `Element with selector ${cssSelector} is present`,
time: result.time,
})
)
.catch((result) => reject(new Error(result)));
});
},

/**
*
* @param {String} cssSelector The css selector for the element
* @param {Number} timeout_ms Timeout in ms
*
* @example
* // Will wait until an element with id="test" is not present
* await SeleniumEngine.waitForElementNotPresent("#test", 2000)
*/
waitForElementNotPresent: function (cssSelector, timeout_ms = 30000) {
if (!cssSelector.trim()) throw new Error("Please specify a css selector");

return new Promise((resolve, reject) => {
this.waitUntil(() => !document.querySelector(cssSelector), timeout_ms)
.then((result) =>
resolve({
msg: `Element with selector ${cssSelector} is not present`,
time: result.time,
})
)
.catch((result) => reject(new Error(result)));
});
},

/**
*
* @param {Number} ms Time to wait (expressed in milliseconds)
* @returns Promise
*/
sleep: function (ms = 0) {
return new Promise((resolve, reject) => {
if (!ms || isNaN(ms) || ms < 0) {
reject(new Error("Timeout must be a positive number"));
}
setTimeout(resolve, ms);
});
}
};


// From https://github.com/umdjs/umd/blob/master/templates/returnExports.js
// if the module has no dependencies, the above pattern can be simplified to
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
root.SeleniumEngine = factory();
}
}(typeof self !== 'undefined' ? self : this, function () {

// Just return a value to define the module export.
// This example returns an object, but the module
// can return a function as the exported value.
return SeleniumEngine;
}));
133 changes: 0 additions & 133 deletions src/SeleniumEngine.js

This file was deleted.

1 change: 1 addition & 0 deletions src/esm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"type": "module"}
5 changes: 5 additions & 0 deletions src/esm/wrapper.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Got the idea from:
// https://redfin.engineering/node-modules-at-war-why-commonjs-and-es-modules-cant-get-along-9617135eeca1?gi=7126aca39737#6b50
import SeleniumEngine from "../SeleniumEngine.cjs";

export default SeleniumEngine
12 changes: 12 additions & 0 deletions tests/test-browser.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SeleniumEngine Browser Test</title>

<script src="../src/SeleniumEngine.cjs"></script>
</head>
<body></body>
</html>
26 changes: 26 additions & 0 deletions tests/test-browser.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import test from 'ava';
import puppeteer from 'puppeteer';

import SeleniumEngine from "../src/esm/wrapper.mjs";

// https://stackoverflow.com/a/64383997/8965861
import { fileURLToPath } from 'url';
import { dirname } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);



test("Script tag - Check if object is defined and with correct keys", async t => {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto(`file://${__dirname}/test-browser.html`);

const remoteSeleniumEngine = await page.evaluate(() => {
return JSON.stringify(Object.keys(SeleniumEngine));
});
await browser.close();

t.deepEqual(JSON.parse(remoteSeleniumEngine).sort(), Object.keys(SeleniumEngine).sort());
})
10 changes: 10 additions & 0 deletions tests/test-cjs.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const test = require('ava');

const SeleniumEngine = require("../src/SeleniumEngine.cjs");

test("Waits for 2 seconds between the two operations", async t => {
// console.log(`[${(new Date()).toISOString()}] Operation 1`);
await SeleniumEngine.sleep(2000);
// console.log(`[${(new Date()).toISOString()}] Operation 2`);
t.pass();
})

0 comments on commit d62c384

Please sign in to comment.