QualState is an automatic SPA(Single Page Application) crawler
const qualstate = require("qualstate");
let qualStateOptions = {
"url": "http://localhost:4000/",
"headless": true,
"maxStates": 2
};
(async () => {
await qualState.crawl(qualStateOptions);
})();
The available options fot the crawl() function are:
{
"url": "http://localhost:3000/", // url to evaluate
"headless": false, // value of headless to use on puppeteer. Default value = false
"waitTime": 2000, // time to wait between actions
"maxStates": 2, // number of maximum states to be found
"numberOfProcess": 3, // Number of concurrent processes. Default value = 1
"log": {
"file": true, // Logs errors to a file. Default value = false
"console": false // Logs errors to the console. Default value = false
},
"cookies": {
"waitBefore": 9000, // time to wait before clicking in the btn
"btn": "body > div:nth-child(27) > div > div > div > div.nq.nr.ns.nt.ge.gi.ah.nu.hx.nv > div > button", // XPaht or ID of btn
"waitAfter": 5000 // time to wait after clicking in the btn
},
"login": [
{
"credentials": {// Credential
"#identifierId": "", // key:value, where key is the ID of input and value the input - in this case could be email
}
},
{
"credentials": {// Credential
"#password > div > div > div > input": "", // key:value, where key is the ID of input and value the input - in this case could be password
}
},
{
"action": {
"id": "#login_button", // Id of button to execute login action in the page
"event": "click"// type of event to be executed
}
},
{
"info": {
"wait": 6000 // time to wait for the login to be executed
}
}
],
"viewport": {
"mobile": false, // default value = false
"landscape": true, // default value = viewPort.width > viewPort.height
"resolution": {
"width": 1920, // default value for desktop = 1366, default value for mobile = 1080
"height": 1080 // default value for desktop = 768, default value for mobile = 1920
}
},
"ignore": {
"ids_compare": ["pErrorMessage"], // array of IDs or any identifying attribute of element that should be ignore when comparing states.
"ids_events": ["btnReq", "hoverTest", "btnId", "aHref", "idTd1", "idTd2"] // array of IDs of element that have events and should be ignore.
},
"interaction": {
"inputs": [ // array of inputs, that will be executed on the page if the inputs exist
{
"value": { // one or more key:value, where key is the ID of input and value the input value
"onChangeInput": "inputOnChange"
},
"info": {
"wait": 2000 // time to wait after executing the inputs
}
}
],
"forms": [ // array of forms, that will be executed on the page if the forms exist
{
"input": [ // array inputs to be place on the form
{ // one or more key:value, where key is the ID of input and value the input value
"fname": "filipe"
}
],
"action": { // action that submits the form
"id": "btnSubmit", // ID of the element
"event": "click" // type of event to be executed
},
"info": {
"formId": "idForm" // ID of the form
},
}
],
"directions": [ // array of directions, that will be executed at the beginning of the crawl process
{
"actions": [ // array of actions to be executed for each directions
{
"values": { // one or more key:value, where key is the ID of input and value the input value
"fname": "Name"
},
"action": { // action that should be trigger
"id": "btnSubmit", // ID of the element
"eventType": "click" // type of event to be executed
}
},
{
"values": {
"newFName": "Name"
},
"action": {
"id": "btnSubmitNewForm",
"eventType": "click"
}
}
],
"info": {
"crawl": "stop", // flag to know if the crawl process shoulf continue after the directions process. Value = "stop" || "continue"
"save": true // save the states during the directions process
}
}
]
}
}
At the end of the process there is a report to the user. The report its divide in two different set of information. In case console was true, there's only one report.
Console:
info: 20:46:13
{
"msg": "Ação realizada",
"action": [
{
"user": "auto",
"id": "input#btnSubmit",
"className": "HTMLInputElement",
"eventType": "click",
"selector": "html > body:nth-child(2) > div:nth-child(1) > form:nth-child(1) > input:nth-child(10)"
}
],
"bodyHash": "1704e38477f0c68cc992b23b6390e77576a2e66121c80496deab4594752a1446"
}
In case the file was true there's two reports File logs.log:
info: 20:46:13
{
"msg": "Ação realizada",
"action": [
{
"user": "auto",
"id": "input#btnSubmit",
"className": "HTMLInputElement",
"eventType": "click",
"selector": "html > body:nth-child(2) > div:nth-child(1) > form:nth-child(1) > input:nth-child(10)"
}
],
"bodyHash": "1704e38477f0c68cc992b23b6390e77576a2e66121c80496deab4594752a1446"
}
File states.log
info: 20:46:13
{
"bodyHash": "1704e38477f0c68cc992b23b6390e77576a2e66121c80496deab4594752a1446",
"body": "<!DOCTYPE html><html>......n</body></html>"
}