citronella 0.0.3 → 0.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66f8d6b39216fe199746a210493b62325bf34f0e02747978a7fe43481cd0441e
4
- data.tar.gz: 321d811f98c49faabc0b9d168d6ecb793a284d6dd794ba99cdcfe22c41d919c4
3
+ metadata.gz: 9020c7d6488bbbd5e67e35a1c939a2af5296736bbe99c45a6d8194ff347676aa
4
+ data.tar.gz: 1fb3f63ea173a9c6ca720bfa945037bb8f6a595922929ee91972751e82d0239d
5
5
  SHA512:
6
- metadata.gz: 34e52701c765f24d210bc22b84cad46bf49821852c1c61815a3885da22190bb3b3fb221f371486a8c8e0ee428e7aaaba33c1c5de1735d3a7003b4de2e3e457b6
7
- data.tar.gz: 10fd8a98f877d3a1bbb71fa1de30c2292c58422feeec31cfb6a426e9f96294cff11966df5284f47b0bb839e107e7d0dff95ffea2f932c3a19bc6ae5b47e49ae2
6
+ metadata.gz: 9c52cdaeca3173c7d224f2d5f390b83d8b5e9835937bf1af3fa2e8a9ad05c03a1f16f02357b7a85f8052f36c598763a8b9a1551a8606964b78f53dee4bbd1194
7
+ data.tar.gz: cfd8f9876408c022b04eab006ac24793473ee5a946bba6963446e2d7a66736e917def508b0528718ae7f6003b40da63dd43be16653d60128798a81aea3068165
data/README.md CHANGED
@@ -1,6 +1,163 @@
1
1
  # Citronella
2
2
 
3
- Webdriver extension with a page object wrapper.
3
+ [![Gem Version](https://badge.fury.io/rb/citronella.png)](http://badge.fury.io/rb/citronella)
4
+
5
+ webdriver extension with a page object wrapper.
6
+
7
+ ## Example Tests
8
+ ```ruby
9
+ require 'test/unit'
10
+ require "selenium-webdriver"
11
+ require 'citronella'
12
+ require_relative '../pages/contents_page'
13
+
14
+
15
+ class NavigationTest < Test::Unit::TestCase
16
+ def setup
17
+ options = Selenium::WebDriver::Chrome::Options.new
18
+ driver = Selenium::WebDriver.for :chrome, options: options
19
+ @web = Citronella::Web::WebPage.new(driver)
20
+ end
21
+
22
+ def teardown
23
+ @web.driver.quit
24
+ end
25
+
26
+ def test_navigation
27
+ @web.page_object(ContentsPage.new.home_page, url=true)
28
+ @web.page.releases_button.click
29
+ assert_includes(@web.driver.title, 'Releases')
30
+
31
+ @web.page.gems_button.click
32
+ assert_includes(@web.driver.title, 'Gem')
33
+
34
+ @web.page.sign_in_button.click
35
+ assert_includes(@web.driver.title, 'Sign in')
36
+
37
+ @web.page.sign_up_button.click
38
+ assert_includes(@web.driver.title, 'Sign up')
39
+
40
+ @web.page.guides_button.click
41
+ assert_includes(@web.driver.title, 'Guides')
42
+
43
+ @web.page.blog_button.click
44
+ assert_includes(@web.driver.title, 'Blog')
45
+ end
46
+ end
47
+ ```
48
+ Even though this module is mainly designed for the page object model, it can also be used without it for quick prototyping or mockups, etc.
49
+ ```ruby
50
+ require 'test/unit'
51
+ require "selenium-webdriver"
52
+ require 'citronella'
53
+
54
+
55
+ class PackageSearchTest < Test::Unit::TestCase
56
+ def setup
57
+ options = Selenium::WebDriver::Chrome::Options.new
58
+ driver = Selenium::WebDriver.for :chrome, options: options
59
+ @web = Citronella::Web::WebPage.new(driver)
60
+ end
61
+
62
+ def teardown
63
+ @web.driver.quit
64
+ end
65
+
66
+ def test_search_package
67
+ @web.driver.navigate.to "https://rubygems.org/"
68
+ @web.locate(id: 'home_query').get_element.send_keys('citronella')
69
+ @web.locate(class: 'home__search__icon').get_element.click
70
+ assert(@web.locate(class: 'gems__gem__name').get_element.text, 'citronella')
71
+ end
72
+ end
73
+ ```
74
+ ___
75
+ ## Install Package
76
+
77
+ ```bash
78
+ gem install citronella
79
+ ```
80
+
81
+ ___
82
+ ## Documentation
83
+
84
+ There are only three modules imported in this package:
85
+
86
+ * The first module is for the tests.
87
+
88
+ ```ruby
89
+ require 'test/unit'
90
+ require "selenium-webdriver"
91
+ require 'citronella'
92
+
93
+ class NavigationTest < Test::Unit::TestCase
94
+ def setup
95
+ options = Selenium::WebDriver::Chrome::Options.new
96
+ driver = Selenium::WebDriver.for :chrome, options: options
97
+ @web = Citronella::Web::WebPage.new(driver)
98
+ end
99
+
100
+ def teardown
101
+ @web.driver.quit
102
+ end
103
+ end
104
+ ```
105
+
106
+ * The second and third modules are for the page object model.
107
+
108
+ ```python
109
+ require 'citronella'
110
+ require_relative '../contents_page'
111
+
112
+ class HomePage < ContentsPage.new.header_menu
113
+ @url = "https://rubygems.org/"
114
+
115
+ def search_button
116
+ ui(class: 'home__search__icon', page: ContentsPage.new.search_page)
117
+ end
118
+
119
+ def code_link_button
120
+ ui(css: 'div.nav--v > a:nth-child(3)', page: Citronella::Dummy::PlaceholderPage)
121
+ end
122
+ end
123
+ ```
124
+
125
+ ___
126
+ ## Usage
127
+
128
+ ### citronella.WebPage
129
+
130
+ ###### Args:
131
+ - driver / webdriver
132
+
133
+ ###### Kwargs (optional):
134
+ - webdriver_wait `number(seconds)`, default value is `10`
135
+ - logger `bool`, default value is `true`
136
+
137
+ ###### Method Lists:
138
+ | Method Name | Args* | Kwargs** | Note |
139
+ | ------------------ |:-----------:|:----------------:|:----:|
140
+ | driver | None | None | return selenium `webdriver` object |
141
+ | locate | None | how: what | similar as`driver.get_element` args |
142
+ | page_object | Page Object | url `bool` | Page Object must contain `@url` variable with if using Kwargs** |
143
+ | page | None | None | |
144
+ | back | None | None | |
145
+ | webdriver_wait | number(sec) | None | |
146
+ | ready_state | number(sec) | None | execute javascript `document.readyState` manually |
147
+
148
+ ### citronella.ui / citronella.WebUi
149
+
150
+ ###### Kwargs:
151
+ - how: what
152
+ - page_object (optional)
153
+
154
+ ###### Method Lists:
155
+ | Method Name | Args* | Kwargs** | Note |
156
+ | ------------- |:------:|:------------------:|:----:|
157
+ | send_keys | text | clear `bool`, return_key `bool`, switch_page `bool` | |
158
+ | click | None | switch_page `bool` | |
159
+ | get_element | None | None | |
160
+ | get_elements | None | None | |
4
161
 
5
162
 
6
163
  ## Testing powered by
data/citronella.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'citronella'
3
- s.version = '0.0.3'
3
+ s.version = '0.0.4'
4
4
 
5
5
  s.authors = ['heyclore']
6
6
  s.email = 'cloore@gmail.com'
@@ -10,9 +10,9 @@ Gem::Specification.new do |s|
10
10
  Webdriver extension with a page object wrapper
11
11
  DESCRIPTION
12
12
 
13
- s.licenses = 'GPL-2.0'
13
+ s.licenses = 'MIT'
14
14
  s.required_ruby_version = '>= 2.7.0'
15
- s.homepage = 'https://github.com/heyclore/citronella#readme'
15
+ s.homepage = 'https://github.com/heyclore/citronella/tree/main/ruby#readme'
16
16
  s.metadata = {
17
17
  'source_code_uri' => 'https://github.com/heyclore/citronella/tree/main/ruby',
18
18
  'github_repo' => 'https://github.com/heyclore/citronella',
data/lib/citronella.rb CHANGED
@@ -1,2 +1,26 @@
1
+ #MIT License
2
+ #
3
+ #Copyright (c) 2023 Eko Purnomo
4
+ #
5
+ #Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ #of this software and associated documentation files (the "Software"), to deal
7
+ #in the Software without restriction, including without limitation the rights
8
+ #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ #copies of the Software, and to permit persons to whom the Software is
10
+ #furnished to do so, subject to the following conditions:
11
+ #
12
+ #The above copyright notice and this permission notice shall be included in all
13
+ #copies or substantial portions of the Software.
14
+ #
15
+ #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ #SOFTWARE.
22
+
23
+
1
24
  require_relative 'web_page'
2
25
  require_relative 'ui'
26
+ require_relative 'placeholder_page'
data/lib/logger.rb ADDED
@@ -0,0 +1,35 @@
1
+ #MIT License
2
+ #
3
+ #Copyright (c) 2023 Eko Purnomo
4
+ #
5
+ #Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ #of this software and associated documentation files (the "Software"), to deal
7
+ #in the Software without restriction, including without limitation the rights
8
+ #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ #copies of the Software, and to permit persons to whom the Software is
10
+ #furnished to do so, subject to the following conditions:
11
+ #
12
+ #The above copyright notice and this permission notice shall be included in all
13
+ #copies or substantial portions of the Software.
14
+ #
15
+ #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ #SOFTWARE.
22
+
23
+
24
+ require 'logger'
25
+
26
+ module Citronella
27
+ module Log
28
+ def self.logger(logger, class_name, function_name, name)
29
+ """This is a logger method."""
30
+ if logger
31
+ Logger.new(STDOUT, level: Logger::INFO).info("#{class_name} => #{function_name} => #{name}")
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,46 @@
1
+ #MIT License
2
+ #
3
+ #Copyright (c) 2023 Eko Purnomo
4
+ #
5
+ #Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ #of this software and associated documentation files (the "Software"), to deal
7
+ #in the Software without restriction, including without limitation the rights
8
+ #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ #copies of the Software, and to permit persons to whom the Software is
10
+ #furnished to do so, subject to the following conditions:
11
+ #
12
+ #The above copyright notice and this permission notice shall be included in all
13
+ #copies or substantial portions of the Software.
14
+ #
15
+ #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ #SOFTWARE.
22
+
23
+
24
+ require_relative 'web_ui.rb'
25
+
26
+ module Citronella
27
+ module Wrapper
28
+ class PageDecorator
29
+ """This is a page decorator class."""
30
+ def initialize(driver, webdriver_wait, pages, logger)
31
+ @driver = driver
32
+ @webdriver_wait = webdriver_wait
33
+ @pages = pages
34
+ @logger = logger
35
+ end
36
+
37
+ def method_missing(attr)
38
+ """look up the attr / method name inside page object."""
39
+ original_method = @pages.current_page.new.method(attr)
40
+ args = original_method.call
41
+ Citronella::Ui::WebUi.new(@driver, @webdriver_wait, @pages, @logger,
42
+ args.last, args.first, attr, @pages.current_page.name)
43
+ end
44
+ end
45
+ end
46
+ end
data/lib/page_tab.rb ADDED
@@ -0,0 +1,51 @@
1
+ #MIT License
2
+ #
3
+ #Copyright (c) 2023 Eko Purnomo
4
+ #
5
+ #Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ #of this software and associated documentation files (the "Software"), to deal
7
+ #in the Software without restriction, including without limitation the rights
8
+ #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ #copies of the Software, and to permit persons to whom the Software is
10
+ #furnished to do so, subject to the following conditions:
11
+ #
12
+ #The above copyright notice and this permission notice shall be included in all
13
+ #copies or substantial portions of the Software.
14
+ #
15
+ #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ #SOFTWARE.
22
+
23
+
24
+ module Citronella
25
+ class PagesList
26
+ """This is a page page tab class."""
27
+ def initialize
28
+ @pages = []
29
+ end
30
+
31
+ def current_page
32
+ """return the last page object stored."""
33
+ @pages.last
34
+ end
35
+
36
+ def append(new_page)
37
+ """store the page object in _pages."""
38
+ @pages << new_page
39
+ if @pages.length > 5
40
+ @pages.shift
41
+ end
42
+ end
43
+
44
+ def pop
45
+ """delete the last item of the _pages lists."""
46
+ return if @pages.empty?
47
+ @pages.delete_at(-1)
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,29 @@
1
+ #MIT License
2
+ #
3
+ #Copyright (c) 2023 Eko Purnomo
4
+ #
5
+ #Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ #of this software and associated documentation files (the "Software"), to deal
7
+ #in the Software without restriction, including without limitation the rights
8
+ #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ #copies of the Software, and to permit persons to whom the Software is
10
+ #furnished to do so, subject to the following conditions:
11
+ #
12
+ #The above copyright notice and this permission notice shall be included in all
13
+ #copies or substantial portions of the Software.
14
+ #
15
+ #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ #SOFTWARE.
22
+
23
+
24
+ module Citronella
25
+ module Dummy
26
+ class PlaceholderPage
27
+ end
28
+ end
29
+ end
data/lib/ui.rb CHANGED
@@ -1,4 +1,42 @@
1
+ #MIT License
2
+ #
3
+ #Copyright (c) 2023 Eko Purnomo
4
+ #
5
+ #Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ #of this software and associated documentation files (the "Software"), to deal
7
+ #in the Software without restriction, including without limitation the rights
8
+ #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ #copies of the Software, and to permit persons to whom the Software is
10
+ #furnished to do so, subject to the following conditions:
11
+ #
12
+ #The above copyright notice and this permission notice shall be included in all
13
+ #copies or substantial portions of the Software.
14
+ #
15
+ #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ #SOFTWARE.
22
+
23
+
1
24
  def ui(args)
2
- baz = Struct.new(:page, :exception, :locator)
3
- baz.new(args.delete(:page), args.delete(:exception), args)
25
+ """
26
+ forward the data to page decorator and wrap into WebUi class.
27
+
28
+ Args:
29
+ by
30
+
31
+ Kwarg:
32
+ page(optional)
33
+
34
+ Usage:
35
+ without reference / page object. ui(name: 'q')
36
+ with reference / page object. ui(id: 'submit', UserMenuPage)
37
+
38
+ page are reference for the next page object if the element redirect to
39
+ another page with WebUi click and send_keys(enter key) from input form.
40
+ """
41
+ return args.delete(:page), args
4
42
  end
data/lib/web_page.rb CHANGED
@@ -1,24 +1,129 @@
1
- require_relative 'page_store'
2
- require_relative 'page_wrapper'
1
+ #MIT License
2
+ #
3
+ #Copyright (c) 2023 Eko Purnomo
4
+ #
5
+ #Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ #of this software and associated documentation files (the "Software"), to deal
7
+ #in the Software without restriction, including without limitation the rights
8
+ #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ #copies of the Software, and to permit persons to whom the Software is
10
+ #furnished to do so, subject to the following conditions:
11
+ #
12
+ #The above copyright notice and this permission notice shall be included in all
13
+ #copies or substantial portions of the Software.
14
+ #
15
+ #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ #SOFTWARE.
22
+
23
+
24
+ require_relative 'page_tab'
25
+ require_relative 'page_decorator'
26
+ require_relative 'web_ui'
3
27
 
4
28
  module Citronella
5
- class WebPage
6
- def initialize(driver, webdriver_wait=10)
7
- @driver = driver
8
- @webdriver_wait = webdriver_wait
9
- @pages = Citronella::PagesStore::PagesList.new
10
- end
29
+ module Web
30
+ class WebPage
31
+ """
32
+ an object class that use across the tests.
33
+ webdriver_wait is set '10' seconds by default
34
+ logger is set 'True' by default
11
35
 
12
- def driver
13
- @driver
14
- end
36
+ Args:
37
+ driver
38
+ Kwargs (optional):
39
+ webdriver_wait
40
+ logger
15
41
 
16
- def page_object(x)
17
- @pages.get << x
18
- end
42
+ Usage:
43
+ driver = Selenium::WebDriver.for :chrome
44
+ web = WebPage(driver)
45
+ """
46
+ def initialize(driver, webdriver_wait:10, logger:true)
47
+ @driver = driver
48
+ @webdriver_wait = webdriver_wait
49
+ @pages = Citronella::PagesList.new
50
+ @logger = logger
51
+ end
52
+
53
+ def driver
54
+ """return the original selenium / appium driver."""
55
+ @driver
56
+ end
57
+
58
+ def page_object(new_page, url=false)
59
+ """
60
+ initialize page object module object, url kwargs is optional and
61
+ it set to FALSE by default, it can be use if the page object have
62
+ an @url variable.
63
+ in selenium:
64
+ it's equal as self.driver.navigate.to(url)
65
+
66
+ Args:
67
+ page_object_model
68
+
69
+ Kwargs:
70
+ url=true
71
+
72
+ Usage:
73
+ self.browser.page_object(Homepage)
74
+ or
75
+ self.browser.page_object(Homepage, url=true)
76
+ """
77
+ @pages.append(new_page)
78
+ if url
79
+ if not new_page.instance_variable_get(:@url)
80
+ raise "Error: '@url' variable does not exist in #{new_page}"
81
+ end
82
+ @driver.navigate.to(new_page.instance_variable_get(:@url))
83
+ end
84
+ end
85
+
86
+ def page
87
+ """return last page object model."""
88
+ Citronella::Wrapper::PageDecorator.new(@driver, @webdriver_wait, @pages, @logger)
89
+ end
90
+
91
+ def locate(args)
92
+ """
93
+ an alternative way for testing without page object and return WebUi
94
+ class, but can't use page and back method and causing an error.
95
+ good for quick prototype / write a tests.
96
+
97
+ Args:
98
+ by
99
+ value
100
+
101
+ Usage:
102
+ web.ui(name: 'q').get_element.text
103
+ web.ui(name: 'q').get_element.click
104
+ """
105
+ Citronella::Ui::WebUi.new(@driver, @webdriver_wait, @pages, @logger, args, nil,
106
+ __method__.to_s, self.class.name.split('::').last.to_s)
107
+ end
108
+
109
+ def back
110
+ """return to previous page and delete the last page object."""
111
+ @driver.navigate.back
112
+ @pages.pop
113
+ end
114
+
115
+ def ready_state(wait)
116
+ """execute javascript for page to fully load"""
117
+ wait.times do |i|
118
+ return if driver.execute_script("return document.readyState") == "complete"
119
+ sleep(1)
120
+ end
121
+ end
19
122
 
20
- def page
21
- Citronella::PageWrapper.ui_decorator(@driver, @webdriver_wait, @pages)
123
+ def webdriver_wait(wait)
124
+ """override webdriver wait."""
125
+ @webdriver_wait = wait
126
+ end
22
127
  end
23
128
  end
24
129
  end
data/lib/web_ui.rb ADDED
@@ -0,0 +1,93 @@
1
+ #MIT License
2
+ #
3
+ #Copyright (c) 2023 Eko Purnomo
4
+ #
5
+ #Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ #of this software and associated documentation files (the "Software"), to deal
7
+ #in the Software without restriction, including without limitation the rights
8
+ #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ #copies of the Software, and to permit persons to whom the Software is
10
+ #furnished to do so, subject to the following conditions:
11
+ #
12
+ #The above copyright notice and this permission notice shall be included in all
13
+ #copies or substantial portions of the Software.
14
+ #
15
+ #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ #SOFTWARE.
22
+
23
+
24
+ require_relative 'logger'
25
+
26
+ module Citronella
27
+ module Ui
28
+ class WebUi
29
+ """a wrapped object of a web element."""
30
+ def initialize(driver, webdriver_wait, pages, logger, locator, new_page,
31
+ function_name, class_name)
32
+ @driver = driver
33
+ @wait = webdriver_wait
34
+ @pages = pages
35
+ @logger = logger
36
+ @locator = locator
37
+ @new_page = new_page
38
+ @function_name = function_name
39
+ @class_name = class_name
40
+ end
41
+
42
+ private def webdriver_wait(ele, displayed=false)
43
+ """return a web element or elements."""
44
+ el = Selenium::WebDriver::Wait.new(timeout: @wait).until { ele }
45
+ if displayed
46
+ @wait.times do
47
+ break if el.displayed?
48
+ sleep(1)
49
+ end
50
+ end
51
+ el
52
+ end
53
+
54
+ def send_keys(text, clear=false, return_key=false, switch_page=true)
55
+ """custom webdriver send_keys with optional clear field."""
56
+ Citronella::Log.logger(@logger, @class_name, @function_name, __method__)
57
+ el = webdriver_wait(@driver.find_element(@locator), displayed=true)
58
+ el.send_keys text
59
+
60
+ if return_key
61
+ el.send_keys :return
62
+
63
+ if @new_page and switch_page
64
+ @pages.append(@new_page)
65
+ end
66
+ end
67
+ end
68
+
69
+ def click(switch_page=true)
70
+ """click to web element."""
71
+ Citronella::Log.logger(@logger, @class_name, @function_name, __method__)
72
+ el = webdriver_wait(@driver.find_element(@locator), displayed=true)
73
+ el.click
74
+
75
+ if @new_page and switch_page
76
+ @pages.append(@new_page)
77
+ end
78
+ end
79
+
80
+ def get_element
81
+ """return web element, equal as find_element."""
82
+ Citronella::Log.logger(@logger, @class_name, @function_name, __method__.to_s)
83
+ webdriver_wait(@driver.find_element(@locator))
84
+ end
85
+
86
+ def get_elements
87
+ """return list of web element, equal as find_elements."""
88
+ Citronella::Log.logger(@logger, @class_name, @function_name, __method__.to_s)
89
+ webdriver_wait(@driver.find_elements(@locator))
90
+ end
91
+ end
92
+ end
93
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: citronella
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - heyclore
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-07 00:00:00.000000000 Z
11
+ date: 2023-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: selenium-webdriver
@@ -34,14 +34,16 @@ files:
34
34
  - README.md
35
35
  - citronella.gemspec
36
36
  - lib/citronella.rb
37
- - lib/page_store.rb
38
- - lib/page_wrapper.rb
37
+ - lib/logger.rb
38
+ - lib/page_decorator.rb
39
+ - lib/page_tab.rb
40
+ - lib/placeholder_page.rb
39
41
  - lib/ui.rb
40
- - lib/ui_object.rb
41
42
  - lib/web_page.rb
42
- homepage: https://github.com/heyclore/citronella#readme
43
+ - lib/web_ui.rb
44
+ homepage: https://github.com/heyclore/citronella/tree/main/ruby#readme
43
45
  licenses:
44
- - GPL-2.0
46
+ - MIT
45
47
  metadata:
46
48
  source_code_uri: https://github.com/heyclore/citronella/tree/main/ruby
47
49
  github_repo: https://github.com/heyclore/citronella
data/lib/page_store.rb DELETED
@@ -1,17 +0,0 @@
1
- module Citronella
2
- module PagesStore
3
- class PagesList
4
- def initialize
5
- @page_lists = []
6
- end
7
-
8
- def get
9
- if @page_lists.length > 5
10
- @page_lists.shift
11
- end
12
- @page_lists
13
- end
14
- end
15
- end
16
- end
17
-
data/lib/page_wrapper.rb DELETED
@@ -1,30 +0,0 @@
1
- require_relative 'ui_object.rb'
2
-
3
- module Citronella
4
- module PageWrapper
5
- def self.method_wrapper(klass, method_name, driver, webdriver_wait, pages)
6
- original_method = klass.instance_method(method_name)
7
- klass.define_method(method_name) do
8
- puts method_name
9
- args = original_method.bind(self).call
10
- Citronella::UiObject::Ui.new(driver, webdriver_wait, pages,
11
- args.locator, args.page, args.exception)
12
- end
13
- end
14
-
15
- def self.class_decorator(klass, driver, webdriver_wait, pages)
16
- return if klass.instance_variable_defined?(:@decorated)
17
- klass.instance_variable_set(:@decorated, true)
18
- return if klass.name == "Object"
19
- lists = klass.instance_methods(false)
20
- lists.each { |method| method_wrapper(klass, method, driver, webdriver_wait, pages) }
21
- class_decorator(klass.superclass, driver, webdriver_wait, pages)
22
- end
23
-
24
- def self.ui_decorator(driver, webdriver_wait, pages)
25
- klass = pages.get.last
26
- class_decorator(klass, driver, webdriver_wait, pages)
27
- klass.new
28
- end
29
- end
30
- end
data/lib/ui_object.rb DELETED
@@ -1,43 +0,0 @@
1
- module Citronella
2
- module UiObject
3
- class Ui
4
- def initialize(driver, webdriver_wait, pages, locator, page, exception)
5
- @driver = driver
6
- @webdriver_wait = Selenium::WebDriver::Wait.new(timeout: webdriver_wait)
7
- @pages = pages
8
- @locator = locator
9
- @page = page
10
- @exception = exception
11
- end
12
-
13
- def send_keys(text, enter: false)
14
- @webdriver_wait.until { @driver.find_element(@locator).displayed? }
15
- el = @driver.find_element(@locator)
16
- el.send_keys text
17
-
18
- if enter
19
- el.send_keys :return
20
-
21
- if @page
22
- @pages.get << @page
23
- end
24
- end
25
- end
26
-
27
- def click
28
- @webdriver_wait.until { @driver.find_element(@locator).displayed? }
29
- @driver.find_element(@locator).click
30
-
31
- if @page
32
- @pages.get << @page
33
- end
34
- end
35
-
36
- def get_elements
37
- @webdriver_wait.until { @driver.find_element(@locator).displayed? }
38
- @driver.find_elements(@locator)
39
- end
40
- end
41
- end
42
- end
43
-