-
Notifications
You must be signed in to change notification settings - Fork 4
/
ActionsHandler.js
152 lines (128 loc) · 4.63 KB
/
ActionsHandler.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import ActionsHelper from './ActionsHelper';
import glob from 'glob-all';
import path from 'path';
import { getRandomElementFromArray } from '../../shared/helpers';
/**
* Actions handler which can work with specific actions.
*/
export default class ActionsHandler {
/**
* @param {Object} config
*/
constructor(config) {
this._config = config;
this._actionsHelper = null;
this._actions = {};
}
/**
* Initializes actions handler dependencies
* @returns {ActionsHandler} this
*/
init() {
this._initActionsHelper();
this._loadActions();
return this;
}
/**
* Executes specific, or random action
* @param {Browser} instance
* @param {string} [actionId] Random if not defined
* @param {Object} [actionConfig]
* @returns {Promise<Object>} Resolves with action results
*/
async execute(instance, actionId, actionConfig) {
let pageAction;
try {
await this._actionsHelper.waitForReadyState(instance.page);
pageAction = await this._getAction(instance, actionId, actionConfig);
} catch (e) {
return { executionError: e.stack };
}
if (!pageAction) {
return { executionError: 'pageAction not defined!' };
}
let { Action, element } = pageAction;
let action = new Action(this._config, this._actionsHelper, actionConfig);
return action.execute(element, instance);
}
/**
* Gets random or specific action
* @param {Browser} instance
* @param {string} [actionId]
* @param {Object} [actionConfig]
* @returns {Object} { Action, element }
*/
async _getAction({ page }, actionId, actionConfig) {
if (actionId && actionConfig && actionConfig.selector) {
return {
Action: this._actions[actionId],
element: await this._actionsHelper.getElement(page, actionConfig),
};
}
let actions = actionId ? [actionId] : Object.keys(this._actions);
let pageActions = await this._getAvailablePageActions(page, actions);
if (pageActions.length > 0) {
return getRandomElementFromArray(pageActions);
}
throw Error(
'QApe is unable to determine any available actions ' +
`on the current page (url: '${page.url()}'). This can be caused by trying to ` +
'peform an action during a page load state, or wrong ' +
'configuration of "elementSelector".'
);
}
/**
* Initializes actions helper
*/
_initActionsHelper() {
this._actionsHelper = new ActionsHelper(this._config);
}
/**
* Loads all defined actions
*/
_loadActions() {
/*
* @FIXME Back action can get you to `about:blank` page,
* which does not have any elements available and causes
* crash of the QApe run. Back action should never get user
* to `about:blank` page. This needs to be fixed at
* `BackAction.prototype.isActionAvailable()`.
*/
let paths = [path.join(__dirname, '!(Abstract|Back)Action.js')];
if (this._config.customActions && this._config.customActions.length > 0) {
this._config.customActions.forEach((action) => paths.push(path.join(process.cwd(), action)));
}
glob.sync(paths).map((actionFile) => {
let action = require(actionFile).default;
if (this._actions[action.id]) {
throw Error(
'ActionsHandler: The same action id for multiple actions has been set. Action id must be unique!'
);
}
this._actions[action.id] = action;
});
}
/**
* @param {puppeteer.Page} page
* @param {string[]} availableActionIds
* @returns {Promise<Object[]>} Returns array of objects with keys
* Action and element, which are all available page actions
*/
async _getAvailablePageActions(page, availableActionIds) {
let elements = await this._actionsHelper.getAllVisiblePageElements(page);
let actions = [];
await Promise.all(
elements.map((element) => {
return Promise.all(
availableActionIds.map(async (id) => {
let Action = this._actions[id];
if (await Action.isActionAvailable(element, page, this._config)) {
actions.push({ Action, element });
}
})
);
})
);
return actions;
}
}