appium_lib_core 5.1.0 → 5.2.0

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: 15447eabf9741951c87185aa6844f1b7acfab0bbcef6e70061ccebbd423dc00b
4
- data.tar.gz: a135b4d1b7b4cfd49cce6a684a98fcb508a5a7f8bf770c35b2c323ac74a4f5e2
3
+ metadata.gz: 9c40ff7aa2c8ad3b9c93cd6e9997aa98bbe62d5118184ce58a802edf7c731ec2
4
+ data.tar.gz: 78eb1cf6946e248a60f0f7b598ab7274add5bbc502f247534ba0d65f14078648
5
5
  SHA512:
6
- metadata.gz: 424e4f1bf4e4fa14bf5582cf74ff32cc248ab430878900bb154ed4a12ad089a84c6c087cee9f2db352ed2292e781c278510d8cb4c86b9582e9af4eb56a705e0f
7
- data.tar.gz: 7d95d25ff6b670c3bd0a2ac6a295a4ef085bbde7479a40fdb0fece4100d1e2a76ba3ab814b0225de007f748f1c6d0f3c0d7b36df1b4010a11dad3951570d90b9
6
+ metadata.gz: eecd7b626412b0103ee2bd15ed684d22264a56eac4469adf2b0b3f8ad659c753ba17cbb2009b9ac71168f12636f41aa251962a7889d7bae4673a697c1bce0e63
7
+ data.tar.gz: cc0f29f03d38beca513472101b19dbf36031a3622aa6cd6edb29c3b296a09b1577ffe6d8fc74a0e7766e10eb21dc60a805f977735b47edd088f9934d4b463960
data/CHANGELOG.md CHANGED
@@ -10,6 +10,27 @@ Read `release_notes.md` for commit level details.
10
10
 
11
11
  ### Deprecations
12
12
 
13
+ ## [5.2.0] - 2022-06-19
14
+
15
+ ### Enhancements
16
+
17
+ - Update base Selenium Ruby client to `4.2+`
18
+ - Please call `key_action` or set `[::Selenium::WebDriver::Interactions.key('keyboard')]` as the deviecs parameter to build `driver.send_keys().perform` W3C action.
19
+ - `driver.key_action.send_keys('xxx').perform` instead of `driver.action.send_keys('xxx').perform`, or `driver.action(devices: [::Selenium::WebDriver::Interactions.key('keyboard')]).send_keys('xxx').perform`.
20
+ - [This PR change](https://github.com/appium/ruby_lib_core/pull/381/files) will help to update your some actions.
21
+
22
+ ## [5.1.2] - 2022-06-02
23
+
24
+ ### Enhancements
25
+
26
+ - Limit selenium webdriver version less than `4.2`
27
+
28
+ ## [5.1.1] - 2022-04-18
29
+
30
+ ### Enhancements
31
+
32
+ - Remove unneccessry files from gem package to reduce the size.
33
+
13
34
  ## [5.1.0] - 2021-12-25
14
35
 
15
36
  ### Enhancements
data/README.md CHANGED
@@ -43,7 +43,7 @@ $ appium --relaxed-security # To run all tests in local
43
43
  ```bash
44
44
  $ bundle install
45
45
  $ rake test:func:android # Andorid, uiautomator2
46
- $ AUTOMATION_NAME_DROID=espresso rake test:func:android # Andorid, uiautomator2
46
+ $ APPIUM_DRIVER=espresso rake test:func:android # Andorid, uiautomator2
47
47
  $ rake test:func:ios # iOS
48
48
  ```
49
49
 
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'appium_lib_core/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.required_ruby_version = '>= 2.6'
7
+ spec.required_ruby_version = '>= 2.7'
8
8
 
9
9
  spec.name = 'appium_lib_core'
10
10
  spec.version = Appium::Core::VERSION
@@ -16,13 +16,13 @@ Gem::Specification.new do |spec|
16
16
  spec.homepage = 'https://github.com/appium/ruby_lib_core/'
17
17
  spec.license = 'Apache-2.0'
18
18
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
- f.match(%r{^(doc|test|spec|features)/})
19
+ f.match(%r{(^(doc|docs|test|spec|features|ci-jobs|.github|script)/|release_notes.md|.gitignore|azure-pipelines.yml|.rubocop.yml)})
20
20
  end
21
21
  spec.bindir = 'exe'
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ['lib']
24
24
 
25
- spec.add_runtime_dependency 'selenium-webdriver', '~> 4.0'
25
+ spec.add_runtime_dependency 'selenium-webdriver', '~> 4.2'
26
26
  spec.add_runtime_dependency 'faye-websocket', '~> 0.11.0'
27
27
 
28
28
  spec.add_development_dependency 'rake', '~> 13.0'
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.add_development_dependency 'minitest', '~> 5.0'
31
31
  spec.add_development_dependency 'minitest-reporters', '~> 1.1'
32
32
  spec.add_development_dependency 'webmock', '~> 3.14.0'
33
- spec.add_development_dependency 'rubocop', '1.24.0'
33
+ spec.add_development_dependency 'rubocop', '1.30.1'
34
34
  spec.add_development_dependency 'appium_thor', '~> 1.0'
35
35
  spec.add_development_dependency 'parallel_tests'
36
36
  spec.add_development_dependency 'simplecov'
@@ -153,30 +153,26 @@ module Appium
153
153
  execute :status
154
154
  end
155
155
 
156
- # Perform touch actions for W3C module.
156
+ # Perform 'touch' actions for W3C module.
157
157
  # Generate +touch+ pointer action here and users can use this via +driver.action+
158
158
  # - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/W3CActionBuilder.html
159
159
  # - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/PointerActions.html
160
160
  # - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/KeyActions.html
161
161
  #
162
- # The pointer type is 'touch' by default in the Appium Ruby client. (The selenium one is 'mouse')
162
+ # The pointer type is 'touch' by default in the Appium Ruby client.
163
163
  #
164
164
  # @example
165
165
  #
166
166
  # element = @driver.find_element(:id, "some id")
167
167
  # @driver.action.click(element).perform # The 'click' is a part of 'PointerActions'
168
168
  #
169
- def action(async = false)
170
- action_builder = ::Selenium::WebDriver::ActionBuilder.new(
169
+ def action(_deprecated_async = nil, async: false, devices: nil)
170
+ ::Selenium::WebDriver::ActionBuilder.new(
171
171
  self,
172
- ::Selenium::WebDriver::Interactions.pointer(:touch, name: 'touch'),
173
- ::Selenium::WebDriver::Interactions.key('keyboard'),
174
- async
172
+ devices: devices || [::Selenium::WebDriver::Interactions.pointer(:touch, name: 'touch')],
173
+ async: async,
174
+ duration: 50 # milliseconds
175
175
  )
176
- # Used for default duration of each touch actions.
177
- # Override from 250 milliseconds to 50 milliseconds in PointerActions included by ::Selenium::WebDriver::ActionBuilder
178
- action_builder.default_move_duration = 0.05
179
- action_builder
180
176
  end
181
177
 
182
178
  # Port from MJSONWP
@@ -189,12 +185,6 @@ module Appium
189
185
  ::Selenium::WebDriver::Remote::Capabilities.json_create execute(:get_capabilities)
190
186
  end
191
187
 
192
- # Port from MJSONWP
193
- def send_keys_to_active_element(key)
194
- text = ::Selenium::WebDriver::Keys.encode(key).join
195
- execute :send_keys_to_active_element, {}, { value: text.chars }
196
- end
197
-
198
188
  # For Appium
199
189
  # override
200
190
  def page_source
@@ -181,6 +181,31 @@ module Appium
181
181
 
182
182
  ### Methods for Appium
183
183
 
184
+ # Perform 'key' actions for W3C module.
185
+ # Generate +key+ pointer action here and users can use this via +driver.key_action+
186
+ # - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/W3CActionBuilder.html
187
+ # - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/KeyActions.html
188
+ #
189
+ # The pointer type is 'key' by default in the Appium Ruby client.
190
+ # +driver.action+ in Appium Ruby client has 'pointer' action by default.
191
+ # This method is a shortcut to set 'key' type.
192
+ # Hense this method is equal to +driver.action(devices: [::Selenium::WebDriver::Interactions.key('keyboard')])+
193
+ # as below example.
194
+ #
195
+ # @example
196
+ #
197
+ # element = @driver.find_element(:id, "some id")
198
+ # @driver.key_action.send_key('hiあ').perform # The 'send_key' is a part of 'KeyActions'
199
+ # # is equal to:
200
+ # # @driver.action(devices: [::Selenium::WebDriver::Interactions.key('keyboard')]).send_keys('hiあ').perform
201
+ #
202
+ def key_action(async: false)
203
+ @bridge.action(
204
+ async: async,
205
+ devices: [::Selenium::WebDriver::Interactions.key('keyboard')]
206
+ )
207
+ end
208
+
184
209
  # Lock the device
185
210
  # @return [String]
186
211
  #
@@ -171,8 +171,6 @@ module Appium
171
171
  ime_deactivate: [:post, 'session/:session_id/ime/deactivate'],
172
172
  ime_activate_engine: [:post, 'session/:session_id/ime/activate'],
173
173
 
174
- send_keys_to_active_element: [:post, 'session/:session_id/keys'],
175
-
176
174
  ### Logs
177
175
  get_available_log_types: [:get, 'session/:session_id/log/types'],
178
176
  get_log: [:post, 'session/:session_id/log'],
@@ -76,6 +76,13 @@ module Appium
76
76
  path: 'directConnectPath'
77
77
  }.freeze
78
78
 
79
+ W3C_KEYS = {
80
+ protocol: 'appium:directConnectProtocol',
81
+ host: 'appium:directConnectHost',
82
+ port: 'appium:directConnectPort',
83
+ path: 'appium:directConnectPath'
84
+ }.freeze
85
+
79
86
  # @return [string] Returns a protocol such as http/https
80
87
  attr_reader :protocol
81
88
 
@@ -89,10 +96,10 @@ module Appium
89
96
  attr_reader :path
90
97
 
91
98
  def initialize(capabilities)
92
- @protocol = capabilities[KEYS[:protocol]]
93
- @host = capabilities[KEYS[:host]]
94
- @port = capabilities[KEYS[:port]]
95
- @path = capabilities[KEYS[:path]]
99
+ @protocol = capabilities[W3C_KEYS[:protocol]] || capabilities[KEYS[:protocol]]
100
+ @host = capabilities[W3C_KEYS[:host]] || capabilities[KEYS[:host]]
101
+ @port = capabilities[W3C_KEYS[:port]] || capabilities[KEYS[:port]]
102
+ @path = capabilities[W3C_KEYS[:path]] || capabilities[KEYS[:path]]
96
103
  end
97
104
  end
98
105
 
@@ -175,7 +182,8 @@ module Appium
175
182
  # - <code>directConnectPort</code>
176
183
  # - <code>directConnectPath</code>
177
184
  #
178
- # Ignore them if this parameter is <code>false</code>. Defaults to true.
185
+ # ignore them if this parameter is <code>false</code>. Defaults to true.
186
+ # These keys can have <code>appium:</code> prefix.
179
187
  #
180
188
  # @return [Bool]
181
189
  attr_reader :direct_connect
@@ -14,7 +14,7 @@
14
14
 
15
15
  module Appium
16
16
  module Core
17
- VERSION = '5.1.0' unless defined? ::Appium::Core::VERSION
18
- DATE = '2021-12-25' unless defined? ::Appium::Core::DATE
17
+ VERSION = '5.2.0' unless defined? ::Appium::Core::VERSION
18
+ DATE = '2022-06-19' unless defined? ::Appium::Core::DATE
19
19
  end
20
20
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appium_lib_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kazuaki MATSUO
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-26 00:00:00.000000000 Z
11
+ date: 2022-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: selenium-webdriver
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '4.0'
19
+ version: '4.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '4.0'
26
+ version: '4.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: faye-websocket
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -114,14 +114,14 @@ dependencies:
114
114
  requirements:
115
115
  - - '='
116
116
  - !ruby/object:Gem::Version
117
- version: 1.24.0
117
+ version: 1.30.1
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - '='
123
123
  - !ruby/object:Gem::Version
124
- version: 1.24.0
124
+ version: 1.30.1
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: appium_thor
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -171,14 +171,6 @@ executables: []
171
171
  extensions: []
172
172
  extra_rdoc_files: []
173
173
  files:
174
- - ".github/ISSUE_TEMPLATE/issue-report.md"
175
- - ".github/contributing.md"
176
- - ".github/dependabot.yml"
177
- - ".github/issue_template.md"
178
- - ".github/workflows/codeql-analysis.yml"
179
- - ".github/workflows/unittest.yml"
180
- - ".gitignore"
181
- - ".rubocop.yml"
182
174
  - CHANGELOG.md
183
175
  - Gemfile
184
176
  - LICENSE.txt
@@ -186,16 +178,8 @@ files:
186
178
  - Rakefile
187
179
  - Thorfile
188
180
  - appium_lib_core.gemspec
189
- - azure-pipelines.yml
190
181
  - bin/console
191
182
  - bin/setup
192
- - ci-jobs/functional/android_setup.yml
193
- - ci-jobs/functional/ios_setup.yml
194
- - ci-jobs/functional/publish_test_result.yml
195
- - ci-jobs/functional/run_appium.yml
196
- - ci-jobs/functional/start-emulator.sh
197
- - ci-jobs/functional_test.yml
198
- - docs/mobile_command.md
199
183
  - lib/appium_lib_core.rb
200
184
  - lib/appium_lib_core/android.rb
201
185
  - lib/appium_lib_core/android/device.rb
@@ -278,8 +262,6 @@ files:
278
262
  - lib/appium_lib_core/windows/bridge.rb
279
263
  - lib/appium_lib_core/windows/device.rb
280
264
  - lib/appium_lib_core/windows/device/screen.rb
281
- - release_notes.md
282
- - script/commands.rb
283
265
  homepage: https://github.com/appium/ruby_lib_core/
284
266
  licenses:
285
267
  - Apache-2.0
@@ -292,14 +274,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
292
274
  requirements:
293
275
  - - ">="
294
276
  - !ruby/object:Gem::Version
295
- version: '2.6'
277
+ version: '2.7'
296
278
  required_rubygems_version: !ruby/object:Gem::Requirement
297
279
  requirements:
298
280
  - - ">="
299
281
  - !ruby/object:Gem::Version
300
282
  version: '0'
301
283
  requirements: []
302
- rubygems_version: 3.2.15
284
+ rubygems_version: 3.3.3
303
285
  signing_key:
304
286
  specification_version: 4
305
287
  summary: Minimal Ruby library for Appium.
@@ -1,29 +0,0 @@
1
- ---
2
- name: issue report
3
- about: Create a report to help us improve
4
- title: ''
5
- labels: ''
6
- assignees: ''
7
-
8
- ---
9
-
10
- ## This is a
11
- * [ ] Bug report
12
- * [ ] Question
13
- * [ ] Feature Request
14
-
15
- ## Summary
16
-
17
- ## Environment
18
- * `ruby_lib_core` version:
19
- * Mobile platform/version/device under test:
20
-
21
- ## Actual behaviour and steps to reproduce
22
-
23
- ## Expected behaviour
24
-
25
- ## Link to Appium/Ruby logs
26
-
27
- Create a [GIST](https://gist.github.com) which is a paste of your _full_ Appium logs, and link them here.
28
-
29
- ## Any additional comments
@@ -1,26 +0,0 @@
1
- # Contributing
2
-
3
- For features or bug fixes, please submit a pull request. Ideally there would be a test as well. The remainder of this doc details how to run the tests.
4
-
5
- ## Tests
6
- ### Unit Tests
7
- - `rake test:unit` run on Travis automatically
8
-
9
- ### Functional Tests
10
- - iOS
11
- - `rake test:func:ios`
12
- - Android
13
- - `rake test:func:android`
14
-
15
- ## Merge
16
- - Squash and merge when we merge PRs to the master
17
-
18
- ## Publishing on rubygems
19
-
20
- 0. Ensure you have `~/.gem/credentials` If not run [the following command](http://guides.rubygems.org/make-your-own-gem/) (replace username with your rubygems user)
21
- > $ curl -u username https://rubygems.org/api/v1/api_key.yaml >
22
- ~/.gem/credentials; chmod 0600 ~/.gem/credentials
23
-
24
- 1. Bump the version number `thor bump`
25
- 2. Generate release note, build and publish gem with `thor release`
26
- 3. Update `changelog.md`
@@ -1,8 +0,0 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: bundler
4
- directory: "/"
5
- schedule:
6
- interval: daily
7
- time: "11:00"
8
- open-pull-requests-limit: 10
@@ -1,20 +0,0 @@
1
- ## This is a
2
- * [ ] Bug report
3
- * [ ] Question
4
- * [ ] Feature Request
5
-
6
- ## Summary
7
-
8
- ## Environment
9
- * `ruby_lib_core` version:
10
- * Mobile platform/version/device under test:
11
-
12
- ## Actual behaviour and steps to reproduce
13
-
14
- ## Expected behaviour
15
-
16
- ## Link to Appium/Ruby logs
17
-
18
- Create a [GIST](https://gist.github.com) which is a paste of your _full_ Appium logs, and link them here.
19
-
20
- ## Any additional comments
@@ -1,70 +0,0 @@
1
- # For most projects, this workflow file will not need changing; you simply need
2
- # to commit it to your repository.
3
- #
4
- # You may wish to alter this file to override the set of languages analyzed,
5
- # or to provide custom queries or build logic.
6
- #
7
- # ******** NOTE ********
8
- # We have attempted to detect the languages in your repository. Please check
9
- # the `language` matrix defined below to confirm you have the correct set of
10
- # supported CodeQL languages.
11
- #
12
- name: "CodeQL"
13
-
14
- on:
15
- push:
16
- branches: [ master ]
17
- pull_request:
18
- # The branches below must be a subset of the branches above
19
- branches: [ master ]
20
- schedule:
21
- - cron: '39 12 * * 0'
22
-
23
- jobs:
24
- analyze:
25
- name: Analyze
26
- runs-on: ubuntu-latest
27
- permissions:
28
- actions: read
29
- contents: read
30
- security-events: write
31
-
32
- strategy:
33
- fail-fast: false
34
- matrix:
35
- language: [ 'ruby' ]
36
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37
- # Learn more about CodeQL language support at https://git.io/codeql-language-support
38
-
39
- steps:
40
- - name: Checkout repository
41
- uses: actions/checkout@v2
42
-
43
- # Initializes the CodeQL tools for scanning.
44
- - name: Initialize CodeQL
45
- uses: github/codeql-action/init@v1
46
- with:
47
- languages: ${{ matrix.language }}
48
- # If you wish to specify custom queries, you can do so here or in a config file.
49
- # By default, queries listed here will override any specified in a config file.
50
- # Prefix the list here with "+" to use these queries and those in the config file.
51
- # queries: ./path/to/local/query, your-org/your-repo/queries@main
52
-
53
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54
- # If this step fails, then you should remove it and run the build manually (see below)
55
- - name: Autobuild
56
- uses: github/codeql-action/autobuild@v1
57
-
58
- # ℹ️ Command-line programs to run using the OS shell.
59
- # 📚 https://git.io/JvXDl
60
-
61
- # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62
- # and modify them (or add more) to build your code if your project
63
- # uses a compiled language
64
-
65
- #- run: |
66
- # make bootstrap
67
- # make release
68
-
69
- - name: Perform CodeQL Analysis
70
- uses: github/codeql-action/analyze@v1
@@ -1,67 +0,0 @@
1
- name: Runs lint and unit tests
2
-
3
- on:
4
- push:
5
- branches: [ master ]
6
- pull_request:
7
- branches: [ master ]
8
-
9
- jobs:
10
- test:
11
- strategy:
12
- fail-fast: false
13
- matrix:
14
- ruby: [2.6, 2.7, 3.0, 3.1]
15
-
16
- runs-on: ubuntu-latest
17
-
18
- env:
19
- UNIT_TEST: true
20
-
21
- steps:
22
- - uses: actions/checkout@v2
23
- - name: Set up Ruby
24
- uses: ruby/setup-ruby@v1
25
- with:
26
- ruby-version: ${{ matrix.ruby }}
27
- - name: Install dependencies
28
- run: bundle install
29
- - name: Run tests
30
- run: |
31
- bundle exec rake rubocop
32
- bundle exec rake test:unit
33
- AUTOMATION_NAME_DROID=espresso bundle exec rake test:unit:android
34
- AUTOMATION_NAME_DROID=appium AUTOMATION_NAME_IOS=appium bundle exec rake test:unit
35
-
36
- test-win:
37
- strategy:
38
- fail-fast: false
39
- matrix:
40
- ruby: [2.6, 2.7, 3.0]
41
-
42
- runs-on: windows-latest
43
-
44
- env:
45
- UNIT_TEST: true
46
-
47
- steps:
48
- - uses: actions/checkout@v2
49
- - name: Set up Ruby
50
- uses: ruby/setup-ruby@v1
51
- with:
52
- ruby-version: ${{ matrix.ruby }}
53
- - name: Install dependencies
54
- run: |
55
- gem install ffi
56
- bundle install
57
- gem uninstall --force eventmachine && gem install eventmachine --platform ruby
58
- - name: Run tests
59
- run: |
60
- rake test:unit
61
-
62
- setx AUTOMATION_NAME_DROID espresso
63
- rake test:unit:android
64
-
65
- setx AUTOMATION_NAME_DROID appium
66
- setx AUTOMATION_NAME_IOS appium
67
- rake test:unit
data/.gitignore DELETED
@@ -1,18 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /docs/
8
- /pkg/
9
- /spec/reports/
10
- /tmp/
11
-
12
- # for Mac
13
- .DS_Store
14
-
15
- # for IDE
16
- .idea
17
-
18
- .coverage