motion-juxtapose 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +59 -2
- data/lib/juxtapose/application/js/app.js +7 -1
- data/lib/juxtapose/capybara.rb +5 -0
- data/lib/juxtapose/rspec.rb +7 -0
- data/lib/juxtapose/screenshotter.rb +46 -12
- data/lib/juxtapose/strategy/capybara_strategy.rb +31 -0
- data/lib/juxtapose/version.rb +1 -1
- data/lib/motion-juxtapose.rb +1 -0
- data/spec/controllers/test_controller_spec.rb +2 -2
- data/spec/screens/iphone-retina/ios_7.1/screenshot-testing-under-bacon-passes-when-there-is-an-identical-accepted-screenshot/accepted-screenshot/accepted.png +0 -0
- data/spec/screens/iphone-retina/{ios_7.0.3/screenshot-testing-under-bacon-passes-when-there-is-an-identical-accepted-screenshot/accepted-screenshot → ios_7.1/screenshot-testing-under-bacon-raises-an-error-and-produces-diffs-on-failure/going-to-differ-screenshot}/accepted.png +0 -0
- data/spec/screens/iphone-retina/{ios_7.0.3 → ios_7.1}/screenshot-testing-under-bacon-raises-an-error-when-screens-are-different-sizes/different-sized-screenshot/accepted.png +0 -0
- metadata +26 -23
- data/spec/screens/iphone-retina/ios_7.0.3/screenshot-testing-under-bacon-raises-an-error-and-produces-diffs-on-failure/going-to-differ-screenshot/accepted.png +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a58dcf720096d73d57d3a904dee1bc216889ce5c
|
4
|
+
data.tar.gz: b54b20541785bfbcb8873f552021578e034ea12e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78e454b932c47f2f76cc27ba28e97c2016fe921f1b3febc6b05164236dd95af2ce5fc542b3a7198af7d3c96be7087e81be1e343b90118c79a83e619d9a6dd2b1
|
7
|
+
data.tar.gz: f2253d30b74513c06b7393734f7155be1c45c482d036328203e5d657d3a3a961596863d8b71a70f2446e703b04a0c5aa15ab5ee1ac2c27b106966daa32fe1347
|
data/README.md
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
# Juxtapose
|
2
2
|
|
3
|
-
Screenshot-driven assertions for testing RubyMotion applications.
|
3
|
+
Screenshot-driven assertions for testing Rails and RubyMotion applications.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
+
Motion-juxtapose uses [ImageMagick](http://www.imagemagick.org/) under the hood for image comparison. If you're using [Homebrew](http://brew.sh/), you can install it with:
|
8
|
+
|
9
|
+
brew install imagemagick
|
10
|
+
|
7
11
|
Add this line to your application's Gemfile:
|
8
12
|
|
9
13
|
gem 'motion-juxtapose'
|
@@ -22,13 +26,15 @@ Juxtapose's workflow generally goes:
|
|
22
26
|
|
23
27
|
1. Write a test that puts your app in the state you want to capture and take a screenshot (see below for how to do this in Bacon and Frank).
|
24
28
|
2. The first time this screenshot is captured, your test will fail with the error "No accepted screen shot".
|
25
|
-
3. You must provide Juxtapose an accepted screenshot to match against in future test runs. Run the juxtapose server with `bundle exec juxtapose` and browse to http://localhost:4567 — you should the just-captured screenshot, like below.
|
29
|
+
3. You must provide Juxtapose an accepted screenshot to match against in future test runs. Run the juxtapose server with `bundle exec juxtapose` and browse to http://localhost:4567 — you should see the just-captured screenshot, like below.
|
26
30
|
4. If the screenshot looks like what you expect, click Accept to save the image. Future runs of this test will be compared against the accepted one. ![Initial view of screenshots.](images/initial-view.png)
|
27
31
|
5. Run the tests again, and they should be green.
|
28
32
|
6. Later, when you introduce a change (intentionally or not), the tests will fail with the message "Screenshot did not match". Open up the juxtapose server again, and you'll see a comparison of your accepted screenshot with what the test captured. ![Diff view of screenshots.](images/diff-view.png)
|
29
33
|
7. If the change was intentional, you can accept the new screenshot - otherwise, you've got some fixin' to do!
|
30
34
|
|
31
35
|
|
36
|
+
## Testing RubyMotion Apps
|
37
|
+
|
32
38
|
### MacBacon
|
33
39
|
|
34
40
|
Juxtapose provides a single new matcher: `it_should_look_like` takes a single argument, a descriptive string predicate of what the screen should look like.
|
@@ -70,6 +76,53 @@ Then /^the screen should match "([^\"]*)"$/ do |template|
|
|
70
76
|
end
|
71
77
|
```
|
72
78
|
|
79
|
+
## Testing Rails Apps
|
80
|
+
|
81
|
+
### Capybara
|
82
|
+
|
83
|
+
Any Capybara driver that supports screenshot capture should work, but I've only tested this with [Poltergeist](https://github.com/teampoltergeist/poltergeist) so far.
|
84
|
+
|
85
|
+
In your `spec_helper.rb`, add:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
require 'juxtapose/capybara'
|
89
|
+
|
90
|
+
# if you're using Rspec, you can get the `look_like?` custom matcher with:
|
91
|
+
require 'juxtapose/rspec'
|
92
|
+
```
|
93
|
+
|
94
|
+
Now the Capybara page object will have a `looks_like?(predicate)` method that can call to make screenshot assertions:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
feature "viewing locations", js: true do
|
98
|
+
scenario "should only show books at first location" do
|
99
|
+
visit location_path Location.find_by_name("Cambridge")
|
100
|
+
expect(page).to look_like("books at Cambridge")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
### Fuzzy matching
|
106
|
+
|
107
|
+
Sometimes screenshots will differ slightly due to things like animation timing. Juxtapose will try to match an existing screenshot multiple times to help counteract this, but it can still be useful to be able to match screenshots using a percent similarity threshold.
|
108
|
+
|
109
|
+
In bacon specs, you can pass a fuzz factor from 0 to 100 to `it_should_look_like`, 0 meaning an exact match and 100 matching anything of the same size. The default is 0.
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
# matches images that are 15% similar
|
113
|
+
it_should_look_like 'a fancy animation', 15
|
114
|
+
```
|
115
|
+
|
116
|
+
In Frank, pass the fuzz factor in as the third argument to the Screenshotter constructor. A more exacting Frank step could be written as:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
Then /^the screen should match "([^\"]*)" within (\d+)% in (\d+) attempts$/ do |template, fuzz_factor, max_attempts|
|
120
|
+
wait_for_nothing_to_be_animating
|
121
|
+
screenshotter = Juxtapose::Screenshotter.new(self, template, fuzz_factor)
|
122
|
+
expect(screenshotter.attempt_verify(max_attempts)).to eq(true)
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
73
126
|
### Juxtapose Server
|
74
127
|
|
75
128
|
Juxtapose comes with a small webapp that you can use to view your screenshot specs, see diffs between accepted and failing specs and accept any changed images that are expected changes.
|
@@ -78,6 +131,10 @@ To start it, run `bundle exec juxtapose` in the root of your project and browse
|
|
78
131
|
|
79
132
|
## Release Notes
|
80
133
|
|
134
|
+
#### v.0.2.0
|
135
|
+
* Rails/rspec support
|
136
|
+
* Fixes to work under newest version of ImageMagick
|
137
|
+
|
81
138
|
#### v.0.1.1
|
82
139
|
|
83
140
|
* Raise error if screenshot sizes don't match ([@mdenomy](http://github.com/mdenomy))
|
@@ -46,7 +46,13 @@ angular.module('juxtapose.controllers').controller('ProjectsCtrl', ['$scope', '$
|
|
46
46
|
});
|
47
47
|
|
48
48
|
$scope.iOSVersion = function(spec){
|
49
|
-
|
49
|
+
version = spec.directory.match(/([^\/]*)\/[^\/]+\/[^\/]+$/)[1]
|
50
|
+
iosVersion = version.match(/[\d\.]+/)
|
51
|
+
if(iosVersion){
|
52
|
+
return iosVersion[0];
|
53
|
+
} else {
|
54
|
+
return version;
|
55
|
+
}
|
50
56
|
}
|
51
57
|
|
52
58
|
var versions = _.uniq(_.map($scope.project.specs, $scope.iOSVersion))
|
@@ -19,8 +19,12 @@ module Juxtapose
|
|
19
19
|
|
20
20
|
MAX_ATTEMPTS = 20
|
21
21
|
|
22
|
+
def looks_like?(template, fuzz_factor=0)
|
23
|
+
Screenshotter.new(self, template, fuzz_factor).attempt_verify(MAX_ATTEMPTS)
|
24
|
+
end
|
25
|
+
|
22
26
|
def it_should_look_like(template, fuzz_factor = 0)
|
23
|
-
|
27
|
+
looks_like?(template, fuzz_factor).should.be.true
|
24
28
|
end
|
25
29
|
|
26
30
|
class Screenshotter
|
@@ -44,11 +48,17 @@ module Juxtapose
|
|
44
48
|
Juxtapose::MacBaconStrategy.new(context)
|
45
49
|
elsif context.respond_to? :frankly_ping
|
46
50
|
Juxtapose::FrankStrategy.new(context)
|
51
|
+
elsif defined?(Capybara)
|
52
|
+
Juxtapose::CapybaraStrategy.new(context)
|
47
53
|
end
|
48
54
|
end
|
49
55
|
|
50
56
|
def project_root
|
51
|
-
|
57
|
+
if defined? Rails
|
58
|
+
Rails.root
|
59
|
+
else
|
60
|
+
ENV["RUBYMOTION_PROJECT_DIR"]
|
61
|
+
end
|
52
62
|
end
|
53
63
|
|
54
64
|
def test_name
|
@@ -80,6 +90,8 @@ module Juxtapose
|
|
80
90
|
end
|
81
91
|
|
82
92
|
def attempt_verify(max_attempts)
|
93
|
+
ensure_imagemagick_installed
|
94
|
+
|
83
95
|
attempts = 0
|
84
96
|
while attempts < max_attempts
|
85
97
|
return true if verify
|
@@ -90,6 +102,8 @@ module Juxtapose
|
|
90
102
|
end
|
91
103
|
|
92
104
|
def verify
|
105
|
+
ensure_imagemagick_installed
|
106
|
+
|
93
107
|
strategy.save_current filename(:current)
|
94
108
|
accept_current if ENV['ACCEPT_ALL_SCREENSHOTS']
|
95
109
|
|
@@ -104,6 +118,16 @@ module Juxtapose
|
|
104
118
|
success
|
105
119
|
end
|
106
120
|
|
121
|
+
def ensure_imagemagick_installed
|
122
|
+
unless imagemagick_installed?
|
123
|
+
raise "Executable for 'convert' not installed or not found on $PATH. Please install Imagemagick or add it to your $PATH."
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def imagemagick_installed?
|
128
|
+
`command -v convert`.length > 0
|
129
|
+
end
|
130
|
+
|
107
131
|
private
|
108
132
|
|
109
133
|
def same_size?
|
@@ -113,20 +137,30 @@ module Juxtapose
|
|
113
137
|
sizes.length == 2 && sizes.uniq.length == 1
|
114
138
|
end
|
115
139
|
|
116
|
-
def
|
117
|
-
|
140
|
+
def create_diff
|
141
|
+
`compare -fuzz #{fuzz_factor}% -dissimilarity-threshold 1 -subimage-search \"#{filename :current}\" \"#{filename :accepted}\" \"#{filename :diff}\" 2>&1`
|
142
|
+
end
|
143
|
+
|
144
|
+
def cleanup
|
145
|
+
`rm #{filename(:current)}`
|
146
|
+
`rm #{filename(:diff)}`
|
147
|
+
end
|
148
|
+
|
149
|
+
def identical_images?
|
118
150
|
compare_command = "compare -fuzz #{fuzz_factor}% -metric AE -dissimilarity-threshold 1 -subimage-search"
|
119
151
|
out = `#{compare_command} \"#{filename :current}\" \"#{filename :accepted}\" \"#{filename :diff}\" 2>&1`
|
120
152
|
out.chomp!
|
121
|
-
(
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
153
|
+
out.start_with?('0')
|
154
|
+
end
|
155
|
+
|
156
|
+
def screenshots_match?
|
157
|
+
if identical_images?
|
158
|
+
cleanup
|
159
|
+
true
|
160
|
+
else
|
161
|
+
create_diff
|
162
|
+
false
|
128
163
|
end
|
129
|
-
match
|
130
164
|
end
|
131
165
|
end
|
132
166
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Juxtapose
|
2
|
+
class CapybaraStrategy
|
3
|
+
|
4
|
+
attr_accessor :context
|
5
|
+
def initialize(context)
|
6
|
+
self.context = context
|
7
|
+
end
|
8
|
+
|
9
|
+
def version
|
10
|
+
@version ||= "web"
|
11
|
+
end
|
12
|
+
|
13
|
+
def current_spec_description
|
14
|
+
"spec-description"
|
15
|
+
#context.instance_variable_get('@__scenario').name
|
16
|
+
end
|
17
|
+
|
18
|
+
def device_name
|
19
|
+
"capybara"
|
20
|
+
end
|
21
|
+
|
22
|
+
def save_current(filename)
|
23
|
+
Capybara.save_screenshot filename
|
24
|
+
end
|
25
|
+
|
26
|
+
def spec_dir
|
27
|
+
"spec/screens"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/lib/juxtapose/version.rb
CHANGED
data/lib/motion-juxtapose.rb
CHANGED
@@ -33,14 +33,14 @@ describe 'screenshot testing under bacon' do
|
|
33
33
|
error.should.not.be.nil
|
34
34
|
error.message.should =~ /Screenshot did not match/
|
35
35
|
|
36
|
-
spec_dir = "spec/screens/iphone-retina/ios_7.
|
36
|
+
spec_dir = "spec/screens/iphone-retina/ios_7.1/screenshot-testing-under-bacon-raises-an-error-and-produces-diffs-on-failure/going-to-differ-screenshot"
|
37
37
|
|
38
38
|
File.should.exist(File.join( ENV["RUBYMOTION_PROJECT_DIR"], spec_dir, "current.png"))
|
39
39
|
File.should.exist(File.join( ENV["RUBYMOTION_PROJECT_DIR"], spec_dir, "diff.png"))
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
it "raises an error when screens are different sizes" do
|
43
|
+
it "raises an error when screens are different sizes" do
|
44
44
|
error = nil
|
45
45
|
begin
|
46
46
|
it_should_look_like "different sized screenshot"
|
Binary file
|
File without changes
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motion-juxtapose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Lind
|
@@ -10,90 +10,90 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2014-
|
13
|
+
date: 2014-08-27 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: sinatra
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
|
-
- - ~>
|
19
|
+
- - "~>"
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '1.4'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
|
-
- - ~>
|
26
|
+
- - "~>"
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: '1.4'
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: haml
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
|
-
- -
|
33
|
+
- - ">="
|
34
34
|
- !ruby/object:Gem::Version
|
35
35
|
version: '0'
|
36
36
|
type: :runtime
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- -
|
40
|
+
- - ">="
|
41
41
|
- !ruby/object:Gem::Version
|
42
42
|
version: '0'
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: bundler
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- - ~>
|
47
|
+
- - "~>"
|
48
48
|
- !ruby/object:Gem::Version
|
49
49
|
version: '1.3'
|
50
50
|
type: :development
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
|
-
- - ~>
|
54
|
+
- - "~>"
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: '1.3'
|
57
57
|
- !ruby/object:Gem::Dependency
|
58
58
|
name: rake
|
59
59
|
requirement: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
|
-
- -
|
61
|
+
- - ">="
|
62
62
|
- !ruby/object:Gem::Version
|
63
63
|
version: '0'
|
64
64
|
type: :development
|
65
65
|
prerelease: false
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
67
67
|
requirements:
|
68
|
-
- -
|
68
|
+
- - ">="
|
69
69
|
- !ruby/object:Gem::Version
|
70
70
|
version: '0'
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
72
|
name: rspec
|
73
73
|
requirement: !ruby/object:Gem::Requirement
|
74
74
|
requirements:
|
75
|
-
- -
|
75
|
+
- - ">="
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: '0'
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
80
|
version_requirements: !ruby/object:Gem::Requirement
|
81
81
|
requirements:
|
82
|
-
- -
|
82
|
+
- - ">="
|
83
83
|
- !ruby/object:Gem::Version
|
84
84
|
version: '0'
|
85
85
|
- !ruby/object:Gem::Dependency
|
86
86
|
name: fakefs
|
87
87
|
requirement: !ruby/object:Gem::Requirement
|
88
88
|
requirements:
|
89
|
-
- -
|
89
|
+
- - ">="
|
90
90
|
- !ruby/object:Gem::Version
|
91
91
|
version: '0'
|
92
92
|
type: :development
|
93
93
|
prerelease: false
|
94
94
|
version_requirements: !ruby/object:Gem::Requirement
|
95
95
|
requirements:
|
96
|
-
- -
|
96
|
+
- - ">="
|
97
97
|
- !ruby/object:Gem::Version
|
98
98
|
version: '0'
|
99
99
|
description: Screenshot-based assertions for RubyMotion projects
|
@@ -104,7 +104,7 @@ executables:
|
|
104
104
|
extensions: []
|
105
105
|
extra_rdoc_files: []
|
106
106
|
files:
|
107
|
-
- .gitignore
|
107
|
+
- ".gitignore"
|
108
108
|
- Gemfile
|
109
109
|
- LICENSE.txt
|
110
110
|
- README.md
|
@@ -151,16 +151,19 @@ files:
|
|
151
151
|
- lib/juxtapose/application/spec/models/spec_spec.rb
|
152
152
|
- lib/juxtapose/application/spec/spec_helper.rb
|
153
153
|
- lib/juxtapose/application/views/index.html.haml
|
154
|
+
- lib/juxtapose/capybara.rb
|
155
|
+
- lib/juxtapose/rspec.rb
|
154
156
|
- lib/juxtapose/screenshotter.rb
|
157
|
+
- lib/juxtapose/strategy/capybara_strategy.rb
|
155
158
|
- lib/juxtapose/strategy/frank_strategy.rb
|
156
159
|
- lib/juxtapose/strategy/mac_bacon_strategy.rb
|
157
160
|
- lib/juxtapose/version.rb
|
158
161
|
- lib/motion-juxtapose.rb
|
159
162
|
- motion-juxtapose.gemspec
|
160
163
|
- spec/controllers/test_controller_spec.rb
|
161
|
-
- spec/screens/iphone-retina/ios_7.
|
162
|
-
- spec/screens/iphone-retina/ios_7.
|
163
|
-
- spec/screens/iphone-retina/ios_7.
|
164
|
+
- spec/screens/iphone-retina/ios_7.1/screenshot-testing-under-bacon-passes-when-there-is-an-identical-accepted-screenshot/accepted-screenshot/accepted.png
|
165
|
+
- spec/screens/iphone-retina/ios_7.1/screenshot-testing-under-bacon-raises-an-error-and-produces-diffs-on-failure/going-to-differ-screenshot/accepted.png
|
166
|
+
- spec/screens/iphone-retina/ios_7.1/screenshot-testing-under-bacon-raises-an-error-when-screens-are-different-sizes/different-sized-screenshot/accepted.png
|
164
167
|
homepage: https://github.com/terriblelabs/motion-juxtapose
|
165
168
|
licenses:
|
166
169
|
- MIT
|
@@ -171,12 +174,12 @@ require_paths:
|
|
171
174
|
- lib
|
172
175
|
required_ruby_version: !ruby/object:Gem::Requirement
|
173
176
|
requirements:
|
174
|
-
- -
|
177
|
+
- - ">="
|
175
178
|
- !ruby/object:Gem::Version
|
176
179
|
version: '0'
|
177
180
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
178
181
|
requirements:
|
179
|
-
- -
|
182
|
+
- - ">="
|
180
183
|
- !ruby/object:Gem::Version
|
181
184
|
version: '0'
|
182
185
|
requirements: []
|
@@ -187,6 +190,6 @@ specification_version: 4
|
|
187
190
|
summary: Screenshot-based assertions for RubyMotion projects
|
188
191
|
test_files:
|
189
192
|
- spec/controllers/test_controller_spec.rb
|
190
|
-
- spec/screens/iphone-retina/ios_7.
|
191
|
-
- spec/screens/iphone-retina/ios_7.
|
192
|
-
- spec/screens/iphone-retina/ios_7.
|
193
|
+
- spec/screens/iphone-retina/ios_7.1/screenshot-testing-under-bacon-passes-when-there-is-an-identical-accepted-screenshot/accepted-screenshot/accepted.png
|
194
|
+
- spec/screens/iphone-retina/ios_7.1/screenshot-testing-under-bacon-raises-an-error-and-produces-diffs-on-failure/going-to-differ-screenshot/accepted.png
|
195
|
+
- spec/screens/iphone-retina/ios_7.1/screenshot-testing-under-bacon-raises-an-error-when-screens-are-different-sizes/different-sized-screenshot/accepted.png
|