testcentricity_web 4.1.2.1 → 4.1.3
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 +4 -4
- data/CHANGELOG.md +10 -1
- data/README.md +197 -153
- data/lib/testcentricity_web/appium_server.rb +2 -2
- data/lib/testcentricity_web/version.rb +1 -1
- metadata +2 -3
- data/Gemfile.lock +0 -112
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 343b7e55068d41c269359c5d0700b4d6819e303b50ccaa789ba57175e3fc37c2
|
4
|
+
data.tar.gz: 2b29b44c8f384d6103505837fb7e550a83b07ee5487088eb8a0f6803f6dce055
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91e6cb334abe12854b8f7d660a01e63c0c9934bbfeb85177ac2a1c864aaf7c1462b061f36323d8c41fa4bced9964e5009f0e388c4a15fa3fda260e3710fd85e0
|
7
|
+
data.tar.gz: 5c3da7d79204c0a0311f703508c4738aaac923d6c4feef63737c690754388e64c2e97056949f61856b72e6c1a43915c43b914237a4d3cb1b1b3f48c6b387422d
|
data/CHANGELOG.md
CHANGED
@@ -2,10 +2,19 @@
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
4
|
|
5
|
+
## [4.1.3] - 08-MAR-2022
|
6
|
+
|
7
|
+
### Fixed
|
8
|
+
* Fixed `AppiumServer.start` so that it no longer times out after failing to start Appium.
|
9
|
+
|
10
|
+
### Updated
|
11
|
+
* Updated docs regarding the `SHUTDOWN_OTHER_SIMS` Environment Variable when testing on iOS Simulators.
|
12
|
+
|
13
|
+
|
5
14
|
## [4.1.2] - 07-MAR-2022
|
6
15
|
|
7
16
|
### Changed
|
8
|
-
* Updated HTML documentation
|
17
|
+
* Updated HTML documentation.
|
9
18
|
|
10
19
|
|
11
20
|
## [4.1.1] - 03-MAR-2022
|
data/README.md
CHANGED
@@ -6,8 +6,6 @@
|
|
6
6
|
The TestCentricity™ Web core generic framework for desktop and mobile web browser-based app testing implements a Page Object and Data
|
7
7
|
Object Model DSL for use with Cucumber, Capybara (version 3.x), and Selenium-Webdriver (version 4.x).
|
8
8
|
|
9
|
-
**An example project that demonstrates the implementation of a page object model framework using Cucumber and TestCentricity™ can be found [here](https://github.com/TestCentricity/tc_web_sample).**
|
10
|
-
|
11
9
|
The TestCentricity™ Web gem supports running automated tests against the following web test targets:
|
12
10
|
* locally hosted desktop browsers (Firefox, Chrome, Edge, Safari, or IE)
|
13
11
|
* locally hosted emulated iOS Mobile Safari, Android, Windows Phone, or Blackberry mobile browsers (running within a local instance of Chrome)
|
@@ -28,10 +26,13 @@ The TestCentricity™ Web gem supports running automated tests against the follo
|
|
28
26
|
|
29
27
|
A complete history of bug fixes and new features can be found in the {file:CHANGELOG.md CHANGELOG} file.
|
30
28
|
|
29
|
+
An example project that demonstrates the implementation of a page object model framework using Cucumber and TestCentricity™
|
30
|
+
can be found [here](https://github.com/TestCentricity/tc_web_sample).
|
31
|
+
|
31
32
|
|
32
33
|
## Installation
|
33
34
|
|
34
|
-
TestCentricity requires Ruby 2.7 or later. To install the TestCentricity gem, add this line to your automation project's Gemfile:
|
35
|
+
TestCentricity version 4.1 and above requires Ruby 2.7 or later. To install the TestCentricity gem, add this line to your automation project's Gemfile:
|
35
36
|
|
36
37
|
gem 'testcentricity_web'
|
37
38
|
|
@@ -78,7 +79,7 @@ And then execute:
|
|
78
79
|
$ bundle
|
79
80
|
|
80
81
|
|
81
|
-
##
|
82
|
+
## PageObjects
|
82
83
|
|
83
84
|
The **Page Object Model** is a test automation pattern that aims to create an abstraction of your web app's User Interface that can be used
|
84
85
|
in tests. A **Page Object** is an object that represents a single page in your AUT (Application Under Test). **Page Objects** encapsulate the
|
@@ -87,14 +88,14 @@ implementation details of a web page and expose an API that supports interaction
|
|
87
88
|
**Page Objects** makes it easier to maintain automated tests because changes to page UI elements are updated in only one location - in the
|
88
89
|
**Page Object** class definition. By adopting a **Page Object Model**, Cucumber Feature files and step definitions are no longer required to
|
89
90
|
hold specific information about a page's UI objects, thus minimizing maintenance requirements. If any element on, or property of a page changes
|
90
|
-
(URL path, text field attributes, button captions, etc.), maintenance is performed in the
|
91
|
+
(URL path, text field attributes, button captions, etc.), maintenance is performed in the `PageObject` class definition only, typically with
|
91
92
|
no need to update the affected feature file, scenarios, or step definitions.
|
92
93
|
|
93
94
|
|
94
|
-
### Defining a
|
95
|
+
### Defining a PageObject
|
95
96
|
|
96
|
-
Your
|
97
|
-
test automation project. You define new
|
97
|
+
Your `PageObject` class definitions should be contained within individual `.rb` files in the `features/support/pages` folder of your
|
98
|
+
test automation project. You define new `PageObjects` as shown below:
|
98
99
|
|
99
100
|
class LoginPage < TestCentricity::PageObject
|
100
101
|
end
|
@@ -108,14 +109,14 @@ test automation project. You define new **Page Objects** as shown below:
|
|
108
109
|
end
|
109
110
|
|
110
111
|
|
111
|
-
### Adding Traits to your
|
112
|
+
### Adding Traits to your PageObject
|
112
113
|
|
113
114
|
Web pages typically have names and URLs associated with them. Web pages also typically have a unique object or attribute that, when present,
|
114
115
|
indicates that the page's contents have fully loaded.
|
115
116
|
|
116
|
-
The `page_name` trait is registered with the
|
117
|
-
parameter and returns an instance of the associated
|
118
|
-
trait for each
|
117
|
+
The `page_name` trait is registered with the `PageManager` object, which includes a `find_page` method that takes a page name as a
|
118
|
+
parameter and returns an instance of the associated `Page Object`. If you intend to use the `PageManager`, you must define a `page_name`
|
119
|
+
trait for each `PageObject` to be registered.
|
119
120
|
|
120
121
|
The `page_name` trait is usually a `String` value that represents the name of the page that will be matched by the `PageManager.findpage` method.
|
121
122
|
`page_name` traits are case and white-space sensitive. For pages that may be referenced with multiple names, the `page_name` trait may also be
|
@@ -133,36 +134,36 @@ for the `page_locator` trait to exist.
|
|
133
134
|
You define your page's **Traits** as shown below:
|
134
135
|
|
135
136
|
class LoginPage < TestCentricity::PageObject
|
136
|
-
trait(:page_name)
|
137
|
-
trait(:page_url)
|
138
|
-
trait(:page_locator)
|
137
|
+
trait(:page_name) { 'Login' }
|
138
|
+
trait(:page_url) { '/sign_in' }
|
139
|
+
trait(:page_locator) { 'body.login-body' }
|
139
140
|
end
|
140
141
|
|
141
142
|
|
142
143
|
class HomePage < TestCentricity::PageObject
|
143
144
|
# this page may be referred to as 'Home' or 'Dashboard' page so page_name trait is an Array of Strings
|
144
|
-
trait(:page_name)
|
145
|
-
trait(:page_url)
|
146
|
-
trait(:page_locator)
|
145
|
+
trait(:page_name) { ['Home', 'Dashboard'] }
|
146
|
+
trait(:page_url) { '/dashboard' }
|
147
|
+
trait(:page_locator) { 'body.dashboard' }
|
147
148
|
end
|
148
149
|
|
149
150
|
|
150
151
|
class RegistrationPage < TestCentricity::PageObject
|
151
|
-
trait(:page_name)
|
152
|
-
trait(:page_url)
|
153
|
-
trait(:page_locator)
|
152
|
+
trait(:page_name) { 'Registration' }
|
153
|
+
trait(:page_url) { '/register' }
|
154
|
+
trait(:page_locator) { 'body.registration' }
|
154
155
|
end
|
155
156
|
|
156
157
|
|
157
|
-
### Adding UI Elements to your
|
158
|
+
### Adding UI Elements to your PageObject
|
158
159
|
|
159
160
|
Web pages are made up of UI elements like text fields, check boxes, combo boxes, radio buttons, tables, lists, buttons, etc.
|
160
|
-
**UI Elements** are added to your
|
161
|
+
**UI Elements** are added to your `PageObject` class definition as shown below:
|
161
162
|
|
162
163
|
class LoginPage < TestCentricity::PageObject
|
163
|
-
trait(:page_name)
|
164
|
-
trait(:page_url)
|
165
|
-
trait(:page_locator)
|
164
|
+
trait(:page_name) { 'Login' }
|
165
|
+
trait(:page_url) { '/sign_in' }
|
166
|
+
trait(:page_locator) { 'body.login-body' }
|
166
167
|
|
167
168
|
# Login page UI elements
|
168
169
|
textfield :user_id_field, 'input#userName'
|
@@ -174,9 +175,9 @@ Web pages are made up of UI elements like text fields, check boxes, combo boxes,
|
|
174
175
|
|
175
176
|
|
176
177
|
class RegistrationPage < TestCentricity::PageObject
|
177
|
-
trait(:page_name)
|
178
|
-
trait(:page_url)
|
179
|
-
trait(:page_locator)
|
178
|
+
trait(:page_name) { 'Registration' }
|
179
|
+
trait(:page_url) { '/register' }
|
180
|
+
trait(:page_locator) { 'body.registration' }
|
180
181
|
|
181
182
|
# Registration page UI elements
|
182
183
|
textfields first_name_field: 'input#firstName',
|
@@ -196,16 +197,16 @@ Web pages are made up of UI elements like text fields, check boxes, combo boxes,
|
|
196
197
|
end
|
197
198
|
|
198
199
|
|
199
|
-
### Adding Methods to your
|
200
|
+
### Adding Methods to your PageObject
|
200
201
|
|
201
|
-
It is good practice for your Cucumber step definitions to call high level methods in your your
|
202
|
-
and interacting with a page object's UI elements. You can add high level methods to your
|
202
|
+
It is good practice for your Cucumber step definitions to call high level methods in your your `PageObject` instead of directly accessing
|
203
|
+
and interacting with a page object's UI elements. You can add high level methods to your `PageObject` class definition for interacting with
|
203
204
|
the UI to hide implementation details, as shown below:
|
204
205
|
|
205
206
|
class LoginPage < TestCentricity::PageObject
|
206
|
-
trait(:page_name)
|
207
|
-
trait(:page_url)
|
208
|
-
trait(:page_locator)
|
207
|
+
trait(:page_name) { 'Login' }
|
208
|
+
trait(:page_url) { '/sign_in' }
|
209
|
+
trait(:page_locator) { 'body.login-body' }
|
209
210
|
|
210
211
|
# Login page UI elements
|
211
212
|
textfield :user_id_field, 'input#userName'
|
@@ -232,9 +233,9 @@ the UI to hide implementation details, as shown below:
|
|
232
233
|
ui = {
|
233
234
|
self => { title: 'Login' },
|
234
235
|
login_button => { visible: true, caption: 'LOGIN' },
|
235
|
-
user_id_field => { visible: true, enabled: true },
|
236
|
+
user_id_field => { visible: true, enabled: true, value: '', placeholder: 'User name' },
|
236
237
|
password_field => { visible: true, enabled: true, value: '', placeholder: 'Password' },
|
237
|
-
remember_checkbox => { :
|
238
|
+
remember_checkbox => { exists: true, enabled: true, checked: false },
|
238
239
|
forgot_password_link => { visible: true, caption: 'Forgot your password?' },
|
239
240
|
error_message_label => { visible: false }
|
240
241
|
}
|
@@ -244,9 +245,9 @@ the UI to hide implementation details, as shown below:
|
|
244
245
|
|
245
246
|
|
246
247
|
class RegistrationPage < TestCentricity::PageObject
|
247
|
-
trait(:page_name)
|
248
|
-
trait(:page_url)
|
249
|
-
trait(:page_locator)
|
248
|
+
trait(:page_name) { 'Registration' }
|
249
|
+
trait(:page_url) { '/register' }
|
250
|
+
trait(:page_locator) { 'body.registration' }
|
250
251
|
|
251
252
|
# Registration page UI elements
|
252
253
|
textfields first_name_field: 'input#firstName',
|
@@ -278,7 +279,8 @@ the UI to hide implementation details, as shown below:
|
|
278
279
|
state_select => profile.state,
|
279
280
|
post_code_field => profile.postal_code,
|
280
281
|
password_field => profile.password,
|
281
|
-
pword_confirm_field => profile.confirm_password
|
282
|
+
pword_confirm_field => profile.confirm_password,
|
283
|
+
email_opt_in_check => profile.email_opt_in
|
282
284
|
}
|
283
285
|
populate_data_fields(fields)
|
284
286
|
sign_up_button.click
|
@@ -287,18 +289,18 @@ the UI to hide implementation details, as shown below:
|
|
287
289
|
|
288
290
|
|
289
291
|
|
290
|
-
Once your
|
292
|
+
Once your `PageObjects` have been instantiated, you can call your methods as shown below:
|
291
293
|
|
292
294
|
login_page.remember_me(true)
|
293
295
|
login_page.login('snicklefritz', 'Pa55w0rd')
|
294
296
|
|
295
297
|
|
296
298
|
|
297
|
-
##
|
299
|
+
## PageSections
|
298
300
|
|
299
|
-
A
|
301
|
+
A `PageSection` is a collection of **UI Elements** that may appear in multiple locations on a page, or on multiple pages in a web
|
300
302
|
app. It is a collection of **UI Elements** that represent a conceptual area of functionality, like a navigation bar, a search capability,
|
301
|
-
or a menu. **UI Elements** and functional behavior are confined to the scope of a
|
303
|
+
or a menu. **UI Elements** and functional behavior are confined to the scope of a `PageSection` object.
|
302
304
|
|
303
305
|
<img src="https://i.imgur.com/BTgi59R.jpg" alt="Navigation Header" title="Navigation Header">
|
304
306
|
|
@@ -307,39 +309,39 @@ or a menu. **UI Elements** and functional behavior are confined to the scope of
|
|
307
309
|
<img src="https://i.imgur.com/Yqgw4sP.jpg" alt="User Profile Popup" title="User Profile Popup">
|
308
310
|
|
309
311
|
|
310
|
-
A
|
312
|
+
A `PageSection` may contain other `PageSection` objects.
|
311
313
|
|
312
314
|
|
313
|
-
### Defining a PageSection
|
315
|
+
### Defining a PageSection
|
314
316
|
|
315
|
-
Your
|
316
|
-
your test automation project. You define new
|
317
|
+
Your `PageSection` class definitions should be contained within individual `.rb` files in the `features/support/sections` folder of
|
318
|
+
your test automation project. You define new `PageSection` as shown below:
|
317
319
|
|
318
320
|
class SearchForm < TestCentricity::PageSection
|
319
321
|
end
|
320
322
|
|
321
323
|
|
322
|
-
### Adding Traits to a PageSection
|
324
|
+
### Adding Traits to a PageSection
|
323
325
|
|
324
|
-
A
|
326
|
+
A `PageSection` typically has a root node object that encapsulates a collection of `UIElements`. The `section_locator` trait
|
325
327
|
specifies the CSS or Xpath expression that uniquely identifies that root node object.
|
326
328
|
|
327
|
-
You define your
|
329
|
+
You define your section's **Traits** as shown below:
|
328
330
|
|
329
331
|
class SearchForm < TestCentricity::PageSection
|
330
|
-
trait(:section_locator)
|
331
|
-
trait(:section_name)
|
332
|
+
trait(:section_locator) { 'form#gnav-search' }
|
333
|
+
trait(:section_name) { 'Search widget' }
|
332
334
|
end
|
333
335
|
|
334
336
|
|
335
|
-
### Adding UI Elements to your PageSection
|
337
|
+
### Adding UI Elements to your PageSection
|
336
338
|
|
337
|
-
|
338
|
-
**UI Elements** are added to your
|
339
|
+
`PageSections` are typically made up of UI elements like text fields, check boxes, combo boxes, radio buttons, tables, lists, buttons, etc.
|
340
|
+
**UI Elements** are added to your `PageSection` class definition as shown below:
|
339
341
|
|
340
342
|
class SearchForm < TestCentricity::PageSection
|
341
|
-
trait(:section_locator)
|
342
|
-
trait(:section_name)
|
343
|
+
trait(:section_locator) { 'form#gnav-search' }
|
344
|
+
trait(:section_name) { 'Search widget' }
|
343
345
|
|
344
346
|
# Search Form UI elements
|
345
347
|
textfield :search_field, 'input#search-query'
|
@@ -347,13 +349,13 @@ Page sections are typically made up of UI elements like text fields, check boxes
|
|
347
349
|
end
|
348
350
|
|
349
351
|
|
350
|
-
### Adding Methods to your PageSection
|
352
|
+
### Adding Methods to your PageSection
|
351
353
|
|
352
|
-
You can add high level methods to your
|
354
|
+
You can add high level methods to your `PageSection` class definition, as shown below:
|
353
355
|
|
354
356
|
class SearchForm < TestCentricity::PageSection
|
355
|
-
trait(:section_locator)
|
356
|
-
trait(:section_name)
|
357
|
+
trait(:section_locator) { 'form#gnav-search' }
|
358
|
+
trait(:section_name) { 'Search widget' }
|
357
359
|
|
358
360
|
# Search Form UI elements
|
359
361
|
textfield :search_field, 'input#search-query'
|
@@ -366,48 +368,48 @@ You can add high level methods to your **PageSection** class definition, as show
|
|
366
368
|
end
|
367
369
|
|
368
370
|
|
369
|
-
### Adding
|
371
|
+
### Adding PageSections to your PageObject
|
370
372
|
|
371
|
-
You add a
|
373
|
+
You add a `PageSection` to its associated `PageObject` as shown below:
|
372
374
|
|
373
375
|
class HomePage < TestCentricity::PageObject
|
374
|
-
trait(:page_name)
|
375
|
-
trait(:page_url)
|
376
|
-
trait(:page_locator)
|
376
|
+
trait(:page_name) { 'Home' }
|
377
|
+
trait(:page_url) { '/dashboard' }
|
378
|
+
trait(:page_locator) { 'body.dashboard' }
|
377
379
|
|
378
380
|
# Home page Section Objects
|
379
381
|
section :search_form, SearchForm
|
380
382
|
end
|
381
383
|
|
382
|
-
Once your
|
384
|
+
Once your `PageObject` has been instantiated, you can call its `PageSection` methods as shown below:
|
383
385
|
|
384
386
|
home_page.search_form.search_for('ocarina')
|
385
387
|
|
386
388
|
|
387
389
|
|
388
|
-
##
|
390
|
+
## UIElements
|
389
391
|
|
390
|
-
|
391
|
-
tables, lists, buttons, images, HTML5 video objects, HTML5 audio objects, etc. **UI Elements** are declared
|
392
|
-
definition of the
|
393
|
-
the
|
392
|
+
`PageObjects` and `PageSections` are typically made up of **UI Element** like text fields, check boxes, select lists (combo boxes),
|
393
|
+
radio buttons, tables, ordered and unordered lists, buttons, images, HTML5 video objects, HTML5 audio objects, etc. **UI Elements** are declared
|
394
|
+
and instantiated within the class definition of the `PageObject` or `PageSection` in which they are contained. With TestCentricity Web,
|
395
|
+
all UI elements are based on the `UIElement` class.
|
394
396
|
|
395
397
|
|
396
|
-
### Declaring and Instantiating
|
398
|
+
### Declaring and Instantiating UIElements
|
397
399
|
|
398
|
-
Single
|
400
|
+
Single `UIElement` declarations have the following format:
|
399
401
|
|
400
402
|
elementType :element Name, locator
|
401
403
|
|
402
|
-
* The `element name` is the unique name that you will use to refer to the UI element and is specified as a
|
403
|
-
* The `locator` is the CSS or XPath attribute that uniquely and unambiguously identifies the
|
404
|
+
* The `element name` is the unique name that you will use to refer to the UI element and is specified as a `Symbol`.
|
405
|
+
* The `locator` is the CSS or XPath attribute that uniquely and unambiguously identifies the `UIElement`.
|
404
406
|
|
405
|
-
Multiple
|
407
|
+
Multiple `UIElement` declarations for a collection of elements of the same type can be performed by passing a hash table containing the
|
406
408
|
names and locators of each individual element.
|
407
409
|
|
408
|
-
### Example
|
410
|
+
### Example UIElement Declarations
|
409
411
|
|
410
|
-
Supported
|
412
|
+
Supported `UIElement` elementTypes and their declarations have the following format:
|
411
413
|
|
412
414
|
*Single element declarations:*
|
413
415
|
|
@@ -469,14 +471,14 @@ Supported **UI Element** elementTypes and their declarations have the following
|
|
469
471
|
end
|
470
472
|
|
471
473
|
|
472
|
-
Refer to the Class List documentation for the
|
473
|
-
and instantiating
|
474
|
+
Refer to the Class List documentation for the `PageObject` and `PageSection` classes for details on the class methods used for declaring
|
475
|
+
and instantiating `UIElements`. Examples of UI element declarations can be found in the ***Adding UI Elements to your Page Object*** and
|
474
476
|
***Adding UI Elements to your PageSection Object*** sections above.
|
475
477
|
|
476
478
|
|
477
479
|
### UIElement Inherited Methods
|
478
480
|
|
479
|
-
With TestCentricity, all UI elements are based on the
|
481
|
+
With TestCentricity, all UI elements are based on the `UIElement` class, and inherit the following methods:
|
480
482
|
|
481
483
|
**Action methods:**
|
482
484
|
|
@@ -600,7 +602,7 @@ for each `UIElement` that requires verification. Depending on the complexity and
|
|
600
602
|
verify the presence of `UIElements` and their correct states can become cumbersome.
|
601
603
|
|
602
604
|
The `PageObject.verify_ui_states` and `PageSection.verify_ui_states` methods support the verification of multiple properties of multiple
|
603
|
-
UI elements on a
|
605
|
+
UI elements on a `PageObject` or `PageSection`. The `verify_ui_states` method accepts a hash containing key/hash pairs of UI
|
604
606
|
elements and their properties or attributes to be verified.
|
605
607
|
|
606
608
|
ui = {
|
@@ -699,6 +701,8 @@ The `verify_ui_states` method supports the following property/state pairs:
|
|
699
701
|
:preload String
|
700
702
|
:poster String
|
701
703
|
|
704
|
+
#### ARIA accessibility property/state pairs
|
705
|
+
|
702
706
|
The `verify_ui_states` method supports the following ARIA accessibility property/state pairs:
|
703
707
|
|
704
708
|
**All Objects:**
|
@@ -734,11 +738,12 @@ The `verify_ui_states` method supports the following ARIA accessibility property
|
|
734
738
|
:aria_multiselectable Boolean
|
735
739
|
:content_editable Boolean
|
736
740
|
|
741
|
+
#### Comparison States
|
737
742
|
The `verify_ui_states` method supports comparison states using property/comparison state pairs:
|
738
743
|
|
739
744
|
object => { property: { comparison_state: value } }
|
740
745
|
|
741
|
-
|
746
|
+
Comparison States:
|
742
747
|
|
743
748
|
:lt or :less_than Integer or String
|
744
749
|
:lt_eq or :less_than_or_equal Integer or String
|
@@ -773,19 +778,19 @@ values appear in the associated text fields after entering data and performing a
|
|
773
778
|
end
|
774
779
|
|
775
780
|
|
776
|
-
|
781
|
+
#### I18n Translation Validation
|
777
782
|
|
778
783
|
The `verify_ui_states` method also supports I18n string translations using property/I18n key name pairs:
|
779
784
|
|
780
|
-
object => { property: { translate_key:
|
785
|
+
object => { property: { translate_key: 'name of key in I18n compatible .yml file' } }
|
781
786
|
|
782
787
|
**I18n Translation Keys:**
|
783
788
|
|
784
|
-
:translate String
|
785
|
-
:translate_upcase String
|
786
|
-
:translate_downcase String
|
787
|
-
:translate_capitalize String
|
788
|
-
translate_titlecase
|
789
|
+
:translate String
|
790
|
+
:translate_upcase String
|
791
|
+
:translate_downcase String
|
792
|
+
:translate_capitalize String
|
793
|
+
:translate_titlecase String
|
789
794
|
|
790
795
|
The example below depicts the usage of the `verify_ui_states` method to verify that the captions for menu items are correctly
|
791
796
|
translated.
|
@@ -804,27 +809,46 @@ translated.
|
|
804
809
|
verify_ui_states(ui)
|
805
810
|
end
|
806
811
|
|
807
|
-
|
808
|
-
|
812
|
+
Each supported language/locale combination has a corresponding `.yml` file. I18n `.yml` file naming convention uses
|
813
|
+
[ISO-639 language codes and ISO-3166 country codes](https://docs.oracle.com/cd/E13214_01/wli/docs92/xref/xqisocodes.html). For example:
|
809
814
|
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
815
|
+
| Language (Country) | File name |
|
816
|
+
|-----------------------|-----------|
|
817
|
+
| English | en.yml |
|
818
|
+
| English (Canada) | en-CA.yml |
|
819
|
+
| French (Canada) | fr-CA.yml |
|
820
|
+
| French | fr.yml |
|
821
|
+
| Spanish | es.yml |
|
822
|
+
| German | de.yml |
|
823
|
+
| Portuguese (Brazil) | pt-BR.yml |
|
824
|
+
| Portuguese (Portugal) | pt-PT.yml |
|
818
825
|
|
819
826
|
I18n `.yml` files contain key/value pairs representing the name of a translated string (key) and the string value.
|
820
827
|
|
828
|
+
Baseline translation strings are stored in `.yml` files in the `config/locales/` folder.
|
829
|
+
|
830
|
+
my_automation_project
|
831
|
+
├── config
|
832
|
+
│ ├── locales
|
833
|
+
│ │ ├── en.yml
|
834
|
+
│ │ ├── es.yml
|
835
|
+
│ │ ├── fr.yml
|
836
|
+
│ │ ├── fr-CA.yml
|
837
|
+
│ │ └── en-AU.yml
|
838
|
+
│ ├── test_data
|
839
|
+
│ └── cucumber.yml
|
840
|
+
├── downloads
|
841
|
+
├── features
|
842
|
+
├── Gemfile
|
843
|
+
└── README.md
|
821
844
|
|
822
845
|
|
823
|
-
## Instantiating your Page Objects
|
824
846
|
|
825
|
-
|
826
|
-
|
827
|
-
|
847
|
+
## Instantiating your PageObjects
|
848
|
+
|
849
|
+
Before you can call the methods in your `PageObjects` and `PageSections`, you must instantiate the `PageObjects` of your web
|
850
|
+
application, as well as create instance variables which can be used when calling a `PageObject`'s methods from your step definitions.
|
851
|
+
There are several ways to instantiate your `PageObjects`.
|
828
852
|
|
829
853
|
One common implementation is shown below:
|
830
854
|
|
@@ -851,14 +875,14 @@ One common implementation is shown below:
|
|
851
875
|
The `WorldPages` module above can be defined in your `env.rb` file, or you can define it in a separate `world_pages.rb` file in the
|
852
876
|
`features/support` folder.
|
853
877
|
|
854
|
-
While this approach is effective for small web applications with only a few pages (and hence few
|
855
|
-
cumbersome to manage if your web application has dozens of
|
878
|
+
While this approach is effective for small web applications with only a few pages (and hence few `PageObjects`), it quickly becomes
|
879
|
+
cumbersome to manage if your web application has dozens of `PageObjects` that need to be instantiated and managed.
|
856
880
|
|
857
881
|
### Using the PageManager
|
858
882
|
|
859
|
-
The
|
860
|
-
the `page_objects` method contains a hash table of your
|
861
|
-
|
883
|
+
The `PageManager` class provides methods for supporting the instantiation and management of `PageObjects`. In the code example below,
|
884
|
+
the `page_objects` method contains a hash table of your `PageObject` instances and their associated `PageObject` class names to be
|
885
|
+
instantiated by `PageManager`:
|
862
886
|
|
863
887
|
module WorldPages
|
864
888
|
def page_objects
|
@@ -888,13 +912,13 @@ to be instantiated by **PageManager**:
|
|
888
912
|
|
889
913
|
The `WorldPages` module above should be defined in the `world_pages.rb` file in the `features/support` folder.
|
890
914
|
|
891
|
-
Include the code below in your `env.rb` file to ensure that your
|
915
|
+
Include the code below in your `env.rb` file to ensure that your `PageObjects` are instantiated before your Cucumber scenarios are
|
892
916
|
executed:
|
893
917
|
|
894
918
|
include WorldPages
|
895
919
|
WorldPages.instantiate_page_objects
|
896
920
|
|
897
|
-
**NOTE:** If you intend to use the
|
921
|
+
**NOTE:** If you intend to use the `PageManager`, you must define a `page_name` trait for each of the `PageObjects` to be registered.
|
898
922
|
|
899
923
|
|
900
924
|
### Leveraging the PageManager in your Cucumber tests
|
@@ -956,14 +980,13 @@ In the above example, the step definitions associated with the 3 steps might be
|
|
956
980
|
end
|
957
981
|
|
958
982
|
|
983
|
+
While this approach may be effective for small web applications with only a few pages (and hence few `PageObjects`), it quickly becomes
|
984
|
+
cumbersome to manage if your web application has dozens of `PageObjects` that need to be managed.
|
959
985
|
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
The **PageManager** class provides a `find_page` method that replaces the cumbersome and difficult to maintain `case` statement used in the
|
964
|
-
above example. The **PageManager** `current_page` method allows you to set or get an instance of the currently active Page Object.
|
986
|
+
The `PageManager` class provides a `find_page` method that replaces the cumbersome and difficult to maintain `case` statement used in the
|
987
|
+
above example. The `PageManager.current_page` method allows you to set or get an instance of the currently active Page Object.
|
965
988
|
|
966
|
-
To use these
|
989
|
+
To use these `PageManager` methods, include the step definitions and code below in a `page_steps.rb` or `generic_steps.rb` file in the
|
967
990
|
`features/step_definitions` folder:
|
968
991
|
|
969
992
|
include TestCentricity
|
@@ -1033,16 +1056,13 @@ To maximize a desktop browser window, you set the `BROWSER_SIZE` Environment Var
|
|
1033
1056
|
|
1034
1057
|
#### Testing file downloads with desktop browsers
|
1035
1058
|
|
1036
|
-
File download functionality can be tested with locally hosted instances of Chrome or Firefox desktop browsers. Your automation project must include
|
1059
|
+
File download functionality can be tested with locally hosted instances of Chrome, Edge, or Firefox desktop browsers. Your automation project must include
|
1037
1060
|
a `/downloads` folder at the same level as the `/config` and `/features` folders, as depicted below:
|
1038
1061
|
|
1039
1062
|
my_automation_project
|
1040
1063
|
├── config
|
1041
|
-
│ └── test_data
|
1042
1064
|
├── downloads
|
1043
1065
|
├── features
|
1044
|
-
│ ├── step_definitions
|
1045
|
-
│ └── support
|
1046
1066
|
├── Gemfile
|
1047
1067
|
└── README.md
|
1048
1068
|
|
@@ -1053,15 +1073,12 @@ test thread. This is to ensure that files downloaded in each test thread are iso
|
|
1053
1073
|
|
1054
1074
|
my_automation_project
|
1055
1075
|
├── config
|
1056
|
-
│ └── test_data
|
1057
1076
|
├── downloads
|
1058
1077
|
│ ├── 1
|
1059
1078
|
│ ├── 2
|
1060
1079
|
│ ├── 3
|
1061
1080
|
│ └── 4
|
1062
1081
|
├── features
|
1063
|
-
│ ├── step_definitions
|
1064
|
-
│ └── support
|
1065
1082
|
├── Gemfile
|
1066
1083
|
└── README.md
|
1067
1084
|
|
@@ -1156,8 +1173,6 @@ of the Chrome desktop browser. The user specified device profiles must be locate
|
|
1156
1173
|
│ └── cucumber.yml
|
1157
1174
|
├── downloads
|
1158
1175
|
├── features
|
1159
|
-
│ ├── step_definitions
|
1160
|
-
│ └── support
|
1161
1176
|
├── Gemfile
|
1162
1177
|
└── README.md
|
1163
1178
|
|
@@ -1197,29 +1212,33 @@ The Appium server must be running prior to invoking Cucumber to run your feature
|
|
1197
1212
|
|
1198
1213
|
Once your test environment is properly configured, the following **Environment Variables** must be set as described in the table below.
|
1199
1214
|
|
1200
|
-
| **Environment Variable** | **Description**
|
1201
|
-
|
1202
|
-
| `WEB_BROWSER` | Must be set to `appium`
|
1203
|
-
| `APP_PLATFORM_NAME` | Must be set to `iOS`
|
1204
|
-
| `APP_BROWSER` | Must be set to `Safari`
|
1205
|
-
| `APP_VERSION` | Must be set to `
|
1206
|
-
| `APP_DEVICE` | Set to iOS device name supported by the iOS Simulator (`iPhone
|
1207
|
-
| `DEVICE_TYPE` | Must be set to `phone` or `tablet`
|
1208
|
-
| `APP_UDID` | UDID of physically connected iOS device (not used for simulators)
|
1209
|
-
| `TEAM_ID` | unique 10-character Apple developer team identifier string (not used for simulators)
|
1210
|
-
| `TEAM_NAME` | String representing a signing certificate (not used for simulators)
|
1211
|
-
| `APP_ALLOW_POPUPS` | [Optional] Allow javascript to open new windows in Safari. Set to `true` or `false`
|
1212
|
-
| `APP_IGNORE_FRAUD_WARNING` | [Optional] Prevent Safari from showing a fraudulent website warning. Set to `true` or `false`
|
1213
|
-
| `APP_NO_RESET` | [Optional] Don't reset app state after each test. Set to `true` or `false`
|
1214
|
-
| `APP_FULL_RESET` | [Optional] Perform a complete reset. Set to `true` or `false`
|
1215
|
-
| `APP_INITIAL_URL` | [Optional] Initial URL, default is a local welcome page. e.g. `http://www.apple.com`
|
1216
|
-
| `WDA_LOCAL_PORT` | [Optional] Used to forward traffic from Mac host to real iOS devices over USB. Default value is same as port number used by WDA on device.
|
1217
|
-
| `LOCALE` | [Optional] Locale to set for the simulator. e.g. `fr_CA`
|
1218
|
-
| `LANGUAGE` | [Optional] Language to set for the simulator. e.g. `fr`
|
1219
|
-
| `ORIENTATION` | [Optional] Set to `portrait` or `landscape` (only for iOS simulators)
|
1220
|
-
| `NEW_COMMAND_TIMEOUT` | [Optional] Time (in Seconds) that Appium will wait for a new command from the client
|
1221
|
-
| `SHOW_SIM_KEYBOARD` | [Optional] Show the simulator keyboard during text entry. Set to `true` or `false`
|
1222
|
-
| `SHUTDOWN_OTHER_SIMS` | [Optional] Close any other running simulators. Set to `true` or `false
|
1215
|
+
| **Environment Variable** | **Description** |
|
1216
|
+
|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
1217
|
+
| `WEB_BROWSER` | Must be set to `appium` |
|
1218
|
+
| `APP_PLATFORM_NAME` | Must be set to `iOS` |
|
1219
|
+
| `APP_BROWSER` | Must be set to `Safari` |
|
1220
|
+
| `APP_VERSION` | Must be set to `15.2`, `14.5`, or which ever iOS version you wish to run within the XCode Simulator |
|
1221
|
+
| `APP_DEVICE` | Set to iOS device name supported by the iOS Simulator (`iPhone 13 Pro Max`, `iPad Pro (12.9-inch) (5th generation)`, etc.) or name of physically connected iOS device |
|
1222
|
+
| `DEVICE_TYPE` | Must be set to `phone` or `tablet` |
|
1223
|
+
| `APP_UDID` | UDID of physically connected iOS device (not used for simulators) |
|
1224
|
+
| `TEAM_ID` | unique 10-character Apple developer team identifier string (not used for simulators) |
|
1225
|
+
| `TEAM_NAME` | String representing a signing certificate (not used for simulators) |
|
1226
|
+
| `APP_ALLOW_POPUPS` | [Optional] Allow javascript to open new windows in Safari. Set to `true` or `false` |
|
1227
|
+
| `APP_IGNORE_FRAUD_WARNING` | [Optional] Prevent Safari from showing a fraudulent website warning. Set to `true` or `false` |
|
1228
|
+
| `APP_NO_RESET` | [Optional] Don't reset app state after each test. Set to `true` or `false` |
|
1229
|
+
| `APP_FULL_RESET` | [Optional] Perform a complete reset. Set to `true` or `false` |
|
1230
|
+
| `APP_INITIAL_URL` | [Optional] Initial URL, default is a local welcome page. e.g. `http://www.apple.com` |
|
1231
|
+
| `WDA_LOCAL_PORT` | [Optional] Used to forward traffic from Mac host to real iOS devices over USB. Default value is same as port number used by WDA on device. |
|
1232
|
+
| `LOCALE` | [Optional] Locale to set for the simulator. e.g. `fr_CA` |
|
1233
|
+
| `LANGUAGE` | [Optional] Language to set for the simulator. e.g. `fr` |
|
1234
|
+
| `ORIENTATION` | [Optional] Set to `portrait` or `landscape` (only for iOS simulators) |
|
1235
|
+
| `NEW_COMMAND_TIMEOUT` | [Optional] Time (in Seconds) that Appium will wait for a new command from the client |
|
1236
|
+
| `SHOW_SIM_KEYBOARD` | [Optional] Show the simulator keyboard during text entry. Set to `true` or `false` |
|
1237
|
+
| `SHUTDOWN_OTHER_SIMS` | [Optional] Close any other running simulators. Set to `true` or `false`. See note below. |
|
1238
|
+
|
1239
|
+
The `SHUTDOWN_OTHER_SIMS` environment variable can only be set if you are running Appium Server with the `--relaxed-security` or
|
1240
|
+
`--allow-insecure=shutdown_other_sims` arguments passed when starting it from the command line, or when running the server from the
|
1241
|
+
Appium Server GUI app. A security violation error will occur without relaxed secutity enabled.
|
1223
1242
|
|
1224
1243
|
Refer to **section 8.7 (Using Browser specific Profiles in cucumber.yml)** below.
|
1225
1244
|
|
@@ -1487,7 +1506,7 @@ that you intend to connect with.
|
|
1487
1506
|
# NOTE: Requires installation of XCode, iOS version specific target simulators, Appium, and the appium_capybara gem
|
1488
1507
|
#==============
|
1489
1508
|
|
1490
|
-
appium_ios: WEB_BROWSER=appium AUTOMATION_ENGINE=XCUITest APP_PLATFORM_NAME="ios" APP_BROWSER="Safari" NEW_COMMAND_TIMEOUT=30
|
1509
|
+
appium_ios: WEB_BROWSER=appium AUTOMATION_ENGINE=XCUITest APP_PLATFORM_NAME="ios" APP_BROWSER="Safari" NEW_COMMAND_TIMEOUT=30 SHOW_SIM_KEYBOARD=false
|
1491
1510
|
app_ios_15: --profile appium_ios APP_VERSION="15.2"
|
1492
1511
|
ipad_pro_12_9_15_sim: --profile app_ios_15 DEVICE_TYPE=tablet APP_DEVICE="iPad Pro (12.9-inch) (5th generation)" <%= desktop %>
|
1493
1512
|
ipad_air_15_sim: --profile app_ios_15 DEVICE_TYPE=tablet APP_DEVICE="iPad Air (4th generation)" <%= desktop %>
|
@@ -1643,6 +1662,31 @@ landscape orientation running on the BrowserStack service:
|
|
1643
1662
|
|
1644
1663
|
|
1645
1664
|
|
1665
|
+
## Recommended Project Organization and Structure
|
1666
|
+
|
1667
|
+
Below is an example of the project structure of a typical Cucumber based test automation framework with a Page Object Model
|
1668
|
+
architecture. `PageObject` class definitions should be stored in the `/features/support/pages` folder, organized in functional
|
1669
|
+
area sub-folders as needed. Likewise, `PageSection` class definitions should be stored in the `/features/support/sections` folder.
|
1670
|
+
|
1671
|
+
my_automation_project
|
1672
|
+
├── config
|
1673
|
+
│ ├── locales
|
1674
|
+
│ ├── test_data
|
1675
|
+
│ └── cucumber.yml
|
1676
|
+
├── downloads
|
1677
|
+
├── features
|
1678
|
+
│ ├── step_definitions
|
1679
|
+
│ └── support
|
1680
|
+
│ │ ├── pages
|
1681
|
+
│ │ ├── sections
|
1682
|
+
│ │ ├── env.rb
|
1683
|
+
│ │ ├── hooks.rb
|
1684
|
+
│ │ └── world_pages.rb
|
1685
|
+
├── Gemfile
|
1686
|
+
└── README.md
|
1687
|
+
|
1688
|
+
|
1689
|
+
|
1646
1690
|
## Web Test Automation Framework Implementation
|
1647
1691
|
|
1648
1692
|
<img src="https://i.imgur.com/eukmEan.jpg" alt="TestCentricity Web Framework Overview" title="TestCentricity Web Framework Overview">
|
@@ -26,7 +26,7 @@ module TestCentricity
|
|
26
26
|
puts 'Appium Server is starting'
|
27
27
|
end
|
28
28
|
# start new Appium Server
|
29
|
-
@process = ChildProcess.build
|
29
|
+
@process = ChildProcess.build(*parameters)
|
30
30
|
process.start
|
31
31
|
# wait until confirmation that Appium Server is running
|
32
32
|
wait = Selenium::WebDriver::Wait.new(timeout: 30)
|
@@ -40,7 +40,7 @@ module TestCentricity
|
|
40
40
|
def running?
|
41
41
|
response = nil
|
42
42
|
begin
|
43
|
-
response = Net::HTTP.get_response(URI('http://
|
43
|
+
response = Net::HTTP.get_response(URI('http://0.0.0.0:4723/wd/hub/sessions'))
|
44
44
|
rescue
|
45
45
|
end
|
46
46
|
response && response.code_type == Net::HTTPOK
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: testcentricity_web
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.1.
|
4
|
+
version: 4.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- A.J. Mrozinski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
11
|
+
date: 2022-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -258,7 +258,6 @@ files:
|
|
258
258
|
- CHANGELOG.md
|
259
259
|
- CODE_OF_CONDUCT.md
|
260
260
|
- Gemfile
|
261
|
-
- Gemfile.lock
|
262
261
|
- LICENSE.md
|
263
262
|
- README.md
|
264
263
|
- Rakefile
|
data/Gemfile.lock
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
testcentricity_web (4.1.2.1)
|
5
|
-
appium_lib
|
6
|
-
browserstack-local
|
7
|
-
capybara (>= 3.1, < 4)
|
8
|
-
childprocess
|
9
|
-
chronic (= 0.10.2)
|
10
|
-
faker
|
11
|
-
i18n
|
12
|
-
os (~> 1.0)
|
13
|
-
selenium-webdriver (>= 4.0, < 5)
|
14
|
-
spreadsheet (= 1.1.7)
|
15
|
-
test-unit
|
16
|
-
virtus
|
17
|
-
webdrivers (~> 5.0)
|
18
|
-
|
19
|
-
GEM
|
20
|
-
remote: https://rubygems.org/
|
21
|
-
specs:
|
22
|
-
addressable (2.8.0)
|
23
|
-
public_suffix (>= 2.0.2, < 5.0)
|
24
|
-
appium_lib (12.0.0)
|
25
|
-
appium_lib_core (~> 5.0.0)
|
26
|
-
nokogiri (~> 1.8, >= 1.8.1)
|
27
|
-
tomlrb (>= 1.1, < 3.0)
|
28
|
-
appium_lib_core (5.0.3)
|
29
|
-
faye-websocket (~> 0.11.0)
|
30
|
-
selenium-webdriver (~> 4.0)
|
31
|
-
axiom-types (0.1.1)
|
32
|
-
descendants_tracker (~> 0.0.4)
|
33
|
-
ice_nine (~> 0.11.0)
|
34
|
-
thread_safe (~> 0.3, >= 0.3.1)
|
35
|
-
browserstack-local (1.3.0)
|
36
|
-
capybara (3.36.0)
|
37
|
-
addressable
|
38
|
-
matrix
|
39
|
-
mini_mime (>= 0.1.3)
|
40
|
-
nokogiri (~> 1.8)
|
41
|
-
rack (>= 1.6.0)
|
42
|
-
rack-test (>= 0.6.3)
|
43
|
-
regexp_parser (>= 1.5, < 3.0)
|
44
|
-
xpath (~> 3.2)
|
45
|
-
childprocess (4.1.0)
|
46
|
-
chronic (0.10.2)
|
47
|
-
coercible (1.0.0)
|
48
|
-
descendants_tracker (~> 0.0.1)
|
49
|
-
concurrent-ruby (1.1.9)
|
50
|
-
descendants_tracker (0.0.4)
|
51
|
-
thread_safe (~> 0.3, >= 0.3.1)
|
52
|
-
eventmachine (1.2.7)
|
53
|
-
faker (2.20.0)
|
54
|
-
i18n (>= 1.8.11, < 2)
|
55
|
-
faye-websocket (0.11.1)
|
56
|
-
eventmachine (>= 0.12.0)
|
57
|
-
websocket-driver (>= 0.5.1)
|
58
|
-
i18n (1.10.0)
|
59
|
-
concurrent-ruby (~> 1.0)
|
60
|
-
ice_nine (0.11.2)
|
61
|
-
matrix (0.4.2)
|
62
|
-
mini_mime (1.1.2)
|
63
|
-
nokogiri (1.13.3-x86_64-darwin)
|
64
|
-
racc (~> 1.4)
|
65
|
-
os (1.1.4)
|
66
|
-
power_assert (2.0.1)
|
67
|
-
public_suffix (4.0.6)
|
68
|
-
racc (1.6.0)
|
69
|
-
rack (2.2.3)
|
70
|
-
rack-test (1.1.0)
|
71
|
-
rack (>= 1.0, < 3)
|
72
|
-
rake (13.0.6)
|
73
|
-
redcarpet (3.5.1)
|
74
|
-
regexp_parser (2.2.1)
|
75
|
-
rexml (3.2.5)
|
76
|
-
ruby-ole (1.2.12.2)
|
77
|
-
rubyzip (2.3.2)
|
78
|
-
selenium-webdriver (4.1.0)
|
79
|
-
childprocess (>= 0.5, < 5.0)
|
80
|
-
rexml (~> 3.2, >= 3.2.5)
|
81
|
-
rubyzip (>= 1.2.2)
|
82
|
-
spreadsheet (1.1.7)
|
83
|
-
ruby-ole (>= 1.0)
|
84
|
-
test-unit (3.5.3)
|
85
|
-
power_assert
|
86
|
-
thread_safe (0.3.6)
|
87
|
-
tomlrb (2.0.1)
|
88
|
-
virtus (2.0.0)
|
89
|
-
axiom-types (~> 0.1)
|
90
|
-
coercible (~> 1.0)
|
91
|
-
descendants_tracker (~> 0.0, >= 0.0.3)
|
92
|
-
webdrivers (5.0.0)
|
93
|
-
nokogiri (~> 1.6)
|
94
|
-
rubyzip (>= 1.3.0)
|
95
|
-
selenium-webdriver (~> 4.0)
|
96
|
-
websocket-driver (0.7.5)
|
97
|
-
websocket-extensions (>= 0.1.0)
|
98
|
-
websocket-extensions (0.1.5)
|
99
|
-
xpath (3.2.0)
|
100
|
-
nokogiri (~> 1.8)
|
101
|
-
|
102
|
-
PLATFORMS
|
103
|
-
x86_64-darwin-21
|
104
|
-
|
105
|
-
DEPENDENCIES
|
106
|
-
bundler
|
107
|
-
rake
|
108
|
-
redcarpet
|
109
|
-
testcentricity_web!
|
110
|
-
|
111
|
-
BUNDLED WITH
|
112
|
-
2.2.1
|