js2 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,220 @@
1
+ class JS2::Test::SeleniumElement
2
+ attr_accessor :options
3
+ QUICK_WAIT_FOR = 5
4
+ AJAX_TIME_OUT = 60
5
+ def initialize (sel, klass, key, selector = nil)
6
+ @sel = sel
7
+ @key = key
8
+ @klass = klass
9
+ @selector = selector
10
+ end
11
+
12
+ def log (str)
13
+ if JS2::Test::Selenium.logger
14
+ JS2::Test::Selenium.logger.info(str)
15
+ end
16
+ end
17
+
18
+ def raw
19
+ ret = self.class.new(@sel, @klass, @key, @selector)
20
+ ret.options[:raw] = true
21
+ return ret
22
+ end
23
+
24
+ def check
25
+ return execute('checked=true')
26
+ end
27
+
28
+ def uncheck
29
+ return execute('checked=false')
30
+ end
31
+
32
+ def checked?
33
+ return execute('checked')
34
+ end
35
+
36
+ def execute (str)
37
+ wait_for_ajax
38
+ return @sel.execute(get_js_locator() + '.' + str)
39
+ end
40
+
41
+ def visible?(wait_for = nil)
42
+ return self.check_attribute(':visible', true, wait_for);
43
+ end
44
+
45
+ def sel_visible?()
46
+ return @sel.visible?(get_ele())
47
+ end
48
+
49
+ def hidden?(wait_for = nil)
50
+ return self.check_attribute(':visible', false, wait_for);
51
+ end
52
+
53
+ def find (str, idx = 0)
54
+ selector = "window.$(#{get_js_locator()}).find(#{str.to_json})[#{idx}]"
55
+ return self.class.new(@sel, @klass, @key, selector)
56
+ end
57
+
58
+ def filter (str, idx = 0)
59
+ selector = "window.$(#{get_js_locator()}).filter(#{str.to_json})[#{idx}]"
60
+ return self.class.new(@sel, @klass, @key, selector)
61
+ end
62
+
63
+ def get (idx)
64
+ # when jqObjects in //@+
65
+ selector = "#{get_js_locator()}[#{idx}][0]"
66
+ return self.class.new(@sel, @klass, @key, selector)
67
+ end
68
+
69
+ def [] (idx)
70
+ selector = "#{get_js_locator()}[#{idx}]"
71
+ return self.class.new(@sel, @klass, @key, selector)
72
+ end
73
+
74
+ def attr (attr)
75
+ get_ele()
76
+ js = %{return window.$(#{get_js_locator()}).attr(#{attr.to_json});}
77
+ return @sel.js_eval(js)
78
+ end
79
+
80
+ def check_attribute (attr, val, wait_for = nil)
81
+ wait_for ||= QUICK_WAIT_FOR
82
+ get_ele()
83
+ js = %{window.$(#{get_js_locator()}).is('#{attr}') == #{val.to_json};}
84
+ begin
85
+ log "Waiting for #{@key}.#{attr} to be #{val.to_json}... Waiting #{wait_for} secs."
86
+ @sel.wait_for_condition(js, wait_for)
87
+ rescue Selenium::CommandError
88
+ return false
89
+ end
90
+
91
+ return true
92
+ end
93
+
94
+ def containsp? (str)
95
+ wait_for_ajax
96
+ js = %{window.$(#{get_js_locator()}).html().toLowerCase().indexOf(#{str.to_json.downcase}) >= 0;}
97
+ begin
98
+ @sel.wait_for_condition(js, QUICK_WAIT_FOR)
99
+ return true
100
+ rescue Selenium::CommandError
101
+ return false
102
+ end
103
+ end
104
+
105
+ def contains? (str,timeout=QUICK_WAIT_FOR)
106
+ wait_for_ajax
107
+ js = %{window.$(#{get_js_locator()}).html().indexOf(#{str.to_json}) >= 0;}
108
+ begin
109
+ @sel.wait_for_condition(js, timeout)
110
+ return true
111
+ rescue Selenium::CommandError
112
+ return false
113
+ end
114
+ end
115
+
116
+ def click (options = {})
117
+ @sel.click(get_ele(), options)
118
+ end
119
+
120
+ def text
121
+ return @sel.text(get_ele())
122
+ end
123
+
124
+ def get_value
125
+ return @sel.get_value(get_ele())
126
+ end
127
+
128
+ def click! (options = {})
129
+ @sel.click(get_ele(), options)
130
+ @sel.wait_for_page
131
+ end
132
+
133
+ def mouse_up ()
134
+ trigger("LEFT_MOUSE_UP")
135
+ end
136
+
137
+ def mouse_down ()
138
+ trigger("LEFT_MOUSE_DOWN")
139
+ end
140
+
141
+ def mouse_over (options = {})
142
+ @sel.mouse_over(get_ele(), options)
143
+ end
144
+
145
+ def mouse_out (options = {})
146
+ @sel.mouse_out(get_ele(), options)
147
+ end
148
+
149
+ def type (*params)
150
+ elem = get_ele()
151
+ @sel.type(elem, *params)
152
+ @sel.fire_event(elem, 'blur')
153
+ end
154
+
155
+ def html ()
156
+ wait_for_ajax
157
+ js = get_js_locator()
158
+ return @sel.js_eval("#{js}.innerHTML;")
159
+ end
160
+
161
+ private
162
+
163
+ def get_locator
164
+ js = get_js_locator()
165
+ dom_locator = "dom=#{js};"
166
+ return dom_locator
167
+ end
168
+
169
+ def get_ele
170
+ real_locator = get_locator()
171
+ wait_for_ele()
172
+
173
+ return real_locator
174
+ end
175
+
176
+ def get_js_locator
177
+ if @selector
178
+ return @selector
179
+ end
180
+
181
+ filtered_locator = @key.to_s
182
+ int = ''
183
+
184
+ if m = filtered_locator.match(/([^\[\]]+)(\[([^\]]*)\])/)
185
+ log "filtered: #{filtered_locator} #{m.inspect}"
186
+ filtered_locator = m[1]
187
+ int = ', ' + m[3] if m[3]
188
+ end
189
+
190
+ funct = 'getVal'
191
+ get_val = "window.USE_SEL_MARKER.#{funct}('#{@klass}', '#{filtered_locator}'#{int})"
192
+
193
+ return get_val
194
+ end
195
+
196
+ def wait_for_ele ()
197
+ real_locator = get_locator
198
+ puts "waiting for #{real_locator}"
199
+ wait_for_ajax
200
+ @sel.wait_for_condition real_locator, QUICK_WAIT_FOR
201
+ puts "found #{real_locator}"
202
+ end
203
+
204
+ def wait_for_ajax
205
+ @sel.wait_for_condition('selenium.browserbot.getCurrentWindow().jQuery.active == 0', AJAX_TIME_OUT)
206
+ end
207
+
208
+ def trigger (event)
209
+ get_ele()
210
+ @sel.execute("window.$(#{get_js_locator}).trigger(JS2.SEL_EVENTS.#{event})")
211
+ end
212
+
213
+ def method_missing (method, *arg)
214
+ if @sel.respond_to?(method)
215
+ return @sel.send(method, get_js_locator, *arg)
216
+ else
217
+ raise "Method missing: #{method}"
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,27 @@
1
+ class JS2::Test::SeleniumHelper
2
+ attr_accessor :klass, :lookup
3
+
4
+ def initialize (klass, lookup, sel)
5
+ @klass = klass
6
+ @lookup = lookup
7
+ @sel = sel
8
+ @all_eles = Hash.new
9
+ end
10
+
11
+ def [] (key, tail = nil)
12
+ key = key.to_s
13
+
14
+ return @all_eles[key] if @all_eles[key]
15
+
16
+ # TODO: support inheritence
17
+ #raise "invalid key #{key}" unless @lookup[key]
18
+
19
+ return @all_eles[key] = JS2::Test::SeleniumElement.new(@sel, @klass, key, tail)
20
+ end
21
+
22
+ def type_form (params = {})
23
+ params.each_pair do |k,v|
24
+ self[k].type(v)
25
+ end
26
+ end
27
+ end
data/lib/js2.rb ADDED
@@ -0,0 +1,95 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module Js2
5
+ VERSION = '0.0.1'
6
+ end
7
+
8
+ module JS2
9
+ module Parser
10
+ end
11
+ module AST
12
+ end
13
+ module Process
14
+ end
15
+ module Decorator
16
+ end
17
+ module Test
18
+ end
19
+
20
+ end
21
+
22
+ $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
23
+
24
+ # for others
25
+ require 'js2/processor'
26
+ require 'js2/daemon'
27
+ require 'js2/config'
28
+
29
+ # parser
30
+ require 'js2/parser/fast'
31
+ require 'js2/parser/haml'
32
+
33
+ # process
34
+ require 'js2/process/haml_engine'
35
+ require 'js2/process/file_handler'
36
+ require 'js2/process/universe'
37
+
38
+ # ast
39
+ require 'js2/ast/node'
40
+ require 'js2/ast/class_node'
41
+ require 'js2/ast/comment_node'
42
+ require 'js2/ast/haml_node'
43
+ require 'js2/ast/inherited_node'
44
+ require 'js2/ast/stuff_node'
45
+ require 'js2/ast/member_node'
46
+ require 'js2/ast/method_node'
47
+ require 'js2/ast/module_node'
48
+ require 'js2/ast/include_node'
49
+ require 'js2/ast/nodes'
50
+
51
+ # decorator
52
+ require 'js2/decorator/standard'
53
+ require 'js2/decorator/test'
54
+ require 'js2/decorator/cleanser'
55
+
56
+ require 'yaml'
57
+
58
+ # test
59
+ require 'js2/test/selenium'
60
+ require 'js2/test/selenium_helper'
61
+ require 'js2/test/selenium_element'
62
+
63
+ module JS2
64
+ def self.config
65
+ return @@config
66
+ end
67
+
68
+ def self.config_file= (config_file)
69
+ @@config = YAML.load_file(config_file)
70
+ end
71
+
72
+ def self.write_files(params)
73
+ return self.daemon.run(1)
74
+ end
75
+
76
+ def self.daemon (params)
77
+ return JS2::Process::Daemon.new(params)
78
+ end
79
+
80
+ begin
81
+ # for haml filter
82
+ module JS2
83
+ include Haml::Filters::Base
84
+
85
+ def render (text)
86
+ parser = ::JS2::Parser::Fast.new
87
+ nodes = parser.parse_string(text)
88
+ decorator = ::JS2::Decorator::Test.new
89
+ return "<script>" + decorator.draw_nodes(nodes) + "</script>"
90
+ end
91
+ end
92
+ rescue Exception
93
+ puts "No haml filter support"
94
+ end
95
+ end
@@ -0,0 +1,9 @@
1
+ namespace :js2 do
2
+ task :build => :environment do
3
+ # normal stuff
4
+ CONFIG_FILE = "#{RAILS_ROOT}/config/js2.yml"
5
+ d = JS2::Processor.daemon_from_file(CONFIG_FILE, RAILS_ENV)
6
+ d.run(1)
7
+ end
8
+
9
+ end
@@ -0,0 +1,322 @@
1
+ # Somewhat based on http://www.mozilla.org/js/language/js20-2000-07/formal/lexer-grammar.html
2
+ # Regular Expression Literals determined with these rules:
3
+ # http://www.mozilla.org/js/language/js20-1999-03-25/tokens.html
4
+
5
+ %%{
6
+ machine dude;
7
+ alphtype char;
8
+
9
+ action actionStringBegin {
10
+ }
11
+
12
+ action actionStringAcc {
13
+ }
14
+
15
+ action actionStringAccBsEscape {
16
+ }
17
+
18
+ action actionStringAccUtf16 {
19
+ }
20
+
21
+ unicodecharacter = any;
22
+ unicodeinitialalphabetic = alpha;
23
+ unicodealphanumeric = alnum;
24
+ whitespacecharacter = [\t\v\f ];
25
+ lineterminator = ('\r' | '\n');
26
+ asciidigit = digit;
27
+
28
+ literals = (
29
+ '==' | '!=' | '===' | '!==' | '<=' | '>=' | '||' | '&&' | '++' | '--' | '<<' | '<<=' | '>>' | '>>=' | '>>>' | '>>>='| '&=' | '%=' | '^=' | '|=' | '+=' | '-=' | '*=' | '/='
30
+ );
31
+
32
+ # put in class
33
+ # put in accessor
34
+ keywords = (
35
+ 'break' | 'case' | 'catch' | 'continue' | 'default' | 'delete' | 'do' |
36
+ 'else' | 'finally' | 'for' | 'function' | 'if' | 'in' | 'instanceof' |
37
+ 'new' | 'return' | 'switch' | 'this' | 'throw' | 'try' | 'typeof' | 'var' | 'accessor' |
38
+ 'void' | 'while' | 'with' | 'const' | 'true' | 'false' | 'null' | 'debugger' |
39
+ 'class' | 'static' | 'foreach' | 'module' | 'include' | 'property'
40
+ );
41
+
42
+ # took out class
43
+ reserved = (
44
+ 'abstract' | 'boolean' | 'byte' | 'char' | 'double' | 'enum' | 'export' |
45
+ 'extends' | 'final' | 'float' | 'goto' | 'implements' | 'import' | 'int' | 'interface' |
46
+ 'long' | 'native' | 'package' | 'private' | 'protected' | 'public' | 'short' |
47
+ 'super' | 'synchronized' | 'throws' | 'transient' | 'volatile'
48
+ );
49
+
50
+ nonterminator = any - lineterminator;
51
+ linecommentcharacters = nonterminator*;
52
+ nonterminatororslash = nonterminator - '/';
53
+ nonterminatororastreisk = nonterminator - '*';
54
+ nonterminatororasteriskorslash = nonterminator - ('*' | '/');
55
+ blockcommentcharacters = (nonterminatororslash | (nonterminatororastreisk '/'))*;
56
+ multilineblockcommentcharacters = (blockcommentcharacters lineterminator)*;
57
+
58
+ linecomment = '//' linecommentcharacters;
59
+ singlelineblockcomment = '/*' blockcommentcharacters '*/';
60
+ multilineblockcomment = '/*' multilineblockcommentcharacters blockcommentcharacters '*/';
61
+ comment = linecomment | singlelineblockcomment | multilineblockcomment;
62
+
63
+ string_begin = '\'' @ actionStringBegin;
64
+ string_end = '\'';
65
+ stringchar_normal = ^(['\\] | 0..0x1f) @ actionStringAcc;
66
+ stringchar_bs_esc = '\\'['\\/bfnrt] @ actionStringAccBsEscape;
67
+ stringchar_utf16 = '\\u'[0-9a-fA-F]{4} @ actionStringAccUtf16;
68
+ stringchar_bs_other = '\\'^(['\\/bfnrtu]|0..0x1f) @ actionStringAccBsEscape;
69
+ stringchar = (stringchar_normal | stringchar_bs_esc | stringchar_utf16 | stringchar_bs_other);
70
+ string = string_begin . stringchar* . string_end;
71
+
72
+ ds_string_begin = '"' @ actionStringBegin;
73
+ ds_string_end = '"';
74
+ ds_stringchar_normal = ^(["\\] | 0..0x1f) @ actionStringAcc;
75
+ ds_stringchar_bs_esc = '\\'["\\/bfnrt] @ actionStringAccBsEscape;
76
+ ds_stringchar_utf16 = '\\u'[0-9a-fA-F]{4} @ actionStringAccUtf16;
77
+ ds_stringchar_bs_other = '\\'^(["\\/bfnrtu]|0..0x1f) @ actionStringAccBsEscape;
78
+ ds_stringchar = (ds_stringchar_normal | ds_stringchar_bs_esc | ds_stringchar_utf16 | ds_stringchar_bs_other);
79
+ ds_string = ds_string_begin . ds_stringchar* . ds_string_end;
80
+
81
+ all_string = string | ds_string;
82
+
83
+ integer = '-'? . digit+;
84
+ float = '-'? (
85
+ (('0' | [1-9][0-9]*) '.' [0-9]+ ([Ee] [+\-]?[0-9]+)?)
86
+ | (('0' | [1-9][0-9]*) ([Ee] [+\-]?[0-9]+))
87
+ );
88
+ number = integer | float;
89
+
90
+ identifier_char = ([a-zA-Z0-9_]) | '$';
91
+ identifier = identifier_char+;
92
+
93
+ ordinaryregexpchar = nonterminator - ('/' | '\\');
94
+ regexpchars = (ordinaryregexpchar | '\\' nonterminator)+;
95
+ regexpbody = '/' @ {regexp_start = p;} regexpchars '/';
96
+ regexpflags = ('g' | 'i' | 'm')*;
97
+ regexpliteral = regexpbody regexpflags;
98
+
99
+ specialcasedivide = (
100
+ identifier '/'
101
+ );
102
+ single_char = any;
103
+
104
+ arg_list = '(' . whitespacecharacter* . (identifier . whitespacecharacter* . ',' . whitespacecharacter*)* . identifier? . whitespacecharacter* . ')';
105
+ curry_with = 'with' . whitespacecharacter* . arg_list;
106
+ curry = 'curry' . whitespacecharacter* . arg_list? . whitespacecharacter* . curry_with? . whitespacecharacter* . '{';
107
+
108
+ main := |*
109
+
110
+ lineterminator => {
111
+ line_number++;
112
+ };
113
+
114
+ curry => {
115
+ curr = ts-data;
116
+ <%= replacer.start :CURRY %>
117
+ cb_count++;
118
+ };
119
+
120
+ whitespacecharacter => {};
121
+ comment => {
122
+ if (<%= replacer.directly_in? :CLASS %>) {
123
+ <%= replacer.do_next(:COMMENT, 'ts-data', 'te-data-1') %>
124
+ }
125
+ };
126
+
127
+ all_string => {};
128
+ number => {};
129
+ keywords => {
130
+ curr = ts-data;
131
+
132
+ <%= replacer.set_keyword %>
133
+
134
+ if (<%= replacer.is? :CLASS %> || <%= replacer.is? :MODULE %>) {
135
+ if (<%= replacer.is? :CLASS %>) {
136
+ <%= replacer.start :CLASS %>
137
+ } else {
138
+ <%= replacer.start :MODULE %>
139
+ }
140
+
141
+ // if "class" wasn't the first thing in the file
142
+ // then submit the :STUFF
143
+ if (curr > 0) {
144
+ <%= replacer.stop :STUFF, 'curr-1' %>
145
+ }
146
+
147
+ } else if (<%= replacer.directly_in? :CLASS %> || <%= replacer.directly_in? :MODULE %>) {
148
+ if (<%= replacer.is? :STATIC %>) {
149
+ <%= replacer.set_static %>
150
+ }
151
+
152
+ if (<%= replacer.is? :FUNCTION %>) {
153
+ <%= replacer.start :METHOD %>
154
+ }
155
+
156
+ if (<%= replacer.is? :VAR %>) {
157
+ <%= replacer.start :MEMBER %>
158
+ }
159
+
160
+
161
+ if (<%= replacer.is? :ACCESSOR %>) {
162
+ <%= replacer.start :MEMBER %>
163
+ }
164
+
165
+ if (<%= replacer.is? :PROPERTY %>) {
166
+ <%= replacer.start :PROPERTY %>
167
+ }
168
+
169
+
170
+ if (<%= replacer.is? :INCLUDE %>) {
171
+ <%= replacer.start :INCLUDE %>
172
+ }
173
+
174
+ }
175
+
176
+ if (<%= replacer.is? :FOREACH %>) {
177
+ <%= replacer.start :FOREACH %>
178
+ }
179
+
180
+ };
181
+
182
+ literals => { };
183
+ reserved => { };
184
+ identifier => { };
185
+
186
+ <%= replacer.def_literals %>
187
+
188
+ single_char | (('(' | '{' | ';') whitespacecharacter* regexpliteral) => {
189
+ curr = ts-data;
190
+ single = data[ts-data];
191
+
192
+ if (single == '(') {
193
+ br_count++;
194
+ } else if (single == ')') {
195
+ br_count--;
196
+
197
+ if (in_FOREACH && br_count == br_lvl_FOREACH) {
198
+ <%= replacer.stop :FOREACH %>
199
+ }
200
+ }
201
+
202
+ if (single == '{') {
203
+ cb_count++;
204
+ } else if (single == '}') {
205
+ if (<%= replacer.directly_in? :CLASS %> || <%= replacer.directly_in? :MODULE %>) {
206
+ <%= replacer.start :STUFF, 'curr+1' %>
207
+ }
208
+
209
+ cb_count--;
210
+
211
+ // stuff that stops on }'s
212
+ <%= replacer.handle_stops [ :METHOD, :CLASS, :FOREACH, :MODULE, :CURRY ] %>
213
+ }
214
+
215
+ if (single == ';') {
216
+ <%= replacer.handle_semicolon_stops [ :MEMBER, :INCLUDE, :PROPERTY ] %>
217
+ }
218
+
219
+ };
220
+
221
+ *|;
222
+ }%%
223
+
224
+ require 'rubygems'
225
+ require 'inline'
226
+
227
+ class JS2::Parser::Fast
228
+ attr_accessor :data
229
+
230
+ inline do |builder|
231
+ builder.c_raw <<-END
232
+
233
+ int do_parse (int argc, VALUE *argv, VALUE self) {
234
+ // convert ruby string to char*
235
+ VALUE r_str = argv[0];
236
+ int data_length = RSTRING(r_str)->len;
237
+ char* data = STR2CSTR(r_str);
238
+
239
+ VALUE ln_argv[1];
240
+ int ln_argc = 1;
241
+ int line_number = 1;
242
+ ID ln_sym = rb_intern("set_line_number");
243
+
244
+ // do_next vars
245
+ VALUE do_next_argv[4];
246
+ int do_next_argc = 4;
247
+ ID do_next_sym = rb_intern("do_next");
248
+
249
+ VALUE set_static_argv[0];
250
+ int set_static_argc = 0;
251
+ ID set_static_sym = rb_intern("set_static");
252
+
253
+ // state vars
254
+ int i = 0; // iterator
255
+ int j = 0; // iterator
256
+ char word[100]; // keyword
257
+ char single;
258
+
259
+ int curr = 0;
260
+ int prev = -1;
261
+ int cb_count = 0;
262
+ int br_count = 0;
263
+
264
+ <%= replacer.declare_vars [ :CLASS, :METHOD, :MEMBER, :STUFF, :ACCESSOR, :COMMENT, :FOREACH, :MODULE, :INCLUDE, :CURRY, :PROPERTY ] %>
265
+
266
+ // ragel variables
267
+ int cs, act;
268
+ char *ts = NULL;
269
+ char *te = NULL;
270
+ char *p = data;
271
+ char *pe = p + data_length;
272
+ char *eof = pe;
273
+ char* regexp_start = NULL;
274
+
275
+ <%= replacer.start :STUFF %>
276
+
277
+ %% write data;
278
+ %% write init;
279
+ %% write exec;
280
+
281
+ if (in_STUFF == 1) {
282
+ <%= replacer.stop :STUFF %>
283
+ }
284
+ }
285
+
286
+ END
287
+ end
288
+
289
+ def set_line_number (l)
290
+ @line_number = l
291
+ end
292
+
293
+ def set_static ()
294
+ @nodes.set_static
295
+ end
296
+
297
+ def do_next (symbol, start_idx, end_idx, line)
298
+ if end_idx == -1 then end_idx = start_idx end
299
+ @nodes.do_next(symbol, start_idx, end_idx, line)
300
+ end
301
+
302
+ def parse_string (str)
303
+ self.data = str
304
+ @nodes = JS2::AST::Nodes.new("string", str)
305
+ self.do_parse(str)
306
+ return @nodes
307
+ end
308
+
309
+ def parse(filename)
310
+ self.data = File.read(filename)
311
+ @nodes = JS2::AST::Nodes.new(filename, data)
312
+
313
+ #@cleanser ||= JS2::DecoCleanser.new
314
+ #@cleanser.cleanse(data)
315
+
316
+ eof = data.size
317
+ self.do_parse(data)
318
+
319
+ return @nodes
320
+ end
321
+
322
+ end