marta 0.37396 → 0.41245
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +42 -19
- data/example_project/p_object/test_page.rb +2 -1
- data/example_project/project_itself/iframe.html +5 -0
- data/{lib/marta/data/test.html → example_project/project_itself/index.html} +1 -1
- data/example_project/spec/p_object/pageobjects/MartaTestPage.json +707 -124
- data/example_project/spec/p_object/pageobjects/TheIframe.json +70 -14
- data/example_project/spec/spec_helper.rb +3 -1
- data/lib/marta/black_magic.rb +59 -13
- data/lib/marta/data/element.html +4 -15
- data/lib/marta/data/element.js +25 -226
- data/lib/marta/data/style.css +2 -2
- data/lib/marta/dialogs.rb +52 -4
- data/lib/marta/element_information.rb +86 -0
- data/lib/marta/injector.rb +2 -2
- data/lib/marta/json_2_class.rb +1 -1
- data/lib/marta/lightning.rb +1 -0
- data/lib/marta/marta_app/content.js +8 -0
- data/lib/marta/marta_app/devtools.html +1 -0
- data/lib/marta/marta_app/devtools.js +5 -0
- data/lib/marta/marta_app/manifest.json +3 -2
- data/lib/marta/options_and_paths.rb +1 -10
- data/lib/marta/page_arithmetic.rb +85 -97
- data/lib/marta/public_methods.rb +5 -4
- data/lib/marta/read_write.rb +50 -1
- data/lib/marta/server.rb +0 -18
- data/lib/marta/simple_element_finder.rb +0 -19
- data/lib/marta/version.rb +1 -1
- data/lib/marta/x_path.rb +154 -139
- metadata +7 -3
data/lib/marta/data/style.css
CHANGED
@@ -158,7 +158,7 @@ div[martaclass=marta_div] {
|
|
158
158
|
outline: 2px solid #bbbbbb;
|
159
159
|
background-color: lightgray;
|
160
160
|
position : relative;
|
161
|
-
z-index :
|
161
|
+
z-index : 2147483647;
|
162
162
|
padding: 0;
|
163
163
|
margin: 0px 0px 0px 0px;
|
164
164
|
overflow-y: scroll;
|
@@ -228,7 +228,7 @@ div[martastyle=at_large] {
|
|
228
228
|
width: 100%;
|
229
229
|
pointer-events: visible;
|
230
230
|
display: block;
|
231
|
-
z-index:
|
231
|
+
z-index: 2147483646;
|
232
232
|
}
|
233
233
|
|
234
234
|
div[martastyle=off] {
|
data/lib/marta/dialogs.rb
CHANGED
@@ -4,6 +4,7 @@ require 'marta/lightning'
|
|
4
4
|
require 'marta/injector'
|
5
5
|
require 'marta/public_methods'
|
6
6
|
require 'marta/page_arithmetic'
|
7
|
+
require 'marta/element_information'
|
7
8
|
|
8
9
|
module Marta
|
9
10
|
|
@@ -23,7 +24,7 @@ module Marta
|
|
23
24
|
class MethodSpeaker
|
24
25
|
|
25
26
|
include XPath, Lightning, Injector, PublicMethods, SimpleElementFinder,
|
26
|
-
PageArithmetic
|
27
|
+
PageArithmetic, ElementInformation
|
27
28
|
|
28
29
|
def initialize(method_name, requestor)
|
29
30
|
@class_name = requestor.class_name
|
@@ -83,8 +84,7 @@ module Marta
|
|
83
84
|
def attrs_plus_result
|
84
85
|
if !attrs_exists?
|
85
86
|
@attrs = @result
|
86
|
-
elsif !@
|
87
|
-
!@result['options']['collection']
|
87
|
+
elsif !@result['options']['collection']
|
88
88
|
@attrs = @result
|
89
89
|
else
|
90
90
|
@attrs = make_collection(@attrs, @result)
|
@@ -93,7 +93,17 @@ module Marta
|
|
93
93
|
|
94
94
|
# Asking: "What are you looking for?"
|
95
95
|
def ask_for_elements
|
96
|
-
ask 'element', "Found #{@found} elements for #{@title}", @attrs
|
96
|
+
answer = ask 'element', "Found #{@found} elements for #{@title}", @attrs
|
97
|
+
return answer.class == Hash ? answer_to_hash(answer) : answer
|
98
|
+
end
|
99
|
+
|
100
|
+
# Creating new fashioned hash out of data
|
101
|
+
def answer_to_hash(answer)
|
102
|
+
result = method_structure
|
103
|
+
result['options']['collection'] = answer['collection']
|
104
|
+
what = answer['exclude'] ? 'negative' : 'positive'
|
105
|
+
result[what] = get_attributes(answer['element'])
|
106
|
+
result
|
97
107
|
end
|
98
108
|
|
99
109
|
# Creating data to save when it is a basically defined element
|
@@ -173,8 +183,46 @@ module Marta
|
|
173
183
|
def user_method_dialogs(method_name)
|
174
184
|
dialog_master = MethodSpeaker.new(method_name, self)
|
175
185
|
data = dialog_master.dialog
|
186
|
+
data['meths'][method_name] =
|
187
|
+
dynamise_method(data['vars'], data['meths'][method_name])
|
176
188
|
file_write(self.class_name.to_s, data)
|
177
189
|
data
|
178
190
|
end
|
191
|
+
|
192
|
+
# Massive gsub for attribute
|
193
|
+
def dynamise(variable_name, what)
|
194
|
+
what.each do |entry|
|
195
|
+
entry.each do |value|
|
196
|
+
value.gsub!(self.instance_variable_get("@#{variable_name}"),
|
197
|
+
'#{@' + variable_name + '}')
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# Marta will search for page variables in attributes of element in order
|
203
|
+
# to create dynamic element by itself. It must be splited. And moved.
|
204
|
+
def dynamise_method(vars, method)
|
205
|
+
vars.each_pair do |variable_name, variable|
|
206
|
+
if variable_name.include?('text')
|
207
|
+
dynamise variable_name, [method['positive']['self']['text'],
|
208
|
+
method['positive']['pappy']['text'],
|
209
|
+
method['positive']['granny']['text'],
|
210
|
+
method['negative']['self']['text'],
|
211
|
+
method['negative']['pappy']['text'],
|
212
|
+
method['negative']['granny']['text']]
|
213
|
+
else
|
214
|
+
[method['positive'], method['negative']].each do |method|
|
215
|
+
method.each_pair do |level, content|
|
216
|
+
content['attributes'].each_pair do |attribute_name, values|
|
217
|
+
if variable_name.include?(attribute_name)
|
218
|
+
dynamise variable_name, [values]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
method
|
226
|
+
end
|
179
227
|
end
|
180
228
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Marta
|
2
|
+
|
3
|
+
# Marta is creating a hash of element data. For now it stores
|
4
|
+
# tag, text, and all the attributes.
|
5
|
+
module ElementInformation
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
#
|
10
|
+
# We are using helper class which can parse element attributes to our
|
11
|
+
# special hash format.
|
12
|
+
#
|
13
|
+
# @note It is believed that no user will use it
|
14
|
+
class ElementHelper
|
15
|
+
|
16
|
+
def initialize(requestor)
|
17
|
+
@engine = requestor.engine
|
18
|
+
end
|
19
|
+
|
20
|
+
# We can get data of the element or data of any parent.
|
21
|
+
def get_element_info(element, parent_count = 0)
|
22
|
+
parent = ''
|
23
|
+
parent_count.times do
|
24
|
+
parent = parent + '.parentElement'
|
25
|
+
end
|
26
|
+
result = Hash.new
|
27
|
+
attr_script = %Q[
|
28
|
+
var s = {};
|
29
|
+
var attrs = arguments[0]#{parent}.attributes;
|
30
|
+
for (var l = 0; l < attrs.length; ++l) {
|
31
|
+
var a = attrs[l]; s[a.name] = a.value.split(" ");
|
32
|
+
} ;
|
33
|
+
return s;]
|
34
|
+
tag_script = "return arguments[0]#{parent}.tagName"
|
35
|
+
text_script = %Q[
|
36
|
+
if (arguments[0]#{parent}.textContent == arguments[0]#{parent}.innerHTML)
|
37
|
+
{return arguments[0]#{parent}.textContent} else {return ''};]
|
38
|
+
result['tag'] = [@engine.execute_script(tag_script, element)]
|
39
|
+
txt = @engine.execute_script(text_script, element)
|
40
|
+
result['text'] = txt != '' ? [txt] : []
|
41
|
+
result['attributes'] = @engine.execute_script(attr_script, element)
|
42
|
+
result['attributes'].each_pair do |attribute, value|
|
43
|
+
value.uniq!
|
44
|
+
end
|
45
|
+
return result
|
46
|
+
end
|
47
|
+
|
48
|
+
# That class is also stores an empty special format hash.
|
49
|
+
def self.method_structure(collection = false)
|
50
|
+
return {'options' => {'collection' => collection},
|
51
|
+
'positive' => {
|
52
|
+
'self' => {
|
53
|
+
'text'=>[], 'tag' => [], 'attributes' => {}},
|
54
|
+
'pappy' => {
|
55
|
+
'text'=>[], 'tag' => [], 'attributes' => {}},
|
56
|
+
'granny' => {
|
57
|
+
'text'=>[], 'tag' => [], 'attributes' => {}}},
|
58
|
+
'negative' => {
|
59
|
+
'self' => {
|
60
|
+
'text'=>[], 'tag' => [], 'attributes' => {}},
|
61
|
+
'pappy' => {
|
62
|
+
'text'=>[], 'tag' => [], 'attributes' => {}},
|
63
|
+
'granny' => {
|
64
|
+
'text'=>[], 'tag' => [], 'attributes' => {}}}
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# We are getting three levels of attributes of element,
|
70
|
+
# parent and grandparent
|
71
|
+
def get_attributes(element, requestor = self)
|
72
|
+
result = Hash.new
|
73
|
+
element_helper = ElementHelper.new requestor
|
74
|
+
result['self'] = element_helper.get_element_info element
|
75
|
+
result['pappy'] = element_helper.get_element_info element, 1
|
76
|
+
result['granny'] = element_helper.get_element_info element, 2
|
77
|
+
return result
|
78
|
+
end
|
79
|
+
|
80
|
+
# We can return the default structure of our special format
|
81
|
+
def method_structure(collection = false)
|
82
|
+
ElementHelper.method_structure collection
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
data/lib/marta/injector.rb
CHANGED
@@ -127,9 +127,9 @@ module Marta
|
|
127
127
|
result = MartaServer.wait_user_dialog_response
|
128
128
|
# We need double check for iframes here. It should be 100% changed.
|
129
129
|
if !result
|
130
|
-
result = engine.execute_script("return document.marta_confirm_mark")
|
130
|
+
result = @engine.execute_script("return document.marta_confirm_mark")
|
131
131
|
end
|
132
|
-
if (!result and
|
132
|
+
if (!result and !@engine.element(id: 'marta_s_everything').exists?)
|
133
133
|
actual_injection
|
134
134
|
end
|
135
135
|
end
|
data/lib/marta/json_2_class.rb
CHANGED
@@ -104,7 +104,7 @@ module Marta
|
|
104
104
|
|
105
105
|
def build_method(name, content)
|
106
106
|
define_singleton_method name.to_sym do
|
107
|
-
learn_status ? method_edit(name) : marta_magic_finder(content)
|
107
|
+
learn_status ? method_edit(name) : marta_magic_finder(content, name)
|
108
108
|
end
|
109
109
|
exact = name + '_exact'
|
110
110
|
define_singleton_method exact.to_sym do
|
data/lib/marta/lightning.rb
CHANGED
@@ -6,6 +6,13 @@ async function port_set(value){
|
|
6
6
|
});
|
7
7
|
};
|
8
8
|
|
9
|
+
function getSelected(value){
|
10
|
+
document.getElementById('marta_show_html').setAttribute('tag', value.tagName);
|
11
|
+
const x = document.getElementsByTagName(value.tagName);
|
12
|
+
const index = Array.prototype.indexOf.call(x, value);
|
13
|
+
document.getElementById('marta_show_html').setAttribute('index', index);
|
14
|
+
};
|
15
|
+
|
9
16
|
async function port_get(){
|
10
17
|
await chrome.storage.sync.get(['port'], function(result) {
|
11
18
|
port = result.port;
|
@@ -22,6 +29,7 @@ document.addEventListener("marta_send", async function(e) {
|
|
22
29
|
//console.log(port);
|
23
30
|
//console.log(e.detail.port);
|
24
31
|
console.log("Marta is acting back. With port = " + port);
|
32
|
+
console.log("Marta is acting back. With mark = " + e.detail.mark);
|
25
33
|
marta_real_send(e.detail.mark, port);
|
26
34
|
});
|
27
35
|
|
@@ -0,0 +1 @@
|
|
1
|
+
<script src="devtools.js"></script>
|
@@ -1,8 +1,9 @@
|
|
1
1
|
{
|
2
2
|
"name": "Marta app",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.41245",
|
4
4
|
"description": "Messaging from browser to main app",
|
5
|
-
"
|
5
|
+
"devtools_page": "devtools.html",
|
6
|
+
"permissions": ["activeTab", "http://127.0.0.1*/*", "storage", "contextMenus"],
|
6
7
|
"background": {
|
7
8
|
"scripts": ["background.js"],
|
8
9
|
"persistent": true
|
@@ -158,7 +158,7 @@ module Marta
|
|
158
158
|
|
159
159
|
# Marta uses simple rules to set the tolerancy value
|
160
160
|
def self.set_tolerancy(value)
|
161
|
-
@@tolerancy = parameter_set(@@tolerancy, value,
|
161
|
+
@@tolerancy = parameter_set(@@tolerancy, value, 100000)
|
162
162
|
end
|
163
163
|
|
164
164
|
def self.parameter_check_and_set(where, value, default, expected_class)
|
@@ -246,14 +246,5 @@ module Marta
|
|
246
246
|
SettingMaster.port
|
247
247
|
end
|
248
248
|
|
249
|
-
# Marta can call server easily
|
250
|
-
def server
|
251
|
-
SettingMaster.server
|
252
|
-
end
|
253
|
-
|
254
|
-
# Marta knows was the browser started by she
|
255
|
-
def correct_engine?
|
256
|
-
SettingMaster.correct_engine?
|
257
|
-
end
|
258
249
|
end
|
259
250
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'marta/element_information'
|
1
2
|
module Marta
|
2
3
|
|
3
4
|
#
|
@@ -15,126 +16,107 @@ module Marta
|
|
15
16
|
# @note It is believed that no user will use it
|
16
17
|
# Now it has only one way to merge hashes
|
17
18
|
# This method is getting common only of two methods in order to generate a
|
18
|
-
# correct hash for collection element.
|
19
|
-
# esoteric. Refactoring is a must here.
|
19
|
+
# correct hash for collection element.
|
20
20
|
class MethodMerger
|
21
21
|
|
22
|
+
include ElementInformation
|
23
|
+
|
22
24
|
# Class is taking two hashes. Sometimes order is valuable
|
23
25
|
def initialize(main_hash, second_hash)
|
24
26
|
@main_hash = main_hash
|
25
27
|
@second_hash = second_hash
|
26
28
|
end
|
27
29
|
|
28
|
-
POSITIVE = ['self', 'pappy', 'granny']
|
29
|
-
NEGATIVE = ['not_self', 'not_pappy', 'not_granny']
|
30
|
-
|
31
30
|
# Main method for adding two elements into a large-wide collection
|
32
31
|
def do_collection
|
33
|
-
result =
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
32
|
+
result = method_structure
|
33
|
+
# Everything is simple for now with options)
|
34
|
+
result['options'] = @main_hash['options']
|
35
|
+
# If we are adding to collection
|
36
|
+
if @second_hash['positive']['self']['tag'] != []
|
37
|
+
result['positive'] = multiply(@main_hash['positive'],
|
38
|
+
@second_hash['positive'])
|
39
|
+
result['negative'] = extract(@main_hash['negative'],
|
40
|
+
@second_hash['positive'])
|
41
|
+
else # If we are excluding from collection
|
42
|
+
result['positive'] = @main_hash['positive']
|
43
|
+
uniqs = extract(@second_hash['negative'], @main_hash['positive'])
|
44
|
+
result['negative'] = summarize(uniqs, @main_hash['negative'])
|
41
45
|
end
|
42
46
|
result
|
43
47
|
end
|
44
48
|
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
if (@second_hash['options'][key] == value) or
|
57
|
-
((@second_hash['options'][key].nil?) and (!value.nil?))
|
58
|
-
temp[key] = value
|
59
|
-
else
|
60
|
-
temp[key] = "*"
|
61
|
-
end
|
62
|
-
if (second_negative != temp[key]) and
|
63
|
-
((second_negative == main_negative) or
|
64
|
-
(main_negative.nil?))
|
65
|
-
temp["not_#{key}"] = second_negative
|
66
|
-
end
|
67
|
-
end
|
68
|
-
temp
|
49
|
+
# When black magic finds something
|
50
|
+
# she's not trusting dynamic attribute anymore. So she's forgetting
|
51
|
+
# unstable attributes and remembering stable and new ones
|
52
|
+
def forget_unstable
|
53
|
+
result = method_structure
|
54
|
+
result['options'] = @main_hash['options']
|
55
|
+
result['positive'] = merge(@main_hash['positive'],
|
56
|
+
@second_hash['positive'])
|
57
|
+
result['negative'] = extract(@main_hash['negative'],
|
58
|
+
@second_hash['positive'])
|
59
|
+
result
|
69
60
|
end
|
70
61
|
|
71
|
-
#
|
72
|
-
def
|
73
|
-
|
74
|
-
first
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
62
|
+
# Recursive operations with method.
|
63
|
+
def do_arithmetic(first, second, what)
|
64
|
+
what == '+' ? result = second : result = Hash.new
|
65
|
+
first.each_pair do |key, value|
|
66
|
+
if value.is_a? Hash
|
67
|
+
result[key] = do_arithmetic(first[key], second[key], what)
|
68
|
+
elsif !second[key].nil? and !value.nil?
|
69
|
+
if what == '+'
|
70
|
+
result[key] = (first[key] + second[key]).uniq
|
71
|
+
elsif what == '&'
|
72
|
+
result[key] = first[key] & second[key]
|
73
|
+
elsif what == '-'
|
74
|
+
result[key] = first[key] - second[key]
|
75
|
+
elsif what == '*'
|
76
|
+
if (second[key] != [])
|
77
|
+
result[key] = first[key] & second[key]
|
78
|
+
end
|
81
79
|
end
|
82
80
|
end
|
83
|
-
|
84
|
-
|
81
|
+
if (second[key] == [] or second[key].nil?) and
|
82
|
+
((what == '+') or (what == '*'))
|
83
|
+
result[key] = first[key]
|
84
|
+
end
|
85
85
|
end
|
86
|
-
|
86
|
+
result
|
87
87
|
end
|
88
88
|
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
if !first.nil?
|
97
|
-
temp = first
|
98
|
-
end
|
99
|
-
if !second.nil? and !temp.nil?
|
100
|
-
second.each_pair do |key, value|
|
101
|
-
if (temp[key].nil?) or ((temp[key] != value) and
|
102
|
-
(temp[key].class != Array) and (value.class != Array))
|
103
|
-
temp[key] = value
|
104
|
-
elsif (temp[key].class == Array) and (value.class == Array)
|
105
|
-
temp[key] = (value + temp[key]).uniq
|
106
|
-
end
|
107
|
-
end
|
108
|
-
if !first.nil?
|
109
|
-
first.each_pair do |key, value|
|
110
|
-
if second[key].nil? and !value.nil?
|
111
|
-
temp[key] = nil
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
else
|
116
|
-
temp = second
|
117
|
-
end
|
118
|
-
temp
|
89
|
+
# That is not a real merge. We are leaving everything that is the same
|
90
|
+
# or new and deleting everyting that is not the same
|
91
|
+
#
|
92
|
+
# Idea:
|
93
|
+
# merge({a:[1],b:[2],c:[3]},{a:[1],b:[2,3]}) #=> {a:[1],b:[2],c:[3]}
|
94
|
+
def merge(first, second)
|
95
|
+
do_arithmetic(second, first, '*')
|
119
96
|
end
|
120
97
|
|
121
|
-
#
|
122
|
-
#
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
98
|
+
# Simple adding everyting to everything
|
99
|
+
#
|
100
|
+
# Idea:
|
101
|
+
# summarize({a:[1],c:[4]},{a:[2],b:[3]}) #=> {a:[1,2],b:[3],c:[4]}
|
102
|
+
def summarize(first, second)
|
103
|
+
do_arithmetic(second, do_arithmetic(first, second, '+'), '+')
|
104
|
+
end
|
105
|
+
|
106
|
+
# That will leave only the same options in the result
|
107
|
+
#
|
108
|
+
# Idea:
|
109
|
+
# multiply({a:[1,2],b:[2],c:[5]},{a:[1,3],b:[4],d:[0]}) #=> {a:[1],b:[2]}
|
110
|
+
def multiply(first, second)
|
111
|
+
do_arithmetic(first, second, '&')
|
112
|
+
end
|
113
|
+
|
114
|
+
# That will take out of the result all options of second
|
115
|
+
#
|
116
|
+
# Idea
|
117
|
+
# extract({a:[1,2],b:[2],c:[5]},{a:[2],c:[5],d:[0]}) #=> {a:[1],b:[2]}
|
118
|
+
def extract(first, second)
|
119
|
+
do_arithmetic(first, second, '-')
|
138
120
|
end
|
139
121
|
end
|
140
122
|
|
@@ -143,5 +125,11 @@ module Marta
|
|
143
125
|
merger = MethodMerger.new(one, two)
|
144
126
|
merger.do_collection
|
145
127
|
end
|
128
|
+
|
129
|
+
# Forgetting unstable attributes leaving the same ones and new ones
|
130
|
+
def forget_unstable(old_one, new_one)
|
131
|
+
merger = MethodMerger.new(old_one, new_one)
|
132
|
+
merger.forget_unstable
|
133
|
+
end
|
146
134
|
end
|
147
135
|
end
|