jekyll-twitter-plugin 1.4.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/.travis.yml +0 -1
- data/README.md +92 -82
- data/jekyll-twitter-plugin.gemspec +5 -6
- data/lib/jekyll-twitter-plugin.rb +141 -121
- data/media/embedded-grid.png +0 -0
- data/media/embedded-moment.png +0 -0
- data/media/embedded-timeline.png +0 -0
- data/media/embedded-tweet.png +0 -0
- data/spec/api_request_spec.rb +82 -0
- data/spec/integration_tests.rb +10 -3
- data/spec/spec_helper.rb +1 -0
- data/spec/support/shared_contexts.rb +5 -4
- data/spec/twitter_tag_spec.rb +117 -112
- metadata +22 -17
- data/spec/oembed_spec.rb +0 -103
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
RSpec.describe TwitterJekyll::ApiRequest do
|
3
|
+
subject(:api_request) { described_class.new(url, params) }
|
4
|
+
let(:url) { "https://twitter.com/twitter_user/status/12345" }
|
5
|
+
let(:params) { {} }
|
6
|
+
|
7
|
+
describe "#to_uri" do
|
8
|
+
subject(:uri) { api_request.to_uri }
|
9
|
+
|
10
|
+
it "uses correct api" do
|
11
|
+
expect(uri.scheme).to eq "https"
|
12
|
+
expect(uri.host).to eq "publish.twitter.com"
|
13
|
+
expect(uri.path).to eq "/oembed"
|
14
|
+
end
|
15
|
+
|
16
|
+
context "with no params" do
|
17
|
+
it "has url encoded query param" do
|
18
|
+
expect(URI.decode_www_form(uri.query)).to match_array [["url", url]]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "with params" do
|
23
|
+
let(:params) { { align: "right" } }
|
24
|
+
|
25
|
+
it "has encoded query params" do
|
26
|
+
expect(URI.decode_www_form(uri.query)).to match_array [["url", url], %w(align right)]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "with an incorrectly passed url param" do
|
31
|
+
let(:params) { { url: "why" } }
|
32
|
+
|
33
|
+
it "has uses the correct url" do
|
34
|
+
expect(URI.decode_www_form(uri.query)).to match_array [["url", url]]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#cache_key" do
|
40
|
+
let(:url) { ["https://twitter.com/twitter_user/status/12345"] }
|
41
|
+
|
42
|
+
context "with no params" do
|
43
|
+
it "matches on status url" do
|
44
|
+
request_one = described_class.new(url, {})
|
45
|
+
request_two = described_class.new(url, {})
|
46
|
+
|
47
|
+
expect(
|
48
|
+
request_one.cache_key == request_two.cache_key
|
49
|
+
).to be true
|
50
|
+
end
|
51
|
+
|
52
|
+
it "fails if different" do
|
53
|
+
request_one = described_class.new(url, {})
|
54
|
+
request_two = described_class.new("https://twitter.com/other_user/status/12345", {})
|
55
|
+
|
56
|
+
expect(
|
57
|
+
request_one.cache_key == request_two.cache_key
|
58
|
+
).to be false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "with params" do
|
63
|
+
it "matches on keys and values" do
|
64
|
+
request_one = described_class.new(url, align: "left")
|
65
|
+
request_two = described_class.new(url, align: "left")
|
66
|
+
|
67
|
+
expect(
|
68
|
+
request_one.cache_key == request_two.cache_key
|
69
|
+
).to be true
|
70
|
+
end
|
71
|
+
|
72
|
+
it "fails if different" do
|
73
|
+
request_one = described_class.new(url, align: "left")
|
74
|
+
request_two = described_class.new(url, align: "right")
|
75
|
+
|
76
|
+
expect(
|
77
|
+
request_one.cache_key == request_two.cache_key
|
78
|
+
).to be false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/spec/integration_tests.rb
CHANGED
@@ -7,13 +7,20 @@ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
|
7
7
|
require_relative "./support/jekyll_template"
|
8
8
|
require "jekyll-twitter-plugin"
|
9
9
|
require "erb"
|
10
|
+
require "byebug"
|
10
11
|
|
11
12
|
OUTPUT_FILENAME = "output_test.html"
|
12
13
|
OPTIONS = [
|
13
|
-
"
|
14
|
-
"
|
14
|
+
"https://twitter.com/jekyllrb maxwidth=500 limit=5",
|
15
|
+
"https://twitter.com/rubygems",
|
16
|
+
"https://twitter.com/i/moments/650667182356082688 maxwidth=500",
|
17
|
+
"https://twitter.com/TwitterDev/timelines/539487832448843776 limit=5 widget_type=grid maxwidth=500",
|
15
18
|
"https://twitter.com/rubygems/status/518821243320287232",
|
16
|
-
"
|
19
|
+
"https://twitter.com/rubygems/status/11",
|
20
|
+
"https://twitter.com/rubygems/status/518821243320287232 align=right width=350",
|
21
|
+
"https://twitter.com/Ace_Tate/status/225611299009216512",
|
22
|
+
"https://twitter.com/FeelsGood2BeMe/status/225456333032398848",
|
23
|
+
"oembed https://twitter.com/rubygems/status/518821243320287232",
|
17
24
|
].freeze
|
18
25
|
|
19
26
|
COLOUR_MAP = {
|
data/spec/spec_helper.rb
CHANGED
@@ -11,11 +11,12 @@ RSpec.shared_context "without cached response" do
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
RSpec.shared_context "with
|
15
|
-
let(:
|
16
|
-
let(:response) {
|
14
|
+
RSpec.shared_context "with a normal request and response" do
|
15
|
+
let(:arguments) { "https://twitter.com/twitter_user/status/12345" }
|
16
|
+
let(:response) { { html: "<p>tweet html</p>" } }
|
17
17
|
|
18
18
|
before do
|
19
|
-
allow(api_client).to receive(:
|
19
|
+
allow(api_client).to receive(:fetch).and_return(response)
|
20
|
+
allow(TwitterJekyll::ApiClient).to receive(:new).and_return(api_client)
|
20
21
|
end
|
21
22
|
end
|
data/spec/twitter_tag_spec.rb
CHANGED
@@ -1,26 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
RSpec.describe TwitterJekyll::TwitterTag do
|
3
|
-
let(:context) {
|
4
|
-
let(:
|
5
|
-
|
3
|
+
let(:context) { empty_jekyll_context }
|
4
|
+
let(:arguments) { "" }
|
5
|
+
let(:api_response_hash) do
|
6
|
+
{
|
7
|
+
"url" => "https://twitter.com/twitter_user/status/12345",
|
8
|
+
"author_name" => "twitter user",
|
9
|
+
"author_url" => "https://twitter.com/twitter_user",
|
10
|
+
"html" => "<p>tweet html</p>",
|
11
|
+
"width" => 550,
|
12
|
+
"height" => nil,
|
13
|
+
"type" => "rich",
|
14
|
+
"cache_age" => "3153600000",
|
15
|
+
"provider_name" => "Twitter",
|
16
|
+
"provider_url" => "https://twitter.com",
|
17
|
+
"version" => "1.0"
|
18
|
+
}
|
19
|
+
end
|
20
|
+
subject { described_class.new(nil, arguments, nil) }
|
6
21
|
|
7
22
|
describe "output from oembed request" do
|
8
|
-
let(:
|
9
|
-
{
|
10
|
-
"url" => "https://twitter.com/twitter_user/status/12345",
|
11
|
-
"author_name" => "twitter user",
|
12
|
-
"author_url" => "https://twitter.com/twitter_user",
|
13
|
-
"html" => "<p>tweet html</p>",
|
14
|
-
"width" => 550,
|
15
|
-
"height" => nil,
|
16
|
-
"type" => "rich",
|
17
|
-
"cache_age" => "3153600000",
|
18
|
-
"provider_name" => "Twitter",
|
19
|
-
"provider_url" => "https://twitter.com",
|
20
|
-
"version" => "1.0"
|
21
|
-
}
|
22
|
-
end
|
23
|
-
let(:options) { "oembed https://twitter.com/twitter_user/status/12345" }
|
23
|
+
let(:arguments) { "https://twitter.com/twitter_user/status/12345" }
|
24
24
|
|
25
25
|
context "with cached response" do
|
26
26
|
let(:cache) { double("TwitterJekyll::FileCache") }
|
@@ -29,7 +29,6 @@ RSpec.describe TwitterJekyll::TwitterTag do
|
|
29
29
|
end
|
30
30
|
|
31
31
|
it "renders response from cache" do
|
32
|
-
allow(Twitter::REST::Client).to receive(:new).and_return(double.as_null_object)
|
33
32
|
expect(cache).to receive(:read).with(an_instance_of(String)).and_return(api_response_hash)
|
34
33
|
|
35
34
|
output = subject.render(context)
|
@@ -39,19 +38,18 @@ RSpec.describe TwitterJekyll::TwitterTag do
|
|
39
38
|
|
40
39
|
context "without cached response" do
|
41
40
|
let(:cache) { double("TwitterJekyll::FileCache") }
|
42
|
-
let(:response) { build_response_object(api_response_hash) }
|
43
41
|
before do
|
44
42
|
subject.cache = cache
|
45
43
|
allow(cache).to receive(:read).with(an_instance_of(String)).and_return(nil)
|
46
44
|
end
|
47
45
|
|
48
46
|
context "with successful api request" do
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
before do
|
48
|
+
stub_api_request(status: 200, body: api_response_hash.to_json, headers: {})
|
49
|
+
end
|
52
50
|
|
53
|
-
|
54
|
-
expect(cache).to receive(:write).with(an_instance_of(String),
|
51
|
+
it "renders response from api and writes to cache" do
|
52
|
+
expect(cache).to receive(:write).with(an_instance_of(String), api_response_hash)
|
55
53
|
|
56
54
|
output = subject.render(context)
|
57
55
|
expect_output_to_match_tag_content(output, api_response_hash.fetch("html"))
|
@@ -59,107 +57,106 @@ RSpec.describe TwitterJekyll::TwitterTag do
|
|
59
57
|
end
|
60
58
|
|
61
59
|
context "with a status not found api request" do
|
62
|
-
|
63
|
-
|
64
|
-
|
60
|
+
before do
|
61
|
+
stub_api_request(status: [404, "Not Found"], body: "", headers: {})
|
62
|
+
end
|
65
63
|
|
66
|
-
|
67
|
-
expect(cache).
|
64
|
+
it "renders error response and writes to cache" do
|
65
|
+
expect(cache).to receive(:write).with(an_instance_of(String), an_instance_of(Hash))
|
68
66
|
|
69
67
|
output = subject.render(context)
|
70
|
-
expect_output_to_have_error(output,
|
68
|
+
expect_output_to_have_error(output, "Not Found")
|
71
69
|
end
|
72
70
|
end
|
73
71
|
|
74
72
|
context "with a status request not permitted api request" do
|
75
|
-
|
76
|
-
|
77
|
-
|
73
|
+
before do
|
74
|
+
stub_api_request(status: [403, "Forbidden"], body: "", headers: {})
|
75
|
+
end
|
78
76
|
|
79
|
-
|
80
|
-
expect(cache).
|
77
|
+
it "renders error response and writes to cache" do
|
78
|
+
expect(cache).to receive(:write).with(an_instance_of(String), an_instance_of(Hash))
|
81
79
|
|
82
80
|
output = subject.render(context)
|
83
|
-
expect_output_to_have_error(output,
|
81
|
+
expect_output_to_have_error(output, "Forbidden")
|
84
82
|
end
|
85
83
|
end
|
86
|
-
end
|
87
|
-
end
|
88
84
|
|
89
|
-
|
90
|
-
|
91
|
-
|
85
|
+
context "with a server error api request" do
|
86
|
+
before do
|
87
|
+
stub_api_request(status: [500, "Internal Server Error"], body: "", headers: {})
|
88
|
+
end
|
92
89
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
90
|
+
it "renders error response and writes to cache" do
|
91
|
+
expect(cache).to receive(:write).with(an_instance_of(String), an_instance_of(Hash))
|
92
|
+
|
93
|
+
output = subject.render(context)
|
94
|
+
expect_output_to_have_error(output, "Internal Server Error")
|
97
95
|
end
|
98
96
|
end
|
99
|
-
end
|
100
97
|
|
101
|
-
|
102
|
-
|
98
|
+
context "with api request that times out" do
|
99
|
+
before do
|
100
|
+
stub_api.to_timeout
|
101
|
+
end
|
103
102
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
103
|
+
it "renders error response and writes to cache" do
|
104
|
+
expect(cache).to receive(:write).with(an_instance_of(String), an_instance_of(Hash))
|
105
|
+
|
106
|
+
output = subject.render(context)
|
107
|
+
expect_output_to_have_error(output, "Timeout::Error")
|
108
108
|
end
|
109
109
|
end
|
110
|
-
end
|
111
110
|
|
112
|
-
|
113
|
-
|
111
|
+
context "with the oembed api type as the first argument" do
|
112
|
+
let(:arguments) { "oembed https://twitter.com/twitter_user/status/12345" }
|
113
|
+
before do
|
114
|
+
stub_api_request(status: 200, body: api_response_hash.to_json, headers: {})
|
115
|
+
end
|
114
116
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
117
|
+
it "renders response from api and writes to cache" do
|
118
|
+
expect(cache).to receive(:write).with(an_instance_of(String), api_response_hash)
|
119
|
+
|
120
|
+
output = subject.render(context)
|
121
|
+
expect_output_to_match_tag_content(output, api_response_hash.fetch("html"))
|
119
122
|
end
|
120
123
|
end
|
121
124
|
end
|
122
125
|
end
|
123
126
|
|
124
|
-
describe "parsing
|
125
|
-
|
126
|
-
|
127
|
-
let(:status) { double }
|
128
|
-
|
129
|
-
context "with oembed requested" do
|
130
|
-
let(:options) { "oembed https://twitter.com/twitter_user/status/12345" }
|
131
|
-
|
132
|
-
it "uses the oembed api" do
|
133
|
-
api_client = double("Twitter::REST::Client", status: status)
|
134
|
-
allow(Twitter::REST::Client).to receive(:new).and_return(api_client)
|
127
|
+
describe "parsing arguments" do
|
128
|
+
context "without any arguments" do
|
129
|
+
let(:arguments) { "" }
|
135
130
|
|
136
|
-
|
137
|
-
|
131
|
+
it "raises an exception" do
|
132
|
+
expect_to_raise_invalid_args_error(arguments) do
|
133
|
+
tag = described_class.new(nil, arguments, nil)
|
134
|
+
tag.render(context)
|
135
|
+
end
|
138
136
|
end
|
139
137
|
end
|
140
138
|
|
141
|
-
context "
|
142
|
-
let(:
|
139
|
+
context "with the oembed api type as the first argument" do
|
140
|
+
let(:arguments) { "oembed https://twitter.com/twitter_user/status/12345" }
|
143
141
|
|
144
|
-
it "uses
|
145
|
-
api_client =
|
146
|
-
allow(
|
142
|
+
it "uses correct twitter url and warns of deprecation" do
|
143
|
+
api_client = api_client_double
|
144
|
+
allow(api_client).to receive(:fetch).and_return({})
|
145
|
+
allow(TwitterJekyll::ApiClient).to receive(:new).and_return(api_client)
|
146
|
+
expect(TwitterJekyll::ApiRequest).to receive(:new).with("https://twitter.com/twitter_user/status/12345", {}).and_call_original
|
147
147
|
|
148
|
-
expect
|
149
|
-
|
148
|
+
expect do
|
149
|
+
tag = described_class.new(nil, arguments, nil)
|
150
|
+
tag.render(context)
|
151
|
+
end.to output(/Passing 'oembed' as the first argument is not required anymore/).to_stderr
|
150
152
|
end
|
151
153
|
end
|
152
154
|
end
|
153
155
|
|
154
156
|
describe "parsing api secrets" do
|
155
157
|
include_context "without cached response"
|
156
|
-
include_context "with
|
157
|
-
let(:api_client) {
|
158
|
-
let(:config_builder) { double }
|
159
|
-
|
160
|
-
before do
|
161
|
-
allow(Twitter::REST::Client).to receive(:new).and_yield(config_builder).and_return(api_client)
|
162
|
-
end
|
158
|
+
include_context "with a normal request and response"
|
159
|
+
let(:api_client) { api_client_double }
|
163
160
|
|
164
161
|
context "with api secrets provided by ENV" do
|
165
162
|
let(:context) { double("context", registers: { site: double(config: {}) }) }
|
@@ -170,13 +167,11 @@ RSpec.describe TwitterJekyll::TwitterTag do
|
|
170
167
|
"TWITTER_ACCESS_TOKEN_SECRET" => "access_token_secret")
|
171
168
|
end
|
172
169
|
|
173
|
-
it "
|
174
|
-
expect
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
subject.render(context)
|
170
|
+
it "warns of deprecation" do
|
171
|
+
expect do
|
172
|
+
tag = described_class.new(nil, arguments, nil)
|
173
|
+
tag.render(context)
|
174
|
+
end.to output(/Found Twitter API keys in ENV, this library does not require these keys anymore/).to_stderr
|
180
175
|
end
|
181
176
|
end
|
182
177
|
|
@@ -191,40 +186,55 @@ RSpec.describe TwitterJekyll::TwitterTag do
|
|
191
186
|
stub_const("ENV", {})
|
192
187
|
end
|
193
188
|
|
194
|
-
it "
|
195
|
-
expect
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
subject.render(context)
|
189
|
+
it "warns of deprecation" do
|
190
|
+
expect do
|
191
|
+
tag = described_class.new(nil, arguments, nil)
|
192
|
+
tag.render(context)
|
193
|
+
end.to output(/Found Twitter API keys in Jekyll _config.yml, this library does not require these keys anymore/).to_stderr
|
201
194
|
end
|
202
195
|
end
|
203
196
|
|
204
197
|
context "with no api secrets provided" do
|
205
|
-
let(:context) {
|
198
|
+
let(:context) { empty_jekyll_context }
|
206
199
|
before do
|
207
200
|
stub_const("ENV", {})
|
208
201
|
end
|
209
202
|
|
210
|
-
it "
|
203
|
+
it "does not warn" do
|
211
204
|
expect do
|
212
205
|
subject.render(context)
|
213
|
-
end.
|
206
|
+
end.to_not output.to_stderr
|
214
207
|
end
|
215
208
|
end
|
216
209
|
end
|
217
210
|
|
218
211
|
private
|
219
212
|
|
213
|
+
def stub_api_request(response)
|
214
|
+
stub_api
|
215
|
+
.to_return(response)
|
216
|
+
end
|
217
|
+
|
218
|
+
def stub_api
|
219
|
+
stub_request(:get, /publish.twitter.com/)
|
220
|
+
end
|
221
|
+
|
222
|
+
def empty_jekyll_context
|
223
|
+
double("context", registers: { site: double(config: {}) })
|
224
|
+
end
|
225
|
+
|
226
|
+
def api_client_double
|
227
|
+
double("TwitterJekyll::ApiClient")
|
228
|
+
end
|
229
|
+
|
220
230
|
def expect_output_to_match_tag_content(actual, content)
|
221
231
|
expect(actual).to eq(
|
222
|
-
"<div class='
|
232
|
+
"<div class='jekyll-twitter-plugin'>#{content}</div>"
|
223
233
|
)
|
224
234
|
end
|
225
235
|
|
226
236
|
def expect_output_to_have_error(actual, error, tweet_url = "https://twitter.com/twitter_user/status/12345")
|
227
|
-
expect_output_to_match_tag_content(actual, "<p>There was a '#{error}' error fetching
|
237
|
+
expect_output_to_match_tag_content(actual, "<p>There was a '#{error}' error fetching URL: '#{tweet_url}'</p>")
|
228
238
|
end
|
229
239
|
|
230
240
|
def expect_to_raise_invalid_args_error(options)
|
@@ -235,9 +245,4 @@ RSpec.describe TwitterJekyll::TwitterTag do
|
|
235
245
|
yield
|
236
246
|
end.to raise_error(ArgumentError, message)
|
237
247
|
end
|
238
|
-
|
239
|
-
# The twitter gem responds with a struct like object so we do too.
|
240
|
-
def build_response_object(response)
|
241
|
-
OpenStruct.new(response)
|
242
|
-
end
|
243
248
|
end
|