zena 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (392) hide show
  1. data/History.txt +23 -1
  2. data/MIT-LICENSE +2 -2
  3. data/README.rdoc +2 -15
  4. data/app/controllers/acls_controller.rb +1 -1
  5. data/app/controllers/documents_controller.rb +12 -3
  6. data/app/controllers/nodes_controller.rb +1 -3
  7. data/app/controllers/sites_controller.rb +7 -0
  8. data/app/controllers/versions_controller.rb +1 -1
  9. data/app/controllers/virtual_classes_controller.rb +7 -8
  10. data/app/models/acl.rb +3 -2
  11. data/app/models/relation_proxy.rb +2 -1
  12. data/app/models/role.rb +16 -0
  13. data/app/models/site.rb +48 -0
  14. data/app/models/text_document.rb +2 -1
  15. data/app/models/user.rb +2 -1
  16. data/app/models/zip.rb +2 -1
  17. data/app/views/acls/index.rhtml +11 -1
  18. data/app/views/documents/show.rhtml +1 -1
  19. data/app/views/relations/_add.erb +2 -2
  20. data/app/views/relations/_form.erb +5 -2
  21. data/app/views/relations/_li.erb +4 -1
  22. data/app/views/relations/index.erb +3 -3
  23. data/app/views/sites/_form.erb +1 -1
  24. data/app/views/sites/_job.erb +4 -0
  25. data/app/views/sites/index.erb +1 -1
  26. data/app/views/sites/jobs.erb +10 -0
  27. data/app/views/templates/document_create_tabs/_file.rhtml +3 -0
  28. data/app/views/zafu/default/Node-+search.zafu +2 -2
  29. data/bricks/acls/zena/init.rb +2 -1
  30. data/bricks/acls/zena/test/integration/acl_integration_test.rb +5 -5
  31. data/bricks/acls/zena/test/sites/erebus/acls.yml +2 -2
  32. data/bricks/acls/zena/test/unit/acl_test.rb +2 -2
  33. data/bricks/grid/lib/bricks/grid.rb +110 -0
  34. data/bricks/grid/zena/init.rb +4 -0
  35. data/bricks/math/lib/bricks/math.rb +1 -0
  36. data/bricks/pdf/lib/bricks/pdf.rb +10 -1
  37. data/bricks/sphinx/zena/tasks.rb +8 -40
  38. data/bricks/spreadsheet/README +31 -0
  39. data/bricks/spreadsheet/lib/bricks/spreadsheet.rb +293 -0
  40. data/bricks/spreadsheet/zena/init.rb +3 -0
  41. data/bricks/spreadsheet/zena/test/sites/zena/links.yml +11 -0
  42. data/bricks/spreadsheet/zena/test/unit/xlsx_test.rb +5 -0
  43. data/bricks/spreadsheet/zena/test/zafu/README +27 -0
  44. data/bricks/spreadsheet/zena/test/zafu/spreadsheet.yml +19 -0
  45. data/bricks/worker/lib/bricks/worker.rb +11 -5
  46. data/bricks/worker/zena/init.rb +8 -0
  47. data/bricks/worker/zena/migrate/20120501091514_add_site_id_to_jobs.rb +9 -0
  48. data/bricks/zena/zena/migrate/01_base.rb +0 -7
  49. data/bricks/zena/zena/migrate/20111214112233_add_comment_to_relations.rb +9 -0
  50. data/config/bricks.yml +15 -1
  51. data/config/environments/production.rb +2 -2
  52. data/config/gems.yml +6 -1
  53. data/doc/zafu_changes.yml +1 -1
  54. data/lib/tasks/zena.rake +6 -2
  55. data/lib/zena/app.rb +1 -1
  56. data/lib/zena/code/default_syntax.rb +5 -1
  57. data/lib/zena/db_helper/mysql.rb +5 -1
  58. data/lib/zena/deploy/app_init.rhtml +11 -7
  59. data/lib/zena/deploy/awstats.conf.rhtml +1 -1
  60. data/lib/zena/deploy/start_stop.rhtml +5 -5
  61. data/lib/zena/deploy/vhost.rhtml +16 -2
  62. data/lib/zena/deploy.rb +5 -3
  63. data/lib/zena/info.rb +1 -1
  64. data/lib/zena/parser/zazen_rules.rb +7 -7
  65. data/lib/zena/remote/node.rb +7 -0
  66. data/lib/zena/routes.rb +4 -5
  67. data/lib/zena/test_controller.rb +18 -1
  68. data/lib/zena/use/action.rb +13 -4
  69. data/lib/zena/use/ajax.rb +10 -4
  70. data/lib/zena/use/authlogic.rb +1 -1
  71. data/lib/zena/use/calendar.rb +4 -0
  72. data/lib/zena/use/conditional.rb +3 -0
  73. data/lib/zena/use/dates.rb +44 -4
  74. data/lib/zena/use/display.rb +45 -15
  75. data/lib/zena/use/forms.rb +59 -30
  76. data/lib/zena/use/query_builder.rb +12 -12
  77. data/lib/zena/use/recursion.rb +1 -1
  78. data/lib/zena/use/rendering.rb +18 -17
  79. data/lib/zena/use/urls.rb +2 -1
  80. data/lib/zena/use/zafu_safe_definitions.rb +0 -9
  81. data/lib/zena/use/zafu_templates.rb +2 -2
  82. data/lib/zena/use/zazen.rb +1 -3
  83. data/lib/zena/use.rb +13 -2
  84. data/lib/zena.rb +8 -2
  85. data/public/images/bullet_back.png +0 -0
  86. data/public/javascripts/grid.js +535 -0
  87. data/public/javascripts/tablekit.js +0 -0
  88. data/public/javascripts/window.js +0 -0
  89. data/public/javascripts/zena.js +17 -7
  90. data/public/stylesheets/admin.css +5 -1
  91. data/public/stylesheets/grid.css +19 -0
  92. data/test/fixtures/files/TestNode.zafu +123 -311
  93. data/test/fixtures/files/translations_fr.yml +6 -1
  94. data/test/functional/acls_controller_test.rb +2 -2
  95. data/test/functional/nodes_controller_test.rb +17 -11
  96. data/test/functional/virtual_classes_controller_test.rb +1 -1
  97. data/test/integration/query_node/basic.yml +9 -0
  98. data/test/integration/query_node/dates.yml +1 -1
  99. data/test/integration/zafu_compiler/ajax.yml +9 -9
  100. data/test/integration/zafu_compiler/basic.yml +0 -24
  101. data/test/integration/zafu_compiler/dates.yml +24 -7
  102. data/test/integration/zafu_compiler/display.yml +45 -2
  103. data/test/integration/zafu_compiler/errors.yml +0 -6
  104. data/test/integration/zafu_compiler/forms.yml +41 -3
  105. data/test/integration/zafu_compiler/query.yml +29 -3
  106. data/test/integration/zafu_compiler/roles.yml +0 -4
  107. data/test/integration/zafu_compiler/safe_definitions.yml +1 -1
  108. data/test/integration/zafu_compiler_test.rb +11 -3
  109. data/test/selenium/Add/add3.rsel +8 -8
  110. data/test/selenium/Drop/drop1.rsel +12 -12
  111. data/test/selenium/Drop/drop2.rsel +14 -14
  112. data/test/selenium/Drop/drop3.rsel +21 -21
  113. data/test/selenium/Drop/drop4.rsel +1 -1
  114. data/test/selenium/Edit/edit2.rsel +9 -9
  115. data/test/selenium/Edit/edit4.rsel +55 -0
  116. data/test/selenium/Edit/edit5.rsel +41 -0
  117. data/test/selenium/Edit/edit6.rsel +53 -0
  118. data/test/selenium/Filter/0setup.rsel +12 -0
  119. data/test/selenium/Filter/filter1.rsel +9 -0
  120. data/test/selenium/Filter/filter2.rsel +9 -0
  121. data/test/selenium/Form/form1.rsel +1 -1
  122. data/test/selenium/Form/form2.rsel +1 -1
  123. data/test/selenium/Form/form3.rsel +15 -0
  124. data/test/selenium/Grid/0setup.rsel +13 -0
  125. data/test/selenium/Grid/grid1.rsel +11 -0
  126. data/test/selenium/Swap/0setup.rsel +12 -0
  127. data/test/selenium/Swap/swap1.rsel +13 -0
  128. data/test/selenium/Swap/swap2.rsel +11 -0
  129. data/test/selenium/Toggle/toggle1.rsel +6 -6
  130. data/test/sites/zena/versions.yml +1 -1
  131. data/test/unit/relation_proxy_test.rb +36 -0
  132. data/test/unit/site_test.rb +10 -2
  133. data/test/unit/zena/use/rendering_test.rb +7 -6
  134. data/test/unit/zena/use/zazen_test.rb +1 -2
  135. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/Run 2.tmCommand +24 -0
  136. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/Run Context.tmCommand +58 -0
  137. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/Run Focused Should.tmCommand +88 -0
  138. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/Run.tmCommand +27 -0
  139. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/YAML to Shoulda.tmCommand +23 -0
  140. data/vendor/TextMate/Ruby Shoulda.tmbundle/Preferences/Symbol List: Context.tmPreferences +19 -0
  141. data/vendor/TextMate/Ruby Shoulda.tmbundle/Preferences/Symbol List: Should.tmPreferences +19 -0
  142. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory attributes for.tmSnippet +16 -0
  143. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory build.tmSnippet +16 -0
  144. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory.tmSnippet +16 -0
  145. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory_define with class.tmSnippet +18 -0
  146. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory_define.tmSnippet +18 -0
  147. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory_next.tmSnippet +16 -0
  148. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory_sequence.tmSnippet +18 -0
  149. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_bad_value.tmSnippet +16 -0
  150. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_contains.tmSnippet +16 -0
  151. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_does_not_contain.tmSnippet +16 -0
  152. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_good_value.tmSnippet +16 -0
  153. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_same_elements.tmSnippet +16 -0
  154. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_save.tmSnippet +16 -0
  155. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_sent_email.tmSnippet +18 -0
  156. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_valid.tmSnippet +16 -0
  157. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/association.tmSnippet +17 -0
  158. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/attribute.tmSnippet +17 -0
  159. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/before_should block.tmSnippet +18 -0
  160. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/context block get.tmSnippet +22 -0
  161. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/context block post.tmSnippet +23 -0
  162. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/context block with setup.tmSnippet +25 -0
  163. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/setup.tmSnippet +18 -0
  164. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should block with before proc.tmSnippet +18 -0
  165. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should block.tmSnippet +18 -0
  166. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_allow_values_for.tmSnippet +16 -0
  167. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_assign_to.tmSnippet +16 -0
  168. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_be_restful denied.tmSnippet +20 -0
  169. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_be_restful.tmSnippet +20 -0
  170. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_belong_to.tmSnippet +16 -0
  171. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_change by.tmSnippet +16 -0
  172. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_change from to.tmSnippet +16 -0
  173. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_ensure_length_at_least.tmSnippet +16 -0
  174. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_ensure_length_in_range.tmSnippet +16 -0
  175. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_ensure_length_is.tmSnippet +16 -0
  176. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_ensure_value_in_range.tmSnippet +16 -0
  177. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_eventually.tmSnippet +18 -0
  178. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_filter_params.tmSnippet +16 -0
  179. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_and_belong_to_many.tmSnippet +16 -0
  180. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_class_methods.tmSnippet +16 -0
  181. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_db_column.tmSnippet +16 -0
  182. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_db_columns.tmSnippet +16 -0
  183. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_index.tmSnippet +16 -0
  184. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_indices.tmSnippet +16 -0
  185. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_instance_methods.tmSnippet +16 -0
  186. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_many.tmSnippet +16 -0
  187. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_named_scope.tmSnippet +16 -0
  188. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_one.tmSnippet +16 -0
  189. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_readonly_attributes.tmSnippet +16 -0
  190. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_allow_mass_assignment_of.tmSnippet +16 -0
  191. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_allow_values_for.tmSnippet +16 -0
  192. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_assign_to.tmSnippet +16 -0
  193. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_change.tmSnippet +16 -0
  194. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_set_the_flash.tmSnippet +16 -0
  195. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_redirect_to.tmSnippet +16 -0
  196. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_render_a_form.tmSnippet +16 -0
  197. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_render_template.tmSnippet +16 -0
  198. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_render_with_layout.tmSnippet +16 -0
  199. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_render_without_layout.tmSnippet +16 -0
  200. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_respond_with.tmSnippet +16 -0
  201. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_respond_with_content_type.tmSnippet +16 -0
  202. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_return_from_session.tmSnippet +16 -0
  203. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_route.tmSnippet +16 -0
  204. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_set_the_flash_to.tmSnippet +16 -0
  205. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_acceptance_of.tmSnippet +16 -0
  206. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_numericality_of.tmSnippet +16 -0
  207. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_presence_of.tmSnippet +16 -0
  208. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_uniqueness_of with scope.tmSnippet +16 -0
  209. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_uniqueness_of.tmSnippet +16 -0
  210. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/catch_exception.rb +39 -0
  211. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/run_script.rb +104 -0
  212. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/stdin_dialog.rb +14 -0
  213. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/test.rb +17 -0
  214. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/todo.txt +13 -0
  215. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/bin/yaml_to_shoulda.rb +25 -0
  216. data/vendor/TextMate/Ruby Shoulda.tmbundle/Syntaxes/Ruby on Rails (Shoulda).tmLanguage +166 -0
  217. data/vendor/TextMate/Ruby Shoulda.tmbundle/info.plist +304 -0
  218. data/vendor/TextMate/Zena.tmbundle/Commands/Run all yaml tests.tmCommand +37 -0
  219. data/vendor/TextMate/Zena.tmbundle/Commands/Run focused yaml test.tmCommand +52 -0
  220. data/vendor/TextMate/Zena.tmbundle/Support/RubyMate/catch_exception.rb +39 -0
  221. data/vendor/TextMate/Zena.tmbundle/Support/RubyMate/run_script.rb +118 -0
  222. data/vendor/TextMate/Zena.tmbundle/Support/RubyMate/stdin_dialog.rb +14 -0
  223. data/vendor/TextMate/Zena.tmbundle/info.plist +17 -0
  224. data/vendor/plugins/selenium-on-rails/CHANGELOG +125 -0
  225. data/vendor/plugins/selenium-on-rails/LICENSE-2.0.txt +202 -0
  226. data/vendor/plugins/selenium-on-rails/README.md +202 -0
  227. data/vendor/plugins/selenium-on-rails/Rakefile +38 -0
  228. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumController.html +265 -0
  229. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumHelper.html +148 -0
  230. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/FixtureLoader.html +231 -0
  231. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/PartialsSupport.html +195 -0
  232. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Paths.html +295 -0
  233. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/RSelenese.html +219 -0
  234. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Renderer.html +156 -0
  235. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Selenese.html +179 -0
  236. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/SuiteRenderer.html +223 -0
  237. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilder.html +441 -0
  238. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderAccessors.html +3098 -0
  239. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderActions.html +2080 -0
  240. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderUserAccessors.html +116 -0
  241. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderUserActions.html +116 -0
  242. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails.html +126 -0
  243. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRailsConfig.html +150 -0
  244. data/vendor/plugins/selenium-on-rails/doc/files/CHANGELOG.html +422 -0
  245. data/vendor/plugins/selenium-on-rails/doc/files/README.html +321 -0
  246. data/vendor/plugins/selenium-on-rails/doc/files/lib/controllers/selenium_controller_rb.html +108 -0
  247. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_helper_rb.html +101 -0
  248. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/acceptance_test_runner_rb.html +222 -0
  249. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/fixture_loader_rb.html +109 -0
  250. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/partials_support_rb.html +111 -0
  251. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/paths_rb.html +101 -0
  252. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/renderer_rb.html +101 -0
  253. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/rselenese_rb.html +118 -0
  254. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/selenese_rb.html +101 -0
  255. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/suite_renderer_rb.html +101 -0
  256. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_accessors_rb.html +114 -0
  257. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_actions_rb.html +113 -0
  258. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_rb.html +120 -0
  259. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails_config_rb.html +108 -0
  260. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails_rb.html +115 -0
  261. data/vendor/plugins/selenium-on-rails/doc/fr_class_index.html +42 -0
  262. data/vendor/plugins/selenium-on-rails/doc/fr_file_index.html +43 -0
  263. data/vendor/plugins/selenium-on-rails/doc/fr_method_index.html +182 -0
  264. data/vendor/plugins/selenium-on-rails/doc/index.html +24 -0
  265. data/vendor/plugins/selenium-on-rails/doc/rdoc-style.css +208 -0
  266. data/vendor/plugins/selenium-on-rails/generators/selenium/USAGE +19 -0
  267. data/vendor/plugins/selenium-on-rails/generators/selenium/selenium_generator.rb +50 -0
  268. data/vendor/plugins/selenium-on-rails/generators/selenium/templates/rhtml.rhtml +16 -0
  269. data/vendor/plugins/selenium-on-rails/generators/selenium/templates/rselenese.rhtml +14 -0
  270. data/vendor/plugins/selenium-on-rails/generators/selenium/templates/selenese.rhtml +11 -0
  271. data/vendor/plugins/selenium-on-rails/init.rb +15 -0
  272. data/vendor/plugins/selenium-on-rails/lib/controllers/selenium_controller.rb +122 -0
  273. data/vendor/plugins/selenium-on-rails/lib/controllers/switch_environment_controller.rb +16 -0
  274. data/vendor/plugins/selenium-on-rails/lib/selenium_helper.rb +8 -0
  275. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/acceptance_test_runner.rb +215 -0
  276. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/fixture_loader.rb +57 -0
  277. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/partials_support.rb +36 -0
  278. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/paths.rb +61 -0
  279. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/renderer.rb +20 -0
  280. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/rselenese.rb +44 -0
  281. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/selenese.rb +87 -0
  282. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/suite_renderer.rb +56 -0
  283. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder.rb +116 -0
  284. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_accessors.rb +1002 -0
  285. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_actions.rb +514 -0
  286. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_user_accessors.rb.example +91 -0
  287. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_user_actions.rb.example +24 -0
  288. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails.rb +11 -0
  289. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails_config.rb +30 -0
  290. data/vendor/plugins/selenium-on-rails/lib/views/layouts/layout.rhtml +18 -0
  291. data/vendor/plugins/selenium-on-rails/lib/views/record.rhtml +5 -0
  292. data/vendor/plugins/selenium-on-rails/lib/views/selenium_helper.rb +9 -0
  293. data/vendor/plugins/selenium-on-rails/lib/views/setup.rhtml +67 -0
  294. data/vendor/plugins/selenium-on-rails/lib/views/test_suite.rhtml +26 -0
  295. data/vendor/plugins/selenium-on-rails/routes.rb +24 -0
  296. data/vendor/plugins/selenium-on-rails/selenium-core/Blank.html +7 -0
  297. data/vendor/plugins/selenium-on-rails/selenium-core/InjectedRemoteRunner.html +8 -0
  298. data/vendor/plugins/selenium-on-rails/selenium-core/RemoteRunner.html +110 -0
  299. data/vendor/plugins/selenium-on-rails/selenium-core/SeleniumLog.html +109 -0
  300. data/vendor/plugins/selenium-on-rails/selenium-core/TestPrompt.html +145 -0
  301. data/vendor/plugins/selenium-on-rails/selenium-core/TestRunner-splash.html +55 -0
  302. data/vendor/plugins/selenium-on-rails/selenium-core/TestRunner.hta +177 -0
  303. data/vendor/plugins/selenium-on-rails/selenium-core/TestRunner.html +177 -0
  304. data/vendor/plugins/selenium-on-rails/selenium-core/domviewer/butmin.gif +0 -0
  305. data/vendor/plugins/selenium-on-rails/selenium-core/domviewer/butplus.gif +0 -0
  306. data/vendor/plugins/selenium-on-rails/selenium-core/domviewer/domviewer.css +298 -0
  307. data/vendor/plugins/selenium-on-rails/selenium-core/domviewer/domviewer.html +16 -0
  308. data/vendor/plugins/selenium-on-rails/selenium-core/domviewer/selenium-domviewer.js +205 -0
  309. data/vendor/plugins/selenium-on-rails/selenium-core/icons/all.png +0 -0
  310. data/vendor/plugins/selenium-on-rails/selenium-core/icons/continue.png +0 -0
  311. data/vendor/plugins/selenium-on-rails/selenium-core/icons/continue_disabled.png +0 -0
  312. data/vendor/plugins/selenium-on-rails/selenium-core/icons/pause.png +0 -0
  313. data/vendor/plugins/selenium-on-rails/selenium-core/icons/pause_disabled.png +0 -0
  314. data/vendor/plugins/selenium-on-rails/selenium-core/icons/selected.png +0 -0
  315. data/vendor/plugins/selenium-on-rails/selenium-core/icons/step.png +0 -0
  316. data/vendor/plugins/selenium-on-rails/selenium-core/icons/step_disabled.png +0 -0
  317. data/vendor/plugins/selenium-on-rails/selenium-core/iedoc-core.xml +1759 -0
  318. data/vendor/plugins/selenium-on-rails/selenium-core/iedoc.xml +1800 -0
  319. data/vendor/plugins/selenium-on-rails/selenium-core/lib/cssQuery/cssQuery-p.js +6 -0
  320. data/vendor/plugins/selenium-on-rails/selenium-core/lib/cssQuery/src/cssQuery-level2.js +142 -0
  321. data/vendor/plugins/selenium-on-rails/selenium-core/lib/cssQuery/src/cssQuery-level3.js +150 -0
  322. data/vendor/plugins/selenium-on-rails/selenium-core/lib/cssQuery/src/cssQuery-standard.js +53 -0
  323. data/vendor/plugins/selenium-on-rails/selenium-core/lib/cssQuery/src/cssQuery.js +356 -0
  324. data/vendor/plugins/selenium-on-rails/selenium-core/lib/prototype.js +2006 -0
  325. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/builder.js +101 -0
  326. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/controls.js +815 -0
  327. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/dragdrop.js +915 -0
  328. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/effects.js +958 -0
  329. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/scriptaculous.js +47 -0
  330. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/slider.js +283 -0
  331. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/unittest.js +383 -0
  332. data/vendor/plugins/selenium-on-rails/selenium-core/lib/snapsie.js +91 -0
  333. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/find_matching_child.js +69 -0
  334. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/htmlutils.js +1616 -0
  335. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/injection.html +72 -0
  336. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-api.js +3184 -0
  337. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-browserbot.js +2300 -0
  338. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-browserdetect.js +153 -0
  339. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-commandhandlers.js +377 -0
  340. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-executionloop.js +175 -0
  341. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-logging.js +148 -0
  342. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-remoterunner.js +695 -0
  343. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-testrunner.js +1362 -0
  344. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-version.js +5 -0
  345. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/ui-doc.html +803 -0
  346. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/ui-element.js +1537 -0
  347. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/ui-map-sample.js +979 -0
  348. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/user-extensions.js +3 -0
  349. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/user-extensions.js.sample +75 -0
  350. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/xmlextras.js +153 -0
  351. data/vendor/plugins/selenium-on-rails/selenium-core/selenium-logo.png +0 -0
  352. data/vendor/plugins/selenium-on-rails/selenium-core/selenium-test.css +43 -0
  353. data/vendor/plugins/selenium-on-rails/selenium-core/selenium.css +316 -0
  354. data/vendor/plugins/selenium-on-rails/selenium-core/xpath/dom.js +566 -0
  355. data/vendor/plugins/selenium-on-rails/selenium-core/xpath/javascript-xpath-0.1.11.js +2816 -0
  356. data/vendor/plugins/selenium-on-rails/selenium-core/xpath/util.js +549 -0
  357. data/vendor/plugins/selenium-on-rails/selenium-core/xpath/xmltoken.js +149 -0
  358. data/vendor/plugins/selenium-on-rails/selenium-core/xpath/xpath.js +2450 -0
  359. data/vendor/plugins/selenium-on-rails/tasks/test_acceptance.rake +8 -0
  360. data/vendor/plugins/selenium-on-rails/test/fixtures/config.yml +37 -0
  361. data/vendor/plugins/selenium-on-rails/test/fixtures/selenium.yml +27 -0
  362. data/vendor/plugins/selenium-on-rails/test/paths_test.rb +72 -0
  363. data/vendor/plugins/selenium-on-rails/test/renderer_test.rb +157 -0
  364. data/vendor/plugins/selenium-on-rails/test/rselenese_test.rb +708 -0
  365. data/vendor/plugins/selenium-on-rails/test/selenese_test.rb +242 -0
  366. data/vendor/plugins/selenium-on-rails/test/selenium_controller_test.rb +67 -0
  367. data/vendor/plugins/selenium-on-rails/test/selenium_on_rails_config_test.rb +43 -0
  368. data/vendor/plugins/selenium-on-rails/test/selenium_support_test.rb +35 -0
  369. data/vendor/plugins/selenium-on-rails/test/setup_test.rb +31 -0
  370. data/vendor/plugins/selenium-on-rails/test/suite_renderer_test.rb +109 -0
  371. data/vendor/plugins/selenium-on-rails/test/switch_environment_controller_test.rb +17 -0
  372. data/vendor/plugins/selenium-on-rails/test/test_builder_functions_authortest.rb +51 -0
  373. data/vendor/plugins/selenium-on-rails/test/test_helper.rb +101 -0
  374. data/vendor/plugins/selenium-on-rails/test_data/_partial.rsel +1 -0
  375. data/vendor/plugins/selenium-on-rails/test_data/own_layout.html +12 -0
  376. data/vendor/plugins/selenium-on-rails/test_data/partials/_html.html +6 -0
  377. data/vendor/plugins/selenium-on-rails/test_data/partials/_nesting.rsel +2 -0
  378. data/vendor/plugins/selenium-on-rails/test_data/partials/_rhtml.rhtml +6 -0
  379. data/vendor/plugins/selenium-on-rails/test_data/partials/_rsel.rsel +1 -0
  380. data/vendor/plugins/selenium-on-rails/test_data/partials/_sel.sel +5 -0
  381. data/vendor/plugins/selenium-on-rails/test_data/partials/all_partials.rsel +5 -0
  382. data/vendor/plugins/selenium-on-rails/test_data/rhtml.rhtml +7 -0
  383. data/vendor/plugins/selenium-on-rails/test_data/rselenese.rsel +8 -0
  384. data/vendor/plugins/selenium-on-rails/test_data/selenese.sel +7 -0
  385. data/vendor/plugins/selenium-on-rails/test_data/suite_one/subsuite/suite_one_subsuite_testcase.sel +1 -0
  386. data/vendor/plugins/selenium-on-rails/test_data/suite_one/suite_one_testcase1.sel +1 -0
  387. data/vendor/plugins/selenium-on-rails/test_data/suite_one/suite_one_testcase2.sel +1 -0
  388. data/vendor/plugins/selenium-on-rails/test_data/suite_two/suite_two_testcase.sel +1 -0
  389. data/zena.gemspec +290 -7
  390. metadata +346 -52
  391. data/app/views/nodes/_table.rhtml +0 -14
  392. data/lib/zena/use/grid.rb +0 -154
@@ -0,0 +1,979 @@
1
+ // sample UI element mapping definition. This is for http://alistapart.com/,
2
+ // a particularly well structured site on web design principles.
3
+
4
+
5
+
6
+ // in general, the map should capture structural aspects of the system, instead
7
+ // of "content". In other words, interactive elements / assertible elements
8
+ // that can be counted on to always exist should be defined here. Content -
9
+ // for example text or a link that appears in a blog entry - is always liable
10
+ // to change, and will not be fun to represent in this way. You probably don't
11
+ // want to be testing specific content anyway.
12
+
13
+ // create the UI mapping object. THIS IS THE MOST IMPORTANT PART - DON'T FORGET
14
+ // TO DO THIS! In order for it to come into play, a user extension must
15
+ // construct the map in this way.
16
+ var myMap = new UIMap();
17
+
18
+
19
+
20
+
21
+ // any values which may appear multiple times can be defined as variables here.
22
+ // For example, here we're enumerating a list of top level topics that will be
23
+ // used as default argument values for several UI elements. Check out how
24
+ // this variable is referenced further down.
25
+ var topics = [
26
+ 'Code',
27
+ 'Content',
28
+ 'Culture',
29
+ 'Design',
30
+ 'Process',
31
+ 'User Science'
32
+ ];
33
+
34
+ // map subtopics to their parent topics
35
+ var subtopics = {
36
+ 'Browsers': 'Code'
37
+ , 'CSS': 'Code'
38
+ , 'Flash': 'Code'
39
+ , 'HTML and XHTML': 'Code'
40
+ , 'Scripting': 'Code'
41
+ , 'Server Side': 'Code'
42
+ , 'XML': 'Code'
43
+ , 'Brand Arts': 'Content'
44
+ , 'Community': 'Content'
45
+ , 'Writing': 'Content'
46
+ , 'Industry': 'Culture'
47
+ , 'Politics and Money': 'Culture'
48
+ , 'State of the Web': 'Culture'
49
+ , 'Graphic Design': 'Design'
50
+ , 'User Interface Design': 'Design'
51
+ , 'Typography': 'Design'
52
+ , 'Layout': 'Design'
53
+ , 'Business': 'Process'
54
+ , 'Creativity': 'Process'
55
+ , 'Project Management and Workflow': 'Process'
56
+ , 'Accessibility': 'User Science'
57
+ , 'Information Architecture': 'User Science'
58
+ , 'Usability': 'User Science'
59
+ };
60
+
61
+
62
+
63
+ // define UI elements common for all pages. This regular expression does the
64
+ // trick. '^' is automatically prepended, and '$' is automatically postpended.
65
+ // Please note that because the regular expression is being represented as a
66
+ // string, all backslashes must be escaped with an additional backslash. Also
67
+ // note that the URL being matched will always have any trailing forward slash
68
+ // stripped.
69
+ myMap.addPageset({
70
+ name: 'allPages'
71
+ , description: 'all alistapart.com pages'
72
+ , pathRegexp: '.*'
73
+ });
74
+ myMap.addElement('allPages', {
75
+ name: 'masthead'
76
+ // the description should be short and to the point, usually no longer than
77
+ // a single line
78
+ , description: 'top level image link to site homepage'
79
+ // make sure the function returns the XPath ... it's easy to leave out the
80
+ // "return" statement by accident!
81
+ , locator: "xpath=//*[@id='masthead']/a/img"
82
+ , testcase1: {
83
+ xhtml: '<h1 id="masthead"><a><img expected-result="1" /></a></h1>'
84
+ }
85
+ });
86
+ myMap.addElement('allPages', {
87
+ // be VERY CAREFUL to include commas in the correct place. Missing commas
88
+ // and extra commas can cause lots of headaches when debugging map
89
+ // definition files!!!
90
+ name: 'current_issue'
91
+ , description: 'top level link to issue currently being browsed'
92
+ , locator: "//div[@id='ish']/a"
93
+ , testcase1: {
94
+ xhtml: '<div id="ish"><a expected-result="1"></a></div>'
95
+ }
96
+ });
97
+ myMap.addElement('allPages', {
98
+ name: 'section'
99
+ , description: 'top level link to articles section'
100
+ , args: [
101
+ {
102
+ name: 'section'
103
+ , description: 'the name of the section'
104
+ , defaultValues: [
105
+ 'articles'
106
+ , 'topics'
107
+ , 'about'
108
+ , 'contact'
109
+ , 'contribute'
110
+ , 'feed'
111
+ ]
112
+ }
113
+ ]
114
+ // getXPath has been deprecated by getLocator, but verify backward
115
+ // compatability here
116
+ , getXPath: function(args) {
117
+ return "//li[@id=" + args.section.quoteForXPath() + "]/a";
118
+ }
119
+ , testcase1: {
120
+ args: { section: 'feed' }
121
+ , xhtml: '<ul><li id="feed"><a expected-result="1" /></li></ul>'
122
+ }
123
+ });
124
+ myMap.addElement('allPages', {
125
+ name: 'search_box'
126
+ , description: 'site search input field'
127
+ // xpath has been deprecated by locator, but verify backward compatability
128
+ , xpath: "//input[@id='search']"
129
+ , testcase1: {
130
+ xhtml: '<input id="search" expected-result="1" />'
131
+ }
132
+ });
133
+ myMap.addElement('allPages', {
134
+ name: 'search_discussions'
135
+ , description: 'site search include discussions checkbox'
136
+ , locator: 'incdisc'
137
+ , testcase1: {
138
+ xhtml: '<input id="incdisc" expected-result="1" />'
139
+ }
140
+ });
141
+ myMap.addElement('allPages', {
142
+ name: 'search_submit'
143
+ , description: 'site search submission button'
144
+ , locator: 'submit'
145
+ , testcase1: {
146
+ xhtml: '<input id="submit" expected-result="1" />'
147
+ }
148
+ });
149
+ myMap.addElement('allPages', {
150
+ name: 'topics'
151
+ , description: 'sidebar links to topic categories'
152
+ , args: [
153
+ {
154
+ name: 'topic'
155
+ , description: 'the name of the topic'
156
+ , defaultValues: topics
157
+ }
158
+ ]
159
+ , getLocator: function(args) {
160
+ return "//div[@id='topiclist']/ul/li" +
161
+ "/a[text()=" + args.topic.quoteForXPath() + "]";
162
+ }
163
+ , testcase1: {
164
+ args: { topic: 'foo' }
165
+ , xhtml: '<div id="topiclist"><ul><li>'
166
+ + '<a expected-result="1">foo</a>'
167
+ + '</li></ul></div>'
168
+ }
169
+ });
170
+ myMap.addElement('allPages', {
171
+ name: 'copyright'
172
+ , description: 'footer link to copyright page'
173
+ , getLocator: function(args) { return "//span[@class='copyright']/a"; }
174
+ , testcase1: {
175
+ xhtml: '<span class="copyright"><a expected-result="1" /></span>'
176
+ }
177
+ });
178
+
179
+
180
+
181
+ // define UI elements for the homepage, i.e. "http://alistapart.com/", and
182
+ // magazine issue pages, i.e. "http://alistapart.com/issues/234".
183
+ myMap.addPageset({
184
+ name: 'issuePages'
185
+ , description: 'pages including magazine issues'
186
+ , pathRegexp: '(issues/.+)?'
187
+ });
188
+ myMap.addElement('issuePages', {
189
+ name: 'article'
190
+ , description: 'front or issue page link to article'
191
+ , args: [
192
+ {
193
+ name: 'index'
194
+ , description: 'the index of the article'
195
+ // an array of default values for the argument. A default
196
+ // value is one that is passed to the getXPath() method of
197
+ // the container UIElement object when trying to build an
198
+ // element locator.
199
+ //
200
+ // range() may be used to count easily. Remember though that
201
+ // the ending value does not include the right extreme; for
202
+ // example range(1, 5) counts from 1 to 4 only.
203
+ , defaultValues: range(1, 5)
204
+ }
205
+ ]
206
+ , getLocator: function(args) {
207
+ return "//div[@class='item'][" + args.index + "]/h4/a";
208
+ }
209
+ });
210
+ myMap.addElement('issuePages', {
211
+ name: 'author'
212
+ , description: 'article author link'
213
+ , args: [
214
+ {
215
+ name: 'index'
216
+ , description: 'the index of the author, by article'
217
+ , defaultValues: range(1, 5)
218
+ }
219
+ ]
220
+ , getLocator: function(args) {
221
+ return "//div[@class='item'][" + args.index + "]/h5/a";
222
+ }
223
+ });
224
+ myMap.addElement('issuePages', {
225
+ name: 'store'
226
+ , description: 'alistapart.com store link'
227
+ , locator: "//ul[@id='banners']/li/a[@title='ALA Store']/img"
228
+ });
229
+ myMap.addElement('issuePages', {
230
+ name: 'special_article'
231
+ , description: "editor's choice article link"
232
+ , locator: "//div[@id='choice']/h4/a"
233
+ });
234
+ myMap.addElement('issuePages', {
235
+ name: 'special_author'
236
+ , description: "author link of editor's choice article"
237
+ , locator: "//div[@id='choice']/h5/a"
238
+ });
239
+
240
+
241
+
242
+ // define UI elements for the articles page, i.e.
243
+ // "http://alistapart.com/articles"
244
+ myMap.addPageset({
245
+ name: 'articleListPages'
246
+ , description: 'page with article listings'
247
+ , paths: [ 'articles' ]
248
+ });
249
+ myMap.addElement('articleListPages', {
250
+ name: 'issue'
251
+ , description: 'link to issue'
252
+ , args: [
253
+ {
254
+ name: 'index'
255
+ , description: 'the index of the issue on the page'
256
+ , defaultValues: range(1, 10)
257
+ }
258
+ ]
259
+ , getLocator: function(args) {
260
+ return "//h2[@class='ishinfo'][" + args.index + ']/a';
261
+ }
262
+ , genericLocator: "//h2[@class='ishinfo']/a"
263
+ });
264
+ myMap.addElement('articleListPages', {
265
+ name: 'article'
266
+ , description: 'link to article, by issue and article number'
267
+ , args: [
268
+ {
269
+ name: 'issue_index'
270
+ , description: "the index of the article's issue on the page; "
271
+ + 'typically five per page'
272
+ , defaultValues: range(1, 6)
273
+ }
274
+ , {
275
+ name: 'article_index'
276
+ , description: 'the index of the article within the issue; '
277
+ + 'typically two per issue'
278
+ , defaultValues: range(1, 5)
279
+ }
280
+ ]
281
+ , getLocator: function(args) {
282
+ var xpath = "//h2[@class='ishinfo'][" + (args.issue_index || 1) + ']'
283
+ + "/following-sibling::div[@class='item']"
284
+ + '[' + (args.article_index || 1) + "]/h3[@class='title']/a";
285
+ return xpath;
286
+ }
287
+ , genericLocator: "//h2[@class='ishinfo']"
288
+ + "/following-sibling::div[@class='item']/h3[@class='title']/a"
289
+ });
290
+ myMap.addElement('articleListPages', {
291
+ name: 'author'
292
+ , description: 'article author link, by issue and article'
293
+ , args: [
294
+ {
295
+ name: 'issue_index'
296
+ , description: "the index of the article's issue on the page; \
297
+ typically five per page"
298
+ , defaultValues: range(1, 6)
299
+ }
300
+ , {
301
+ name: 'article_index'
302
+ , description: "the index of the article within the issue; \
303
+ typically two articles per issue"
304
+ , defaultValues: range(1, 3)
305
+ }
306
+ ]
307
+ // this XPath uses the "following-sibling" axis. The div elements for
308
+ // the articles in an issue are not children, but siblings of the h2
309
+ // element identifying the article.
310
+ , getLocator: function(args) {
311
+ var xpath = "//h2[@class='ishinfo'][" + (args.issue_index || 1) + ']'
312
+ + "/following-sibling::div[@class='item']"
313
+ + '[' + (args.article_index || 1) + "]/h4[@class='byline']/a";
314
+ return xpath;
315
+ }
316
+ , genericLocator: "//h2[@class='ishinfo']"
317
+ + "/following-sibling::div[@class='item']/h4[@class='byline']/a"
318
+ });
319
+ myMap.addElement('articleListPages', {
320
+ name: 'next_page'
321
+ , description: 'link to next page of articles (older)'
322
+ , locator: "//a[contains(text(),'Next page')]"
323
+ });
324
+ myMap.addElement('articleListPages', {
325
+ name: 'previous_page'
326
+ , description: 'link to previous page of articles (newer)'
327
+ , locator: "//a[contains(text(),'Previous page')]"
328
+ });
329
+
330
+
331
+
332
+ // define UI elements for specific article pages, i.e.
333
+ // "http://alistapart.com/articles/culturalprobe"
334
+ myMap.addPageset({
335
+ name: 'articlePages'
336
+ , description: 'pages for actual articles'
337
+ , pathRegexp: 'articles/.+'
338
+ });
339
+ myMap.addElement('articlePages', {
340
+ name: 'title'
341
+ , description: 'article title loop-link'
342
+ , locator: "//div[@id='content']/h1[@class='title']/a"
343
+ });
344
+ myMap.addElement('articlePages', {
345
+ name: 'author'
346
+ , description: 'article author link'
347
+ , locator: "//div[@id='content']/h3[@class='byline']/a"
348
+ });
349
+ myMap.addElement('articlePages', {
350
+ name: 'article_topics'
351
+ , description: 'links to topics under which article is published, before \
352
+ article content'
353
+ , args: [
354
+ {
355
+ name: 'topic'
356
+ , description: 'the name of the topic'
357
+ , defaultValues: keys(subtopics)
358
+ }
359
+ ]
360
+ , getLocator: function(args) {
361
+ return "//ul[@id='metastuff']/li/a"
362
+ + "[@title=" + args.topic.quoteForXPath() + "]";
363
+ }
364
+ });
365
+ myMap.addElement('articlePages', {
366
+ name: 'discuss'
367
+ , description: 'link to article discussion area, before article content'
368
+ , locator: "//ul[@id='metastuff']/li[@class='discuss']/p/a"
369
+ });
370
+ myMap.addElement('articlePages', {
371
+ name: 'related_topics'
372
+ , description: 'links to topics under which article is published, after \
373
+ article content'
374
+ , args: [
375
+ {
376
+ name: 'topic'
377
+ , description: 'the name of the topic'
378
+ , defaultValues: keys(subtopics)
379
+ }
380
+ ]
381
+ , getLocator: function(args) {
382
+ return "//div[@id='learnmore']/p/a"
383
+ + "[@title=" + args.topic.quoteForXPath() + "]";
384
+ }
385
+ });
386
+ myMap.addElement('articlePages', {
387
+ name: 'join_discussion'
388
+ , description: 'link to article discussion area, after article content'
389
+ , locator: "//div[@class='discuss']/p/a"
390
+ });
391
+
392
+
393
+
394
+ myMap.addPageset({
395
+ name: 'topicListingPages'
396
+ , description: 'top level listing of topics'
397
+ , paths: [ 'topics' ]
398
+ });
399
+ myMap.addElement('topicListingPages', {
400
+ name: 'topic'
401
+ , description: 'link to topic category'
402
+ , args: [
403
+ {
404
+ name: 'topic'
405
+ , description: 'the name of the topic'
406
+ , defaultValues: topics
407
+ }
408
+ ]
409
+ , getLocator: function(args) {
410
+ return "//div[@id='content']/h2/a"
411
+ + "[text()=" + args.topic.quoteForXPath() + "]";
412
+ }
413
+ });
414
+ myMap.addElement('topicListingPages', {
415
+ name: 'subtopic'
416
+ , description: 'link to subtopic category'
417
+ , args: [
418
+ {
419
+ name: 'subtopic'
420
+ , description: 'the name of the subtopic'
421
+ , defaultValues: keys(subtopics)
422
+ }
423
+ ]
424
+ , getLocator: function(args) {
425
+ return "//div[@id='content']" +
426
+ "/descendant::a[text()=" + args.subtopic.quoteForXPath() + "]";
427
+ }
428
+ });
429
+
430
+ // the following few subtopic page UI elements are very similar. Define UI
431
+ // elements for the code page, which is a subpage under topics, i.e.
432
+ // "http://alistapart.com/topics/code/"
433
+ myMap.addPageset({
434
+ name: 'subtopicListingPages'
435
+ , description: 'pages listing subtopics'
436
+ , pathPrefix: 'topics/'
437
+ , paths: [
438
+ 'code'
439
+ , 'content'
440
+ , 'culture'
441
+ , 'design'
442
+ , 'process'
443
+ , 'userscience'
444
+ ]
445
+ });
446
+ myMap.addElement('subtopicListingPages', {
447
+ name: 'subtopic'
448
+ , description: 'link to a subtopic category'
449
+ , args: [
450
+ {
451
+ name: 'subtopic'
452
+ , description: 'the name of the subtopic'
453
+ , defaultValues: keys(subtopics)
454
+ }
455
+ ]
456
+ , getLocator: function(args) {
457
+ return "//div[@id='content']/h2" +
458
+ "/a[text()=" + args.subtopic.quoteForXPath() + "]";
459
+ }
460
+ });
461
+
462
+
463
+
464
+ // subtopic articles page
465
+ myMap.addPageset({
466
+ name: 'subtopicArticleListingPages'
467
+ , description: 'pages listing the articles for a given subtopic'
468
+ , pathRegexp: 'topics/[^/]+/.+'
469
+ });
470
+ myMap.addElement('subtopicArticleListingPages', {
471
+ name: 'article'
472
+ , description: 'link to a subtopic article'
473
+ , args: [
474
+ {
475
+ name: 'index'
476
+ , description: 'the index of the article'
477
+ , defaultValues: range(1, 51) // the range seems unlimited ...
478
+ }
479
+ ]
480
+ , getLocator: function(args) {
481
+ return "//div[@id='content']/div[@class='item']"
482
+ + "[" + args.index + "]/h3/a";
483
+ }
484
+ , testcase1: {
485
+ args: { index: 2 }
486
+ , xhtml: '<div id="content"><div class="item" /><div class="item">'
487
+ + '<h3><a expected-result="1" /></h3></div></div>'
488
+ }
489
+ });
490
+ myMap.addElement('subtopicArticleListingPages', {
491
+ name: 'author'
492
+ , description: "link to a subtopic article author's page"
493
+ , args: [
494
+ {
495
+ name: 'article_index'
496
+ , description: 'the index of the authored article'
497
+ , defaultValues: range(1, 51)
498
+ }
499
+ , {
500
+ name: 'author_index'
501
+ , description: 'the index of the author when there are multiple'
502
+ , defaultValues: range(1, 4)
503
+ }
504
+ ]
505
+ , getLocator: function(args) {
506
+ return "//div[@id='content']/div[@class='item'][" +
507
+ args.article_index + "]/h4/a[" +
508
+ (args.author_index ? args.author_index : '1') + ']';
509
+ }
510
+ });
511
+ myMap.addElement('subtopicArticleListingPages', {
512
+ name: 'issue'
513
+ , description: 'link to issue a subtopic article appears in'
514
+ , args: [
515
+ {
516
+ name: 'index'
517
+ , description: 'the index of the subtopic article'
518
+ , defaultValues: range(1, 51)
519
+ }
520
+ ]
521
+ , getLocator: function(args) {
522
+ return "//div[@id='content']/div[@class='item']"
523
+ + "[" + args.index + "]/h5/a";
524
+ }
525
+ });
526
+
527
+
528
+
529
+ myMap.addPageset({
530
+ name: 'aboutPages'
531
+ , description: 'the website about page'
532
+ , paths: [ 'about' ]
533
+ });
534
+ myMap.addElement('aboutPages', {
535
+ name: 'crew'
536
+ , description: 'link to site crew member bio or personal website'
537
+ , args: [
538
+ {
539
+ name: 'role'
540
+ , description: 'the role of the crew member'
541
+ , defaultValues: [
542
+ 'ALA Crew'
543
+ , 'Support'
544
+ , 'Emeritus'
545
+ ]
546
+ }
547
+ , {
548
+ name: 'role_index'
549
+ , description: 'the index of the member within the role'
550
+ , defaultValues: range(1, 20)
551
+ }
552
+ , {
553
+ name: 'member_index'
554
+ , description: 'the index of the member within the role title'
555
+ , defaultValues: range(1, 5)
556
+ }
557
+ ]
558
+ , getLocator: function(args) {
559
+ // the first role is kind of funky, and requires a conditional to
560
+ // build the XPath correctly. Its header looks like this:
561
+ //
562
+ // <h3>
563
+ // <span class="caps">ALA 4</span>.0 <span class="caps">CREW</span>
564
+ // </h3>
565
+ //
566
+ // This kind of complexity is a little daunting, but you can see
567
+ // how the format can handle it relatively easily and concisely.
568
+ if (args.role == 'ALA Crew') {
569
+ var selector = "descendant::text()='CREW'";
570
+ }
571
+ else {
572
+ var selector = "text()=" + args.role.quoteForXPath();
573
+ }
574
+ var xpath =
575
+ "//div[@id='secondary']/h3[" + selector + ']' +
576
+ "/following-sibling::dl/dt[" + (args.role_index || 1) + ']' +
577
+ '/a[' + (args.member_index || '1') + ']';
578
+ return xpath;
579
+ }
580
+ });
581
+
582
+
583
+
584
+ myMap.addPageset({
585
+ name: 'searchResultsPages'
586
+ , description: 'pages listing search results'
587
+ , paths: [ 'search' ]
588
+ });
589
+ myMap.addElement('searchResultsPages', {
590
+ name: 'result_link'
591
+ , description: 'search result link'
592
+ , args: [
593
+ {
594
+ name: 'index'
595
+ , description: 'the index of the search result'
596
+ , defaultValues: range(1, 11)
597
+ }
598
+ ]
599
+ , getLocator: function(args) {
600
+ return "//div[@id='content']/ul[" + args.index + ']/li/h3/a';
601
+ }
602
+ });
603
+ myMap.addElement('searchResultsPages', {
604
+ name: 'more_results_link'
605
+ , description: 'next or previous results link at top or bottom of page'
606
+ , args: [
607
+ {
608
+ name: 'direction'
609
+ , description: 'next or previous results page'
610
+ // demonstrate a method which acquires default values from the
611
+ // document object. Such default values may contain EITHER commas
612
+ // OR equals signs, but NOT BOTH.
613
+ , getDefaultValues: function(inDocument) {
614
+ var defaultValues = [];
615
+ var divs = inDocument.getElementsByTagName('div');
616
+ for (var i = 0; i < divs.length; ++i) {
617
+ if (divs[i].className == 'pages') {
618
+ break;
619
+ }
620
+ }
621
+ var links = divs[i].getElementsByTagName('a');
622
+ for (i = 0; i < links.length; ++i) {
623
+ defaultValues.push(links[i].innerHTML
624
+ .replace(/^\xab\s*/, "")
625
+ .replace(/\s*\bb$/, "")
626
+ .replace(/\s*\d+$/, ""));
627
+ }
628
+ return defaultValues;
629
+ }
630
+ }
631
+ , {
632
+ name: 'position'
633
+ , description: 'position of the link'
634
+ , defaultValues: ['top', 'bottom']
635
+ }
636
+ ]
637
+ , getLocator: function(args) {
638
+ return "//div[@id='content']/div[@class='pages']["
639
+ + (args.position == 'top' ? '1' : '2') + ']'
640
+ + "/a[contains(text(), "
641
+ + (args.direction ? args.direction.quoteForXPath() : undefined)
642
+ + ")]";
643
+ }
644
+ });
645
+
646
+
647
+
648
+ myMap.addPageset({
649
+ name: 'commentsPages'
650
+ , description: 'pages listing comments made to an article'
651
+ , pathRegexp: 'comments/.+'
652
+ });
653
+ myMap.addElement('commentsPages', {
654
+ name: 'article_link'
655
+ , description: 'link back to the original article'
656
+ , locator: "//div[@id='content']/h1[@class='title']/a"
657
+ });
658
+ myMap.addElement('commentsPages', {
659
+ name: 'comment_link'
660
+ , description: 'same-page link to comment'
661
+ , args: [
662
+ {
663
+ name: 'index'
664
+ , description: 'the index of the comment'
665
+ , defaultValues: range(1, 11)
666
+ }
667
+ ]
668
+ , getLocator: function(args) {
669
+ return "//div[@class='content']/div[contains(@class, 'comment')]" +
670
+ '[' + args.index + ']/h4/a[2]';
671
+ }
672
+ });
673
+ myMap.addElement('commentsPages', {
674
+ name: 'paging_link'
675
+ , description: 'links to more pages of comments'
676
+ , args: [
677
+ {
678
+ name: 'dest'
679
+ , description: 'the destination page'
680
+ , defaultValues: ['next', 'prev'].concat(range(1, 16))
681
+ }
682
+ , {
683
+ name: 'position'
684
+ , description: 'position of the link'
685
+ , defaultValues: ['top', 'bottom']
686
+ }
687
+ ]
688
+ , getLocator: function(args) {
689
+ var dest = args.dest;
690
+ var xpath = "//div[@id='content']/div[@class='pages']" +
691
+ '[' + (args.position == 'top' ? '1' : '2') + ']/p';
692
+ if (dest == 'next' || dest == 'prev') {
693
+ xpath += "/a[contains(text(), " + dest.quoteForXPath() + ")]";
694
+ }
695
+ else {
696
+ xpath += "/a[text()=" + dest.quoteForXPath() + "]";
697
+ }
698
+ return xpath;
699
+ }
700
+ });
701
+
702
+
703
+
704
+ myMap.addPageset({
705
+ name: 'authorPages'
706
+ , description: 'personal pages for each author'
707
+ , pathRegexp: 'authors/[a-z]/.+'
708
+ });
709
+ myMap.addElement('authorPages', {
710
+ name: 'article'
711
+ , description: "link to article written by this author.\n"
712
+ + 'This description has a line break.'
713
+ , args: [
714
+ {
715
+ name: 'index'
716
+ , description: 'index of the article on the page'
717
+ , defaultValues: range(1, 11)
718
+ }
719
+ ]
720
+ , getLocator: function(args) {
721
+ var index = args.index;
722
+ // try out the CSS locator!
723
+ //return "//h4[@class='title'][" + index + "]/a";
724
+ return 'css=h4.title:nth-child(' + index + ') > a';
725
+ }
726
+ , testcase1: {
727
+ args: { index: '2' }
728
+ , xhtml: '<h4 class="title" /><h4 class="title">'
729
+ + '<a expected-result="1" /></h4>'
730
+ }
731
+ });
732
+
733
+
734
+
735
+ // test the offset locator. Something like the following can be recorded:
736
+ // ui=qaPages::content()//a[contains(text(),'May I quote from your articles?')]
737
+ myMap.addPageset({
738
+ name: 'qaPages'
739
+ , description: 'question and answer pages'
740
+ , pathRegexp: 'qa'
741
+ });
742
+ myMap.addElement('qaPages', {
743
+ name: 'content'
744
+ , description: 'the content pane containing the q&a entries'
745
+ , locator: "//div[@id='content' and "
746
+ + "child::h1[text()='Questions and Answers']]"
747
+ , getOffsetLocator: UIElement.defaultOffsetLocatorStrategy
748
+ });
749
+ myMap.addElement('qaPages', {
750
+ name: 'last_updated'
751
+ , description: 'displays the last update date'
752
+ // demonstrate calling getLocator() for another UI element within a
753
+ // getLocator(). The former must have already been added to the map. And
754
+ // obviously, you can't randomly combine different locator types!
755
+ , locator: myMap.getUIElement('qaPages', 'content').getLocator() + '/p/em'
756
+ });
757
+
758
+
759
+
760
+ //******************************************************************************
761
+
762
+ var myRollupManager = new RollupManager();
763
+
764
+ // though the description element is required, its content is free form. You
765
+ // might want to create a documentation policy as given below, where the pre-
766
+ // and post-conditions of the rollup are spelled out.
767
+ //
768
+ // To take advantage of a "heredoc" like syntax for longer descriptions,
769
+ // add a backslash to the end of the current line and continue the string on
770
+ // the next line.
771
+ myRollupManager.addRollupRule({
772
+ name: 'navigate_to_subtopic_article_listing'
773
+ , description: 'drill down to the listing of articles for a given subtopic \
774
+ from the section menu, then the topic itself.'
775
+ , pre: 'current page contains the section menu (most pages should)'
776
+ , post: 'navigated to the page listing all articles for a given subtopic'
777
+ , args: [
778
+ {
779
+ name: 'subtopic'
780
+ , description: 'the subtopic whose article listing to navigate to'
781
+ , exampleValues: keys(subtopics)
782
+ }
783
+ ]
784
+ , commandMatchers: [
785
+ {
786
+ command: 'clickAndWait'
787
+ , target: 'ui=allPages::section\\(section=topics\\)'
788
+ // must escape parentheses in the the above target, since the
789
+ // string is being used as a regular expression. Again, backslashes
790
+ // in strings must be escaped too.
791
+ }
792
+ , {
793
+ command: 'clickAndWait'
794
+ , target: 'ui=topicListingPages::topic\\(.+'
795
+ }
796
+ , {
797
+ command: 'clickAndWait'
798
+ , target: 'ui=subtopicListingPages::subtopic\\(.+'
799
+ , updateArgs: function(command, args) {
800
+ // don't bother stripping the "ui=" prefix from the locator
801
+ // here; we're just using UISpecifier to parse the args out
802
+ var uiSpecifier = new UISpecifier(command.target);
803
+ args.subtopic = uiSpecifier.args.subtopic;
804
+ return args;
805
+ }
806
+ }
807
+ ]
808
+ , getExpandedCommands: function(args) {
809
+ var commands = [];
810
+ var topic = subtopics[args.subtopic];
811
+ var subtopic = args.subtopic;
812
+ commands.push({
813
+ command: 'clickAndWait'
814
+ , target: 'ui=allPages::section(section=topics)'
815
+ });
816
+ commands.push({
817
+ command: 'clickAndWait'
818
+ , target: 'ui=topicListingPages::topic(topic=' + topic + ')'
819
+ });
820
+ commands.push({
821
+ command: 'clickAndWait'
822
+ , target: 'ui=subtopicListingPages::subtopic(subtopic=' + subtopic
823
+ + ')'
824
+ });
825
+ commands.push({
826
+ command: 'verifyLocation'
827
+ , target: 'regexp:.+/topics/.+/.+'
828
+ });
829
+ return commands;
830
+ }
831
+ });
832
+
833
+
834
+
835
+ myRollupManager.addRollupRule({
836
+ name: 'replace_click_with_clickAndWait'
837
+ , description: 'replaces commands where a click was detected with \
838
+ clickAndWait instead'
839
+ , alternateCommand: 'clickAndWait'
840
+ , commandMatchers: [
841
+ {
842
+ command: 'click'
843
+ , target: 'ui=subtopicArticleListingPages::article\\(.+'
844
+ }
845
+ ]
846
+ , expandedCommands: []
847
+ });
848
+
849
+
850
+
851
+ myRollupManager.addRollupRule({
852
+ name: 'navigate_to_subtopic_article'
853
+ , description: 'navigate to an article listed under a subtopic.'
854
+ , pre: 'current page contains the section menu (most pages should)'
855
+ , post: 'navigated to an article page'
856
+ , args: [
857
+ {
858
+ name: 'subtopic'
859
+ , description: 'the subtopic whose article listing to navigate to'
860
+ , exampleValues: keys(subtopics)
861
+ }
862
+ , {
863
+ name: 'index'
864
+ , description: 'the index of the article in the listing'
865
+ , exampleValues: range(1, 11)
866
+ }
867
+ ]
868
+ , commandMatchers: [
869
+ {
870
+ command: 'rollup'
871
+ , target: 'navigate_to_subtopic_article_listing'
872
+ , value: 'subtopic\\s*=.+'
873
+ , updateArgs: function(command, args) {
874
+ var args1 = parse_kwargs(command.value);
875
+ args.subtopic = args1.subtopic;
876
+ return args;
877
+ }
878
+ }
879
+ , {
880
+ command: 'clickAndWait'
881
+ , target: 'ui=subtopicArticleListingPages::article\\(.+'
882
+ , updateArgs: function(command, args) {
883
+ var uiSpecifier = new UISpecifier(command.target);
884
+ args.index = uiSpecifier.args.index;
885
+ return args;
886
+ }
887
+ }
888
+ ]
889
+ /*
890
+ // this is pretty much equivalent to the commandMatchers immediately above.
891
+ // Seems more verbose and less expressive, doesn't it? But sometimes you
892
+ // might prefer the flexibility of a function.
893
+ , getRollup: function(commands) {
894
+ if (commands.length >= 2) {
895
+ command1 = commands[0];
896
+ command2 = commands[1];
897
+ var args1 = parse_kwargs(command1.value);
898
+ try {
899
+ var uiSpecifier = new UISpecifier(command2.target
900
+ .replace(/^ui=/, ''));
901
+ }
902
+ catch (e) {
903
+ return false;
904
+ }
905
+ if (command1.command == 'rollup' &&
906
+ command1.target == 'navigate_to_subtopic_article_listing' &&
907
+ args1.subtopic &&
908
+ command2.command == 'clickAndWait' &&
909
+ uiSpecifier.pagesetName == 'subtopicArticleListingPages' &&
910
+ uiSpecifier.elementName == 'article') {
911
+ var args = {
912
+ subtopic: args1.subtopic
913
+ , index: uiSpecifier.args.index
914
+ };
915
+ return {
916
+ command: 'rollup'
917
+ , target: this.name
918
+ , value: to_kwargs(args)
919
+ , replacementIndexes: [ 0, 1 ]
920
+ };
921
+ }
922
+ }
923
+ return false;
924
+ }
925
+ */
926
+ , getExpandedCommands: function(args) {
927
+ var commands = [];
928
+ commands.push({
929
+ command: 'rollup'
930
+ , target: 'navigate_to_subtopic_article_listing'
931
+ , value: to_kwargs({ subtopic: args.subtopic })
932
+ });
933
+ var uiSpecifier = new UISpecifier(
934
+ 'subtopicArticleListingPages'
935
+ , 'article'
936
+ , { index: args.index });
937
+ commands.push({
938
+ command: 'clickAndWait'
939
+ , target: 'ui=' + uiSpecifier.toString()
940
+ });
941
+ commands.push({
942
+ command: 'verifyLocation'
943
+ , target: 'regexp:.+/articles/.+'
944
+ });
945
+ return commands;
946
+ }
947
+ });
948
+
949
+
950
+
951
+
952
+
953
+
954
+
955
+
956
+
957
+
958
+
959
+
960
+
961
+
962
+
963
+
964
+
965
+
966
+
967
+
968
+
969
+
970
+
971
+
972
+
973
+
974
+
975
+
976
+
977
+
978
+
979
+