jekyll-twitter-plugin 1.4.0 → 2.0.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 +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
|