Skip to content
This repository has been archived by the owner on Aug 25, 2022. It is now read-only.

Commit

Permalink
Merge 26ec863 into 1fa3cb3
Browse files Browse the repository at this point in the history
  • Loading branch information
xudafeng committed Nov 25, 2018
2 parents 1fa3cb3 + 26ec863 commit 6e5bf63
Show file tree
Hide file tree
Showing 34 changed files with 1,772 additions and 186 deletions.
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
**/node_modules
**/dist
**/public
**/demo
**/coverage
**/lib/tesseract
**/temp
7 changes: 5 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
parserOptions: {
ecmaVersion: 2017
},
"env": {
"browser": true,
"node": true,
Expand Down Expand Up @@ -80,7 +83,7 @@
"no-loop-func": 0,
"no-mixed-requires": 0,
"no-mixed-spaces-and-tabs": [2, false],
"no-multi-spaces": 2,
"no-multi-spaces": 0,
"no-multi-str": 2,
"no-multiple-empty-lines": [2, { "max": 1 }],
"no-native-reassign": 2,
Expand Down Expand Up @@ -130,7 +133,7 @@
"no-with": 2,
"no-extra-parens": 2,
"object-curly-spacing": 0,
"one-var": [2, { "initialized": "never" }],
"one-var": [0, { "initialized": "never" }],
"operator-assignment": 0,
"operator-linebreak": [2, "after"],
"padded-blocks": 0,
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ nosmoke
| :---: | :---: | :---: |


This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto upated at `Thu Jun 14 2018 12:15:53 GMT+0800`.
This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto upated at `Sun Nov 18 2018 09:56:54 GMT+0800`.

<!-- GITCONTRIBUTOR_END -->

Expand Down
File renamed without changes.
15 changes: 15 additions & 0 deletions demo/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

function Hooks(config, sessionId) {}

/**
* Method to intercept the crawling process after an specific action has been performed
* @Params: action the action which belongs to current active node, and has just been performed
* @Params: crawler the crawler instance which contains the context information as well as crawler config
* @Returns: a Promise to indicate the action has been handled and otherwise the default logic will bypass it
* */
Hooks.prototype.onActionPerformed = async function(action, crawler) {
return null;
};

exports.Hooks = Hooks;
File renamed without changes.
99 changes: 98 additions & 1 deletion lib/common/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const utils = require('macaca-utils');
const childProcess = require('child_process');

const root = global;
const _ = utils.merge({}, utils);

_.exec = function(cmd, opts) {
Expand Down Expand Up @@ -72,4 +72,101 @@ _.retry = function(func, interval, num) {
});
};

_ .shuffleArray = function (array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]]; // eslint-disable-line no-param-reassign
}
};

_.randomSubarray = function(arr, size) {
var shuffled = arr.slice(0), i = arr.length, min = i - size, temp, index;
while (i-- > min) {
index = Math.floor((i + 1) * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled.slice(min);
};

_.addHookAPIs = function(hook, crawler) {

hook.click = async function(data) {
if (data.method === 'xpath') {

// trigger tap based on location
let element = await root.wdclient.send('/wd/hub/session/' + crawler.sessionId + '/element', 'post', {
using: 'xpath',
value: `${data.xpath}`
}, null);

await root.wdclient.send('/wd/hub/session/' + crawler.sessionId + '/element/' + element.value.ELEMENT + '/click', 'post', {}, null);

} else if (data.method === 'location') {

// trigger tap based on location
await root.wdclient.send('/wd/hub/session/' + crawler.sessionId + '/actions', 'post', {'actions': [
{
'type': 'tap',
'x': data.location.x,
'y': data.location.y
}
]}, null);

} else {

// log for invalid args
console.log(`invalid input params: ${JSON.stringify(data)}`);

}
};

hook.type = async function(data) {
if (data.method === 'xpath') {

// log for invalid args
let element = await root.wdclient.send('/wd/hub/session/' + crawler.sessionId + '/element', 'post', {
using: 'xpath',
value: `${data.xpath}`
}, null);

// send keys to specific element
await root.wdclient.send('/wd/hub/session/' + crawler.sessionId + '/element/' + element.value.ELEMENT + '/value', 'post', {'value': [data.value]}, null);

} else {

// trigger input without knowing for element
await root.wdclient.send('/wd/hub/session/' + crawler.sessionId + '/keys', 'post', {'value': data.value}, null);

}
};

hook.drag = async function(data) {
// drag based on coordinates
await root.wdclient.send('/wd/hub/session/' + crawler.sessionId + '/actions', 'post', {'actions': [
{
'type': 'drag',
'fromX': data.location.fromX,
'fromY': data.location.fromY,
'toX': data.location.toX,
'toY': data.location.toY,
'duration': data.duration
}
]}, null);
};

hook.press = async function(data) {
// press based on coordinates
await root.wdclient.send('/wd/hub/session/' + crawler.sessionId + '/actions', 'post', {'actions': [
{
'type': 'press',
'x': data.location.x,
'y': data.location.y,
'duration': data.duration
}
]}, null);
};
};

module.exports = _;
19 changes: 9 additions & 10 deletions lib/crawler/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ function NSCrawlerConfig() {
this.testingPeriod = 0.5 * 60 * 60;
this.testingDepth = 8;
this.takeScreenShot = true;
this.newCommandTimeout = 6;
this.newCommandTimeout = 3;
this.launchTimeout = 6;
this.packages = '.*';
this.maxActionPerPage = 15;
this.maxActionPerPage = 10;
this.asserts = [];
this.targetElements = {};
this.exclusivePattern = '';
Expand All @@ -25,15 +25,14 @@ function NSCrawlerConfig() {
this.tabBarTypes = [];
this.exclusiveTypes = [];
this.blacklist = [];
}

NSCrawlerConfig.prototype.debugDesriptoin = function() {
return 'newCommandTimeout: ' + this.newCommandTimeout + '\n' +
'testingDepth: ' + this.testingDepth + '\n' +
'takeScreenShot: ' + this.takeScreenShot + '\n' +
'launchTimeout: ' + this.launchTimeout + '\n' +
'targetElements: ' + this.targetElements + '\n';
};
// - phase 2 - OCR
this.strategy = 'source';
this.depth = 10;
this.duration = 3600;
this.triggers = [];
this.exclude = [];
}

NSCrawlerConfig.prototype.loadDefault = function() {
let configFile = path.join(__dirname, '..', '..', 'public', 'crawler.config.yml');
Expand Down
17 changes: 11 additions & 6 deletions lib/crawler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ let crawlerConfig = new NSCrawlerConfig();
crawlerConfig.loadDefault();

let NSCrawler;
if (crawlerConfig.platform.toLowerCase() === 'ios') {
NSCrawler = require('./ios').NSCrawler;
} else if (crawlerConfig.platform.toLowerCase() === 'pc-web') {
NSCrawler = require('./web').NSCrawler;

if (crawlerConfig.strategy.toLowerCase() === 'ocr') {
NSCrawler = require('./ocr-impl/ocr-crawler').NSCrawler;
} else {
NSCrawler = require('./android').NSCrawler;
if (crawlerConfig.platform.toLowerCase() === 'ios') {
NSCrawler = require('./wda-impl/ios').NSCrawler;
} else if (crawlerConfig.platform.toLowerCase() === 'pc-web') {
NSCrawler = require('./wda-impl/web').NSCrawler;
} else {
NSCrawler = require('./wda-impl/android').NSCrawler;
}
}

if (!root.eventEmmiter) {
Expand All @@ -27,7 +32,7 @@ root.wdclient = new WDClient({
config: crawlerConfig
});

root.eventEmmiter.addListener('onSessionCreated', (data)=>{
root.eventEmmiter.addListener('onSessionCreated', (data) => {
console.log('initialize crawler');
let crawler = new NSCrawler(crawlerConfig, data.sessionId).initialize();
setTimeout(crawler.crawl.bind(crawler), crawlerConfig.launchTimeout * 1000);
Expand Down
159 changes: 159 additions & 0 deletions lib/crawler/ocr-impl/models.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
'use strict';

const ReporterTemplate = require('../reporterTemplate');
const _ = require('../../common/helper');
let root = require('window-or-global');

/* Test suite control */
root.updatePassedTestsCount = function () {
if (root.mockData.stats.tests > root.mockData.stats.passes) {
root.mockData.stats.passes++;
}

if (root.mockData.stats.pending > 0) {
root.mockData.stats.pending--;
}
};

/** Crawling Node: each of the tree node represents a unique user page */
function NSAppCrawlingTreeNode() {
this.path = ''; // - not in use -
this.parent = null; // parent node
this.type = 'normal'; // - not in use - for source type strategy only
this.depth = 0; // depth in parent
this.actions = []; // user actions
this.digest = null; // digest of current node
this.reportSuite = null; // testing report suite
this.repeatable = false; // whether the action in current node can repeat, true for user defined action
this.text = ''; // used for ocr mode
}

function NSAppCrawlingTreeNodeActionLocation() {
this.origin = { x: 0.0, y: 0.0};
this.size = { w: 0.0, h: 0.0};
}

function NSAppCrawlingTreeNodeAction() {
this.isTriggered = false;
this.location = new NSAppCrawlingTreeNodeActionLocation(); // source strategy: xpath; ocr strategy: location in view (NSAppCrawlingTreeNodeActionLocation);
this.input = null; // - not in use - for source type strategy only
this.interval = 0; // test duration interval
this.source = {}; // - not in use - for source type strategy only
this.name = ''; // name
}

NSAppCrawlingTreeNodeAction.prototype.title = function() {
return `${this.name}-${JSON.stringify(this.location)}`;
};

NSAppCrawlingTreeNodeAction.prototype.markActionStart = function (crawler) {
this.previousTime = new Date();
};

NSAppCrawlingTreeNodeAction.prototype.markActionFinish = function (crawler, fileName) {
this.currentTime = new Date();
this.interval = this.currentTime.getTime() - this.previousTime.getTime();

// update report data
if (this.bindedTest &&
this.bindedTest.state !== 'passed') {
this.bindedTest.context = `${fileName}` || null;
this.bindedTest.pending = false;
this.bindedTest.pass = true;
this.bindedTest.state = 'passed';
this.bindedTest.duration = crawler.currentAction.interval;
this.bindedTest.code = JSON.stringify(crawler.currentAction, null, 2);
}
};

NSAppCrawlingTreeNode.prototype.isFinishedBrowsing = function() {
// check for ordinary nodes, whether current action has been finished
let isFinished = true;
for (let key in this.actions) {
if (this.actions[key].isTriggered === false) {
isFinished = false;
break;
}
}

// special case for repeatable nodes
if (isFinished && this.repeatable === true) {
for (let key in this.actions) {
this.actions[key].isTriggered = false;
}

isFinished = false;
}

return isFinished;
};

NSAppCrawlingTreeNode.prototype.produceNodeActions = function(rawElements) {
let actions = [];
for (let index = 0; index < rawElements.length; index++) {
let rawElement = rawElements[index];
let action = new NSAppCrawlingTreeNodeAction();
let shallowCopy = Object.assign({}, rawElement);
action.location = shallowCopy.location;
action.input = shallowCopy.input;
action.name = shallowCopy.name;
actions.push(action);
}

return actions;
};

NSAppCrawlingTreeNode.prototype.checkDigest = function(platform, source, crawler) {
// assign to the full text body of the source, and then conduct similarity tests
return source.text;
};

NSAppCrawlingTreeNode.prototype.abbreviatedDigest = function() {
// usually the first one or two action is the navigation item, TODO: provide a speicify property for the title
return this.actions.reduce((accumulator, currentAction, index) => {
if (index <= 1) {
return `${accumulator}_${currentAction.name}`;
}
});
};

NSAppCrawlingTreeNode.prototype.checkAndInitReportDataIfNeeded = function (crawler) {
// empty case
if (!this.reportSuite) {
this.reportSuite = ReporterTemplate.suite();
this.reportSuite.title = 'View: ' + this.abbreviatedDigest();
this.reportSuite.uuid = `${_.uuid()}`;

let shell = ReporterTemplate.suite();
shell.uuid = _.uuid();
shell.suites.push(this.reportSuite);
shell.title = 'View: ' + this.abbreviatedDigest();
this.reportSuite.context = this.fileName || null;
root.mockData.suites.suites.push(shell);
}

// update total actions
if (!this.reportSuite.tests.length) {
this.reportSuite.totalPasses = 0;
this.reportSuite.totalTests = 0;

for (let i = 0; i < this.actions.length; i++) {
let test = ReporterTemplate.test();
test.title = this.actions[i].title();
test.fullTitle = `View: ${this.abbreviatedDigest()}//${this.actions[i].title()}`;
this.actions[i].bindedTest = test;
this.reportSuite.tests.push(test);
this.reportSuite.totalPasses++;
this.reportSuite.totalTests++;
root.mockData.stats.pending++;
root.mockData.stats.tests++;
}

this.reportSuite.passes = this.reportSuite.tests;
}

root.mockData.stats.suites = crawler.crawlingBuffer.length;
};

exports.NSAppCrawlingTreeNodeAction = NSAppCrawlingTreeNodeAction;
exports.NSAppCrawlingTreeNode = NSAppCrawlingTreeNode;

0 comments on commit 6e5bf63

Please sign in to comment.