pusher-fake 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/features/channel_trigger.feature +10 -0
- data/features/channel_webhooks.feature +40 -0
- data/features/step_definitions/channel_steps.rb +32 -35
- data/features/step_definitions/client_steps.rb +2 -2
- data/features/step_definitions/event_steps.rb +12 -6
- data/features/step_definitions/presence_steps.rb +1 -1
- data/features/step_definitions/webhook_steps.rb +21 -0
- data/features/support/application.rb +1 -1
- data/features/support/application/views/index.erb +5 -6
- data/features/support/environment.rb +0 -1
- data/features/support/webhooks.rb +36 -0
- data/lib/pusher-fake.rb +3 -1
- data/lib/pusher-fake/channel/presence.rb +6 -2
- data/lib/pusher-fake/channel/public.rb +12 -0
- data/lib/pusher-fake/configuration.rb +4 -0
- data/lib/pusher-fake/webhook.rb +30 -0
- data/spec/lib/pusher-fake/channel/presence_spec.rb +49 -6
- data/spec/lib/pusher-fake/channel/private_spec.rb +24 -2
- data/spec/lib/pusher-fake/channel/public_spec.rb +28 -7
- data/spec/lib/pusher-fake/channel_spec.rb +16 -6
- data/spec/lib/pusher-fake/configuration_spec.rb +1 -0
- data/spec/lib/pusher-fake/server_spec.rb +3 -3
- data/spec/lib/pusher-fake/webhook_spec.rb +41 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/support/have_configuration_option_matcher.rb +0 -3
- metadata +29 -36
@@ -72,3 +72,13 @@ Feature: Triggering events on a channel
|
|
72
72
|
When I trigger the "client-message" event on the "chat" channel
|
73
73
|
Then I should not receive a "client-message" event on the "chat" channel
|
74
74
|
And Bob should not receive a "client-message" event on the "chat" channel
|
75
|
+
|
76
|
+
Scenario: Server triggers an event on multiple private channels
|
77
|
+
Given I am subscribed to the "private-chat-1" channel
|
78
|
+
And Bob is subscribed to the "private-chat-2" channel
|
79
|
+
When a "message" event is triggered on the following channels:
|
80
|
+
| name |
|
81
|
+
| private-chat-1 |
|
82
|
+
| private-chat-2 |
|
83
|
+
Then I should receive a "message" event on the "private-chat-1" channel
|
84
|
+
And Bob should receive a "message" event on the "private-chat-2" channel
|
@@ -0,0 +1,40 @@
|
|
1
|
+
@javascript
|
2
|
+
Feature: Triggering channel webhooks
|
3
|
+
|
4
|
+
Background:
|
5
|
+
Given I am connected
|
6
|
+
And Bob is connected
|
7
|
+
|
8
|
+
Scenario: Occupying and vacating a channel
|
9
|
+
When I subscribe to the "game-1" channel
|
10
|
+
Then the server should have received the following event:
|
11
|
+
| name | channel_occupied |
|
12
|
+
| channel | game-1 |
|
13
|
+
When Bob is subscribed to the "game-1" channel
|
14
|
+
Then the server should have received no events
|
15
|
+
When Bob unsubscribes from the "game-1" channel
|
16
|
+
Then the server should have received no events
|
17
|
+
When I unsubscribe from the "game-1" channel
|
18
|
+
Then the server should have received the following event:
|
19
|
+
| name | channel_vacated |
|
20
|
+
| channel | game-1 |
|
21
|
+
|
22
|
+
Scenario: Subscribing and unsubscribing from a presence channel
|
23
|
+
When I subscribe to the "presence-chat-1" channel
|
24
|
+
Then the server should have received the following user event:
|
25
|
+
| name | member_added |
|
26
|
+
| channel | presence-chat-1 |
|
27
|
+
When Bob is subscribed to the "presence-chat-1" channel
|
28
|
+
Then the server should have received the following user event:
|
29
|
+
| user | Bob |
|
30
|
+
| name | member_added |
|
31
|
+
| channel | presence-chat-1 |
|
32
|
+
When Bob unsubscribes from the "presence-chat-1" channel
|
33
|
+
Then the server should have received the following user event:
|
34
|
+
| user | Bob |
|
35
|
+
| name | member_removed |
|
36
|
+
| channel | presence-chat-1 |
|
37
|
+
When I unsubscribe from the "presence-chat-1" channel
|
38
|
+
Then the server should have received the following user event:
|
39
|
+
| name | member_removed |
|
40
|
+
| channel | presence-chat-1 |
|
@@ -10,51 +10,48 @@ Given %{$name is subscribed to the "$channel" channel} do |name, channel|
|
|
10
10
|
end
|
11
11
|
|
12
12
|
When %{I subscribe to the "$channel" channel} do |channel|
|
13
|
-
page.execute_script("Pusher.instance.subscribe(#{channel
|
13
|
+
page.execute_script("Pusher.instance.subscribe(#{MultiJson.dump(channel)})")
|
14
14
|
end
|
15
15
|
|
16
16
|
When %{I subscribe to the "$channel" channel with presence events} do |channel|
|
17
17
|
page.execute_script(%{
|
18
|
-
var list
|
19
|
-
count
|
20
|
-
|
18
|
+
var list = list || document.querySelector("section ul"),
|
19
|
+
count = count || document.querySelector("section header h1 span"),
|
20
|
+
addClient = addClient || function(client) {
|
21
|
+
var
|
22
|
+
element = list.appendChild(document.createElement("li"));
|
23
|
+
element.setAttribute("id", "client-" + client.id);
|
21
24
|
|
22
|
-
|
23
|
-
|
25
|
+
if (client.info) {
|
26
|
+
element.innerHTML = client.info.name;
|
27
|
+
}
|
28
|
+
},
|
29
|
+
changeCount = changeCount || function(delta) {
|
30
|
+
count.innerHTML = parseInt(count.innerHTML, 10) + delta;
|
31
|
+
};
|
24
32
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
element.setAttribute("id", "client-" + client.id);
|
33
|
+
Pusher.instance.subscribe(#{MultiJson.dump(channel)})
|
34
|
+
.bind("pusher:subscription_succeeded", function(clients) {
|
35
|
+
clients.each(addClient);
|
29
36
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
var
|
39
|
-
element = list.appendChild(document.createElement("li"));
|
40
|
-
element.setAttribute("id", "client-" + client.id);
|
37
|
+
count.innerHTML = clients.count;
|
38
|
+
})
|
39
|
+
.bind("pusher:member_added", function(client) {
|
40
|
+
addClient(client);
|
41
|
+
changeCount(1);
|
42
|
+
})
|
43
|
+
.bind("pusher:member_removed", function(client) {
|
44
|
+
var item = list.querySelector("li#client-" + client.id);
|
41
45
|
|
42
|
-
|
43
|
-
element.innerHTML = client.info.name;
|
44
|
-
}
|
45
|
-
});
|
46
|
-
channel.bind("pusher:member_removed", function(client) {
|
47
|
-
var item = list.querySelector("li#client-" + client.id);
|
46
|
+
list.removeChild(item);
|
48
47
|
|
49
|
-
|
50
|
-
|
51
|
-
list.removeChild(item);
|
52
|
-
});
|
48
|
+
changeCount(-1);
|
49
|
+
});
|
53
50
|
})
|
54
51
|
end
|
55
52
|
|
56
53
|
When %{I unsubscribe from the "$channel" channel} do |channel|
|
57
|
-
page.execute_script("Pusher.instance.unsubscribe(#{channel
|
54
|
+
page.execute_script("Pusher.instance.unsubscribe(#{MultiJson.dump(channel)})")
|
58
55
|
end
|
59
56
|
|
60
57
|
When %{$name unsubscribes from the "$channel" channel} do |name, channel|
|
@@ -64,10 +61,10 @@ When %{$name unsubscribes from the "$channel" channel} do |name, channel|
|
|
64
61
|
end
|
65
62
|
|
66
63
|
Then %{I should be subscribed to the "$channel" channel} do |channel|
|
67
|
-
|
64
|
+
Capybara.timeout do
|
68
65
|
subscribed = page.evaluate_script(%{
|
69
66
|
var
|
70
|
-
channel = Pusher.instance.channel(#{channel
|
67
|
+
channel = Pusher.instance.channel(#{MultiJson.dump(channel)});
|
71
68
|
channel && channel.subscribed;
|
72
69
|
})
|
73
70
|
subscribed == true
|
@@ -78,7 +75,7 @@ Then %{I should not be subscribed to the "$channel" channel} do |channel|
|
|
78
75
|
wait do
|
79
76
|
subscribed = page.evaluate_script(%{
|
80
77
|
var
|
81
|
-
channel = Pusher.instance.channel(#{channel
|
78
|
+
channel = Pusher.instance.channel(#{MultiJson.dump(channel)});
|
82
79
|
channel && channel.subscribed;
|
83
80
|
})
|
84
81
|
subscribed.should be_false
|
@@ -14,14 +14,14 @@ Given "I change my socket ID" do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
Then "I should be connected" do
|
17
|
-
|
17
|
+
Capybara.timeout do
|
18
18
|
state = page.evaluate_script("Pusher.instance.connection.state")
|
19
19
|
state == "connected"
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
Then "I should not be connected" do
|
24
|
-
|
24
|
+
Capybara.timeout do
|
25
25
|
state = page.evaluate_script("Pusher.instance.connection.state")
|
26
26
|
state == "unavailable"
|
27
27
|
end
|
@@ -1,17 +1,23 @@
|
|
1
1
|
When %{a "$event" event is triggered on the "$channel" channel} do |event, channel|
|
2
|
-
Pusher.trigger(
|
2
|
+
Pusher.trigger(channel, event, {})
|
3
|
+
end
|
4
|
+
|
5
|
+
When %{a "$event" event is triggered on the following channels:} do |event, table|
|
6
|
+
channels = table.hashes.collect { |hash| hash["name"] }
|
7
|
+
|
8
|
+
Pusher.trigger(channels, event, {})
|
3
9
|
end
|
4
10
|
|
5
11
|
When %{I trigger the "$event" event on the "$channel" channel} do |event, channel|
|
6
12
|
page.execute_script(%{
|
7
13
|
var
|
8
|
-
channel = Pusher.instance.channel(#{channel
|
9
|
-
channel.trigger(#{event
|
14
|
+
channel = Pusher.instance.channel(#{MultiJson.dump(channel)});
|
15
|
+
channel.trigger(#{MultiJson.dump(event)}, {});
|
10
16
|
})
|
11
17
|
end
|
12
18
|
|
13
19
|
When %{I manually trigger the "$event" event on the "$channel" channel} do |event, channel|
|
14
|
-
page.execute_script(%{Pusher.instance.send_event(#{event
|
20
|
+
page.execute_script(%{Pusher.instance.send_event(#{MultiJson.dump(event)}, {}, #{MultiJson.dump(channel)})})
|
15
21
|
end
|
16
22
|
|
17
23
|
Then /^([^ ]+) should receive a "([^"]+)" event on the "([^"]+)" channel$/ do |name, event, channel|
|
@@ -19,7 +25,7 @@ Then /^([^ ]+) should receive a "([^"]+)" event on the "([^"]+)" channel$/ do |n
|
|
19
25
|
|
20
26
|
using_session(name) do
|
21
27
|
wait do
|
22
|
-
events = page.evaluate_script("Pusher.instance.events[#{[channel, event].join(":")
|
28
|
+
events = page.evaluate_script("Pusher.instance.events[#{MultiJson.dump([channel, event].join(":"))}]")
|
23
29
|
events.length.should == 1
|
24
30
|
end
|
25
31
|
end
|
@@ -30,7 +36,7 @@ Then /^([^ ]+) should not receive a "([^"]+)" event on the "([^"]+)" channel$/ d
|
|
30
36
|
|
31
37
|
using_session(name) do
|
32
38
|
wait do
|
33
|
-
events = page.evaluate_script("Pusher.instance.events[#{[channel, event].join(":")
|
39
|
+
events = page.evaluate_script("Pusher.instance.events[#{MultiJson.dump([channel, event].join(":"))}]")
|
34
40
|
events.should be_nil
|
35
41
|
end
|
36
42
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
Then /^the server should have received the following (user )*event:$/ do |user_event, table|
|
2
|
+
event = table.transpose.hashes.first
|
3
|
+
|
4
|
+
using_session(event.delete("user")) do
|
5
|
+
page.evaluate_script("Pusher.instance.connection.socket_id").tap do |socket_id|
|
6
|
+
event.merge!("user_id" => socket_id.to_s)
|
7
|
+
end
|
8
|
+
end if user_event
|
9
|
+
|
10
|
+
Capybara.timeout do
|
11
|
+
$events.include?(event)
|
12
|
+
end
|
13
|
+
|
14
|
+
$events.replace([])
|
15
|
+
end
|
16
|
+
|
17
|
+
Then /^the server should have received no events$/ do
|
18
|
+
wait do
|
19
|
+
$events.should be_empty
|
20
|
+
end
|
21
|
+
end
|
@@ -5,24 +5,23 @@
|
|
5
5
|
</head>
|
6
6
|
<body>
|
7
7
|
|
8
|
-
<section
|
8
|
+
<section>
|
9
9
|
<header>
|
10
10
|
<h1><span>0</span> Clients</h1>
|
11
11
|
</header>
|
12
12
|
|
13
|
-
<ul>
|
14
|
-
</ul>
|
13
|
+
<ul></ul>
|
15
14
|
</section>
|
16
15
|
|
17
16
|
<script src="/javascripts/vendor/pusher-1.12.5.js"></script>
|
18
17
|
<script>
|
19
18
|
window.addEventListener("DOMContentLoaded", function() {
|
20
19
|
// Use the PusherFake server.
|
21
|
-
Pusher.host = <%= PusherFake.configuration.socket_host
|
22
|
-
Pusher.ws_port = <%= PusherFake.configuration.socket_port
|
20
|
+
Pusher.host = <%= MultiJson.dump(PusherFake.configuration.socket_host) %>;
|
21
|
+
Pusher.ws_port = <%= MultiJson.dump(PusherFake.configuration.socket_port) %>;
|
23
22
|
|
24
23
|
// Create the client instance.
|
25
|
-
Pusher.instance = new Pusher(<%= PusherFake.configuration.key
|
24
|
+
Pusher.instance = new Pusher(<%= MultiJson.dump(PusherFake.configuration.key) %>);
|
26
25
|
Pusher.instance.events = {};
|
27
26
|
|
28
27
|
// Force the connection to go unavailable after a single attempt.
|
@@ -0,0 +1,36 @@
|
|
1
|
+
$events = []
|
2
|
+
|
3
|
+
Thread.new do
|
4
|
+
# Not explicitly requiring Thin::Server occasionally results in
|
5
|
+
# Thin::Server.start not being defined.
|
6
|
+
require "thin"
|
7
|
+
require "thin/server"
|
8
|
+
|
9
|
+
class WebhookEndpoint
|
10
|
+
def self.call(environment)
|
11
|
+
request = Rack::Request.new(environment)
|
12
|
+
webhook = Pusher::WebHook.new(request)
|
13
|
+
|
14
|
+
if webhook.valid?
|
15
|
+
$events.concat(webhook.events)
|
16
|
+
end
|
17
|
+
|
18
|
+
Rack::Response.new.finish
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
EventMachine.run do
|
23
|
+
Thin::Logging.silent = true
|
24
|
+
Thin::Server.start('0.0.0.0', 8082, WebhookEndpoint)
|
25
|
+
Thread.current[:ready] = true
|
26
|
+
end
|
27
|
+
end.tap do |thread|
|
28
|
+
at_exit { thread.exit }
|
29
|
+
|
30
|
+
# Wait for the webhook endpoint server to start.
|
31
|
+
Capybara.timeout do
|
32
|
+
thread[:ready]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
PusherFake.configuration.webhooks = ["http://localhost:8082"]
|
data/lib/pusher-fake.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "em-http-request"
|
1
2
|
require "em-websocket"
|
2
3
|
require "multi_json"
|
3
4
|
require "openssl"
|
@@ -11,10 +12,11 @@ require "pusher-fake/configuration"
|
|
11
12
|
require "pusher-fake/connection"
|
12
13
|
require "pusher-fake/server"
|
13
14
|
require "pusher-fake/server/application"
|
15
|
+
require "pusher-fake/webhook"
|
14
16
|
|
15
17
|
module PusherFake
|
16
18
|
# The current version string.
|
17
|
-
VERSION = "0.
|
19
|
+
VERSION = "0.3.0"
|
18
20
|
|
19
21
|
# Call this method to modify the defaults.
|
20
22
|
#
|
@@ -19,6 +19,8 @@ module PusherFake
|
|
19
19
|
def remove(connection)
|
20
20
|
super
|
21
21
|
|
22
|
+
trigger("member_removed", channel: name, user_id: members[connection][:user_id])
|
23
|
+
|
22
24
|
emit("pusher_internal:member_removed", members.delete(connection))
|
23
25
|
end
|
24
26
|
|
@@ -43,9 +45,11 @@ module PusherFake
|
|
43
45
|
# @param [Connection] connection The connection a subscription succeeded for.
|
44
46
|
# @param [Hash] options The options for the channel.
|
45
47
|
def subscription_succeeded(connection, options = {})
|
46
|
-
members[connection] = MultiJson.load(options[:channel_data], symbolize_keys: true)
|
48
|
+
member = members[connection] = MultiJson.load(options[:channel_data], symbolize_keys: true)
|
49
|
+
|
50
|
+
emit("pusher_internal:member_added", member)
|
47
51
|
|
48
|
-
|
52
|
+
trigger("member_added", channel: name, user_id: member[:user_id])
|
49
53
|
|
50
54
|
super
|
51
55
|
end
|
@@ -46,6 +46,10 @@ module PusherFake
|
|
46
46
|
# @param [Connection] connection The connection to remove.
|
47
47
|
def remove(connection)
|
48
48
|
connections.delete(connection)
|
49
|
+
|
50
|
+
if connections.empty?
|
51
|
+
trigger("channel_vacated", channel: name)
|
52
|
+
end
|
49
53
|
end
|
50
54
|
|
51
55
|
# Return subscription data for the channel.
|
@@ -66,6 +70,14 @@ module PusherFake
|
|
66
70
|
def subscription_succeeded(connection, options = {})
|
67
71
|
connection.emit("pusher_internal:subscription_succeeded", subscription_data, name)
|
68
72
|
connections.push(connection)
|
73
|
+
|
74
|
+
if connections.length == 1
|
75
|
+
trigger("channel_occupied", channel: name)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def trigger(name, data = {})
|
80
|
+
PusherFake::Webhook.trigger(name, data)
|
69
81
|
end
|
70
82
|
end
|
71
83
|
end
|
@@ -21,6 +21,9 @@ module PusherFake
|
|
21
21
|
# @return [Fixnum] The port on which the web server listens. (Defaults to +8081+.)
|
22
22
|
attr_accessor :web_port
|
23
23
|
|
24
|
+
# @return [Array] An array of webhook URLs. (Defaults to +[]+.)
|
25
|
+
attr_accessor :webhooks
|
26
|
+
|
24
27
|
# Instantiated from {PusherFake.configuration}. Sets the defaults.
|
25
28
|
def initialize
|
26
29
|
self.app_id = "PUSHER_APP_ID"
|
@@ -30,6 +33,7 @@ module PusherFake
|
|
30
33
|
self.socket_port = 8080
|
31
34
|
self.web_host = "127.0.0.1"
|
32
35
|
self.web_port = 8081
|
36
|
+
self.webhooks = []
|
33
37
|
end
|
34
38
|
end
|
35
39
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module PusherFake
|
2
|
+
class Webhook
|
3
|
+
class << self
|
4
|
+
def trigger(name, data = {})
|
5
|
+
payload = MultiJson.dump({
|
6
|
+
events: [data.merge(name: name)],
|
7
|
+
time_ms: Time.now.to_i
|
8
|
+
})
|
9
|
+
|
10
|
+
PusherFake.configuration.webhooks.each do |url|
|
11
|
+
http = EventMachine::HttpRequest.new(url)
|
12
|
+
http.post(body: payload, head: headers_for(payload))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def headers_for(payload)
|
19
|
+
{ "Content-Type" => "application/json",
|
20
|
+
"X-Pusher-Key" => PusherFake.configuration.key,
|
21
|
+
"X-Pusher-Signature" => signature_for(payload)
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def signature_for(payload)
|
26
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, PusherFake.configuration.secret, payload)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -16,16 +16,19 @@ describe PusherFake::Channel::Presence do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
describe PusherFake::Channel::Presence, "#add" do
|
19
|
-
let(:data) { { auth: authentication, channel_data:
|
19
|
+
let(:data) { { auth: authentication, channel_data: MultiJson.dump(channel_data) } }
|
20
|
+
let(:name) { "name" }
|
21
|
+
let(:user_id) { "1234" }
|
20
22
|
let(:connection) { stub(emit: nil) }
|
21
|
-
let(:connections) { stub(push: nil) }
|
22
|
-
let(:channel_data) { {} }
|
23
|
+
let(:connections) { stub(push: nil, length: 0) }
|
24
|
+
let(:channel_data) { { user_id: user_id } }
|
23
25
|
let(:authentication) { "auth" }
|
24
26
|
let(:subscription_data) { { presence: { hash: {}, count: 1 } } }
|
25
27
|
|
26
|
-
subject { PusherFake::Channel::Presence.new(
|
28
|
+
subject { PusherFake::Channel::Presence.new(name) }
|
27
29
|
|
28
30
|
before do
|
31
|
+
PusherFake::Webhook.stubs(:trigger)
|
29
32
|
MultiJson.stubs(:load).returns(channel_data)
|
30
33
|
subject.stubs(connections: connections, emit: nil, subscription_data: subscription_data)
|
31
34
|
end
|
@@ -66,20 +69,55 @@ describe PusherFake::Channel::Presence, "#add" do
|
|
66
69
|
connections.should have_received(:push).with(connection)
|
67
70
|
end
|
68
71
|
|
72
|
+
it "triggers channel occupied webhook for the first connection added when authorized" do
|
73
|
+
subject.unstub(:connections)
|
74
|
+
subject.stubs(authorized?: true)
|
75
|
+
|
76
|
+
subject.add(connection, data)
|
77
|
+
PusherFake::Webhook.should have_received(:trigger).with("channel_occupied", channel: name).once
|
78
|
+
subject.add(connection, data)
|
79
|
+
PusherFake::Webhook.should have_received(:trigger).with("channel_occupied", channel: name).once
|
80
|
+
end
|
81
|
+
|
82
|
+
it "triggers the member added webhook when authorized" do
|
83
|
+
subject.stubs(authorized?: true)
|
84
|
+
subject.add(connection, data)
|
85
|
+
PusherFake::Webhook.should have_received(:trigger).with("member_added", channel: name, user_id: user_id).once
|
86
|
+
end
|
87
|
+
|
69
88
|
it "unsuccessfully subscribes the connection when not authorized" do
|
70
89
|
subject.stubs(authorized?: false)
|
71
90
|
subject.add(connection, data)
|
72
91
|
connection.should have_received(:emit).with("pusher_internal:subscription_error", {}, subject.name)
|
73
92
|
end
|
93
|
+
|
94
|
+
it "does not trigger channel occupied webhook when not authorized" do
|
95
|
+
subject.unstub(:connections)
|
96
|
+
subject.stubs(authorized?: false)
|
97
|
+
|
98
|
+
subject.add(connection, data)
|
99
|
+
PusherFake::Webhook.should have_received(:trigger).never
|
100
|
+
subject.add(connection, data)
|
101
|
+
PusherFake::Webhook.should have_received(:trigger).never
|
102
|
+
end
|
103
|
+
|
104
|
+
it "does not trigger the member added webhook when not authorized" do
|
105
|
+
subject.stubs(authorized?: false)
|
106
|
+
subject.add(connection, data)
|
107
|
+
PusherFake::Webhook.should have_received(:trigger).never
|
108
|
+
end
|
74
109
|
end
|
75
110
|
|
76
111
|
describe PusherFake::Channel::Presence, "#remove" do
|
112
|
+
let(:name) { "name" }
|
113
|
+
let(:user_id) { "1234" }
|
77
114
|
let(:connection) { stub }
|
78
|
-
let(:channel_data) { {} }
|
115
|
+
let(:channel_data) { { user_id: user_id } }
|
79
116
|
|
80
|
-
subject { PusherFake::Channel::Presence.new(
|
117
|
+
subject { PusherFake::Channel::Presence.new(name) }
|
81
118
|
|
82
119
|
before do
|
120
|
+
PusherFake::Webhook.stubs(:trigger)
|
83
121
|
subject.members[connection] = channel_data
|
84
122
|
subject.stubs(connections: [connection], emit: nil)
|
85
123
|
end
|
@@ -94,6 +132,11 @@ describe PusherFake::Channel::Presence, "#remove" do
|
|
94
132
|
subject.members.should_not have_key(connection)
|
95
133
|
end
|
96
134
|
|
135
|
+
it "triggers the member removed webhook" do
|
136
|
+
subject.remove(connection)
|
137
|
+
PusherFake::Webhook.should have_received(:trigger).with("member_removed", channel: name, user_id: user_id).once
|
138
|
+
end
|
139
|
+
|
97
140
|
it "notifies the channel of the removed member" do
|
98
141
|
subject.remove(connection)
|
99
142
|
subject.should have_received(:emit).with("pusher_internal:member_removed", channel_data)
|
@@ -10,13 +10,15 @@ end
|
|
10
10
|
|
11
11
|
describe PusherFake::Channel::Private, "#add" do
|
12
12
|
let(:data) { { auth: authentication } }
|
13
|
+
let(:name) { "name" }
|
13
14
|
let(:connection) { stub(emit: nil) }
|
14
|
-
let(:connections) { stub(push: nil) }
|
15
|
+
let(:connections) { stub(push: nil, length: 0) }
|
15
16
|
let(:authentication) { "auth" }
|
16
17
|
|
17
|
-
subject { PusherFake::Channel::Private.new(
|
18
|
+
subject { PusherFake::Channel::Private.new(name) }
|
18
19
|
|
19
20
|
before do
|
21
|
+
PusherFake::Webhook.stubs(:trigger)
|
20
22
|
subject.stubs(connections: connections)
|
21
23
|
end
|
22
24
|
|
@@ -38,11 +40,31 @@ describe PusherFake::Channel::Private, "#add" do
|
|
38
40
|
connection.should have_received(:emit).with("pusher_internal:subscription_succeeded", {}, subject.name)
|
39
41
|
end
|
40
42
|
|
43
|
+
it "triggers channel occupied webhook for the first connection added when authorized" do
|
44
|
+
subject.unstub(:connections)
|
45
|
+
subject.stubs(authorized?: true)
|
46
|
+
|
47
|
+
subject.add(connection, data)
|
48
|
+
PusherFake::Webhook.should have_received(:trigger).with("channel_occupied", channel: name).once
|
49
|
+
subject.add(connection, data)
|
50
|
+
PusherFake::Webhook.should have_received(:trigger).with("channel_occupied", channel: name).once
|
51
|
+
end
|
52
|
+
|
41
53
|
it "unsuccessfully subscribes the connection when not authorized" do
|
42
54
|
subject.stubs(authorized?: false)
|
43
55
|
subject.add(connection, data)
|
44
56
|
connection.should have_received(:emit).with("pusher_internal:subscription_error", {}, subject.name)
|
45
57
|
end
|
58
|
+
|
59
|
+
it "does not trigger channel occupied webhook when not authorized" do
|
60
|
+
subject.unstub(:connections)
|
61
|
+
subject.stubs(authorized?: false)
|
62
|
+
|
63
|
+
subject.add(connection, data)
|
64
|
+
PusherFake::Webhook.should have_received(:trigger).never
|
65
|
+
subject.add(connection, data)
|
66
|
+
PusherFake::Webhook.should have_received(:trigger).never
|
67
|
+
end
|
46
68
|
end
|
47
69
|
|
48
70
|
describe PusherFake::Channel::Private, "#authentication_for" do
|
@@ -17,12 +17,14 @@ describe PusherFake::Channel::Public do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
describe PusherFake::Channel, "#add" do
|
20
|
+
let(:name) { "name" }
|
20
21
|
let(:connection) { stub(emit: nil) }
|
21
|
-
let(:connections) { stub(push: nil) }
|
22
|
+
let(:connections) { stub(push: nil, length: 0) }
|
22
23
|
|
23
|
-
subject { PusherFake::Channel::Public.new(
|
24
|
+
subject { PusherFake::Channel::Public.new(name) }
|
24
25
|
|
25
26
|
before do
|
27
|
+
PusherFake::Webhook.stubs(:trigger)
|
26
28
|
subject.stubs(connections: connections)
|
27
29
|
end
|
28
30
|
|
@@ -35,6 +37,15 @@ describe PusherFake::Channel, "#add" do
|
|
35
37
|
subject.add(connection)
|
36
38
|
connection.should have_received(:emit).with("pusher_internal:subscription_succeeded", {}, subject.name)
|
37
39
|
end
|
40
|
+
|
41
|
+
it "triggers channel occupied webhook for the first connection added" do
|
42
|
+
subject.unstub(:connections)
|
43
|
+
|
44
|
+
subject.add(connection)
|
45
|
+
PusherFake::Webhook.should have_received(:trigger).with("channel_occupied", channel: name).once
|
46
|
+
subject.add(connection)
|
47
|
+
PusherFake::Webhook.should have_received(:trigger).with("channel_occupied", channel: name).once
|
48
|
+
end
|
38
49
|
end
|
39
50
|
|
40
51
|
describe PusherFake::Channel, "#emit" do
|
@@ -75,17 +86,27 @@ describe PusherFake::Channel, "#includes?" do
|
|
75
86
|
end
|
76
87
|
|
77
88
|
describe PusherFake::Channel, "#remove" do
|
78
|
-
let(:
|
89
|
+
let(:name) { "name" }
|
90
|
+
let(:connection_1) { stub }
|
91
|
+
let(:connection_2) { stub }
|
79
92
|
|
80
|
-
subject { PusherFake::Channel::Public.new(
|
93
|
+
subject { PusherFake::Channel::Public.new(name) }
|
81
94
|
|
82
95
|
before do
|
83
|
-
subject.stubs(connections: [
|
96
|
+
subject.stubs(connections: [connection_1, connection_2])
|
97
|
+
PusherFake::Webhook.stubs(:trigger)
|
84
98
|
end
|
85
99
|
|
86
100
|
it "removes the connection from the channel" do
|
87
|
-
subject.remove(
|
88
|
-
subject.connections.
|
101
|
+
subject.remove(connection_1)
|
102
|
+
subject.connections.should_not include(connection_1)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "triggers channel vacated webhook when all connections are removed" do
|
106
|
+
subject.remove(connection_1)
|
107
|
+
PusherFake::Webhook.should have_received(:trigger).never
|
108
|
+
subject.remove(connection_2)
|
109
|
+
PusherFake::Webhook.should have_received(:trigger).with("channel_vacated", channel: name).once
|
89
110
|
end
|
90
111
|
end
|
91
112
|
|
@@ -6,22 +6,25 @@ describe PusherFake::Channel, ".factory" do
|
|
6
6
|
|
7
7
|
subject { PusherFake::Channel }
|
8
8
|
|
9
|
+
before do
|
10
|
+
PusherFake::Channel::Public.stubs(new: channel)
|
11
|
+
end
|
12
|
+
|
9
13
|
after do
|
10
14
|
PusherFake::Channel.reset
|
11
15
|
end
|
12
16
|
|
13
17
|
it "caches the channel" do
|
18
|
+
PusherFake::Channel::Public.unstub(:new)
|
14
19
|
subject.factory(name).should == subject.factory(name)
|
15
20
|
end
|
16
21
|
|
17
22
|
it "creates a public channel by name" do
|
18
|
-
PusherFake::Channel::Public.stubs(new: channel)
|
19
23
|
subject.factory(name)
|
20
24
|
PusherFake::Channel::Public.should have_received(:new).with(name)
|
21
25
|
end
|
22
26
|
|
23
27
|
it "returns the channel instance" do
|
24
|
-
PusherFake::Channel::Public.stubs(new: channel)
|
25
28
|
subject.factory(name).should == channel
|
26
29
|
end
|
27
30
|
end
|
@@ -32,22 +35,25 @@ describe PusherFake::Channel, ".factory, for a private channel" do
|
|
32
35
|
|
33
36
|
subject { PusherFake::Channel }
|
34
37
|
|
38
|
+
before do
|
39
|
+
PusherFake::Channel::Private.stubs(new: channel)
|
40
|
+
end
|
41
|
+
|
35
42
|
after do
|
36
43
|
PusherFake::Channel.reset
|
37
44
|
end
|
38
45
|
|
39
46
|
it "caches the channel" do
|
47
|
+
PusherFake::Channel::Private.unstub(:new)
|
40
48
|
subject.factory(name).should == subject.factory(name)
|
41
49
|
end
|
42
50
|
|
43
51
|
it "creates a private channel by name" do
|
44
|
-
PusherFake::Channel::Private.stubs(new: channel)
|
45
52
|
subject.factory(name)
|
46
53
|
PusherFake::Channel::Private.should have_received(:new).with(name)
|
47
54
|
end
|
48
55
|
|
49
56
|
it "returns the channel instance" do
|
50
|
-
PusherFake::Channel::Private.stubs(new: channel)
|
51
57
|
subject.factory(name).should == channel
|
52
58
|
end
|
53
59
|
end
|
@@ -58,22 +64,25 @@ describe PusherFake::Channel, ".factory, for a presence channel" do
|
|
58
64
|
|
59
65
|
subject { PusherFake::Channel }
|
60
66
|
|
67
|
+
before do
|
68
|
+
PusherFake::Channel::Presence.stubs(new: channel)
|
69
|
+
end
|
70
|
+
|
61
71
|
after do
|
62
72
|
PusherFake::Channel.reset
|
63
73
|
end
|
64
74
|
|
65
75
|
it "caches the channel" do
|
76
|
+
PusherFake::Channel::Presence.unstub(:new)
|
66
77
|
subject.factory(name).should == subject.factory(name)
|
67
78
|
end
|
68
79
|
|
69
80
|
it "creates a presence channel by name" do
|
70
|
-
PusherFake::Channel::Presence.stubs(new: channel)
|
71
81
|
subject.factory(name)
|
72
82
|
PusherFake::Channel::Presence.should have_received(:new).with(name)
|
73
83
|
end
|
74
84
|
|
75
85
|
it "returns the channel instance" do
|
76
|
-
PusherFake::Channel::Presence.stubs(new: channel)
|
77
86
|
subject.factory(name).should == channel
|
78
87
|
end
|
79
88
|
end
|
@@ -116,6 +125,7 @@ describe PusherFake::Channel, ".reset" do
|
|
116
125
|
subject { PusherFake::Channel }
|
117
126
|
|
118
127
|
it "empties the channel cache" do
|
128
|
+
subject.factory("example")
|
119
129
|
subject.reset
|
120
130
|
subject.channels.should == {}
|
121
131
|
end
|
@@ -8,4 +8,5 @@ describe PusherFake::Configuration do
|
|
8
8
|
it { should have_configuration_option(:socket_port).with_default(8080) }
|
9
9
|
it { should have_configuration_option(:web_host).with_default("127.0.0.1") }
|
10
10
|
it { should have_configuration_option(:web_port).with_default(8081) }
|
11
|
+
it { should have_configuration_option(:webhooks).with_default([]) }
|
11
12
|
end
|
@@ -10,7 +10,7 @@ describe PusherFake::Server, ".start" do
|
|
10
10
|
|
11
11
|
it "runs the event loop" do
|
12
12
|
subject.start
|
13
|
-
EventMachine.should have_received(:run)
|
13
|
+
EventMachine.should have_received(:run).with()
|
14
14
|
end
|
15
15
|
|
16
16
|
it "starts the socket web server when run yields" do
|
@@ -20,7 +20,7 @@ describe PusherFake::Server, ".start" do
|
|
20
20
|
EventMachine.stubs(:run).yields
|
21
21
|
|
22
22
|
subject.start
|
23
|
-
subject.should have_received(:start_web_server)
|
23
|
+
subject.should have_received(:start_web_server).with()
|
24
24
|
end
|
25
25
|
|
26
26
|
it "starts the socket server when run yields" do
|
@@ -30,7 +30,7 @@ describe PusherFake::Server, ".start" do
|
|
30
30
|
EventMachine.stubs(:run).yields
|
31
31
|
|
32
32
|
subject.start
|
33
|
-
subject.should have_received(:start_socket_server)
|
33
|
+
subject.should have_received(:start_socket_server).with()
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe PusherFake::Webhook, ".trigger" do
|
4
|
+
subject { PusherFake::Webhook }
|
5
|
+
|
6
|
+
let(:data) { { channel: "name" } }
|
7
|
+
let(:http) { stub(post: true) }
|
8
|
+
let(:name) { "channel_occupied" }
|
9
|
+
let(:payload) { MultiJson.dump({ events: [data.merge(name: name)], time_ms: Time.now.to_i }) }
|
10
|
+
let(:webhooks) { ["url"] }
|
11
|
+
let(:signature) { "signature" }
|
12
|
+
let(:configuration) { stub(key: "key", secret: "secret", webhooks: webhooks) }
|
13
|
+
|
14
|
+
before do
|
15
|
+
OpenSSL::HMAC.stubs(hexdigest: signature)
|
16
|
+
EventMachine::HttpRequest.stubs(new: http)
|
17
|
+
PusherFake.stubs(configuration: configuration)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "generates a signature" do
|
21
|
+
subject.trigger(name, data)
|
22
|
+
OpenSSL::HMAC.should have_received(:hexdigest).with(kind_of(OpenSSL::Digest::SHA256), configuration.secret, payload)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "creates a HTTP request for each webhook URL" do
|
26
|
+
subject.trigger(name, data)
|
27
|
+
EventMachine::HttpRequest.should have_received(:new).with(webhooks.first)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "posts the payload to the webhook URL" do
|
31
|
+
subject.trigger(name, data)
|
32
|
+
http.should have_received(:post).with(
|
33
|
+
body: payload,
|
34
|
+
head: {
|
35
|
+
"Content-Type" => "application/json",
|
36
|
+
"X-Pusher-Key" => configuration.key,
|
37
|
+
"X-Pusher-Signature" => signature
|
38
|
+
}
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,11 +6,8 @@ module BartenderHelper
|
|
6
6
|
|
7
7
|
def matches?(configuration)
|
8
8
|
@configuration = configuration
|
9
|
-
|
10
9
|
@configuration.respond_to?(@option).should == true
|
11
|
-
|
12
10
|
@configuration.__send__(@option).should == @default if instance_variables.include?("@default")
|
13
|
-
|
14
11
|
@configuration.__send__(:"#{@option}=", "value")
|
15
12
|
@configuration.__send__(@option).should == "value"
|
16
13
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pusher-fake
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,16 +9,16 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-12-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name: em-
|
15
|
+
name: em-http-request
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - '='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.3
|
21
|
+
version: 1.0.3
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,15 +26,15 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - '='
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 0.3
|
29
|
+
version: 1.0.3
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
31
|
+
name: em-websocket
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
none: false
|
34
34
|
requirements:
|
35
35
|
- - '='
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version:
|
37
|
+
version: 0.3.8
|
38
38
|
type: :runtime
|
39
39
|
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -42,15 +42,15 @@ dependencies:
|
|
42
42
|
requirements:
|
43
43
|
- - '='
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
version:
|
45
|
+
version: 0.3.8
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
47
|
+
name: thin
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
49
49
|
none: false
|
50
50
|
requirements:
|
51
51
|
- - '='
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 1.
|
53
|
+
version: 1.5.0
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -58,47 +58,31 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - '='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.
|
61
|
+
version: 1.5.0
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
|
-
name:
|
63
|
+
name: multi_json
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
65
65
|
none: false
|
66
66
|
requirements:
|
67
67
|
- - '='
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: 1.
|
70
|
-
type: :
|
69
|
+
version: 1.4.0
|
70
|
+
type: :runtime
|
71
71
|
prerelease: false
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
75
|
- - '='
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version: 1.
|
77
|
+
version: 1.4.0
|
78
78
|
- !ruby/object:Gem::Dependency
|
79
|
-
name:
|
80
|
-
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
|
-
requirements:
|
83
|
-
- - ! '>='
|
84
|
-
- !ruby/object:Gem::Version
|
85
|
-
version: 1.1.0
|
86
|
-
type: :development
|
87
|
-
prerelease: false
|
88
|
-
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
|
-
requirements:
|
91
|
-
- - ! '>='
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
version: 1.1.0
|
94
|
-
- !ruby/object:Gem::Dependency
|
95
|
-
name: capybara
|
79
|
+
name: bourne
|
96
80
|
requirement: !ruby/object:Gem::Requirement
|
97
81
|
none: false
|
98
82
|
requirements:
|
99
83
|
- - '='
|
100
84
|
- !ruby/object:Gem::Version
|
101
|
-
version: 1.
|
85
|
+
version: 1.3.0
|
102
86
|
type: :development
|
103
87
|
prerelease: false
|
104
88
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -106,7 +90,7 @@ dependencies:
|
|
106
90
|
requirements:
|
107
91
|
- - '='
|
108
92
|
- !ruby/object:Gem::Version
|
109
|
-
version: 1.
|
93
|
+
version: 1.3.0
|
110
94
|
- !ruby/object:Gem::Dependency
|
111
95
|
name: capybara-webkit
|
112
96
|
requirement: !ruby/object:Gem::Requirement
|
@@ -249,22 +233,26 @@ files:
|
|
249
233
|
- lib/pusher-fake/connection.rb
|
250
234
|
- lib/pusher-fake/server/application.rb
|
251
235
|
- lib/pusher-fake/server.rb
|
236
|
+
- lib/pusher-fake/webhook.rb
|
252
237
|
- lib/pusher-fake.rb
|
253
238
|
- features/channel_presence.feature
|
254
239
|
- features/channel_subscribe.feature
|
255
240
|
- features/channel_trigger.feature
|
241
|
+
- features/channel_webhooks.feature
|
256
242
|
- features/client_connect.feature
|
257
243
|
- features/step_definitions/channel_steps.rb
|
258
244
|
- features/step_definitions/client_steps.rb
|
259
245
|
- features/step_definitions/event_steps.rb
|
260
246
|
- features/step_definitions/navigation_steps.rb
|
261
247
|
- features/step_definitions/presence_steps.rb
|
248
|
+
- features/step_definitions/webhook_steps.rb
|
262
249
|
- features/support/application/public/javascripts/vendor/pusher-1.12.5.js
|
263
250
|
- features/support/application/views/index.erb
|
264
251
|
- features/support/application.rb
|
265
252
|
- features/support/environment.rb
|
266
253
|
- features/support/pusher-fake.rb
|
267
254
|
- features/support/wait.rb
|
255
|
+
- features/support/webhooks.rb
|
268
256
|
- spec/lib/pusher-fake/channel/presence_spec.rb
|
269
257
|
- spec/lib/pusher-fake/channel/private_spec.rb
|
270
258
|
- spec/lib/pusher-fake/channel/public_spec.rb
|
@@ -273,6 +261,7 @@ files:
|
|
273
261
|
- spec/lib/pusher-fake/connection_spec.rb
|
274
262
|
- spec/lib/pusher-fake/server/application_spec.rb
|
275
263
|
- spec/lib/pusher-fake/server_spec.rb
|
264
|
+
- spec/lib/pusher-fake/webhook_spec.rb
|
276
265
|
- spec/spec_helper.rb
|
277
266
|
- spec/support/have_configuration_option_matcher.rb
|
278
267
|
homepage: http://github.com/tristandunn/pusher-fake
|
@@ -290,7 +279,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
290
279
|
version: '0'
|
291
280
|
segments:
|
292
281
|
- 0
|
293
|
-
hash:
|
282
|
+
hash: 4186586164521781753
|
294
283
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
295
284
|
none: false
|
296
285
|
requirements:
|
@@ -299,7 +288,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
299
288
|
version: '0'
|
300
289
|
segments:
|
301
290
|
- 0
|
302
|
-
hash:
|
291
|
+
hash: 4186586164521781753
|
303
292
|
requirements: []
|
304
293
|
rubyforge_project:
|
305
294
|
rubygems_version: 1.8.23
|
@@ -310,18 +299,21 @@ test_files:
|
|
310
299
|
- features/channel_presence.feature
|
311
300
|
- features/channel_subscribe.feature
|
312
301
|
- features/channel_trigger.feature
|
302
|
+
- features/channel_webhooks.feature
|
313
303
|
- features/client_connect.feature
|
314
304
|
- features/step_definitions/channel_steps.rb
|
315
305
|
- features/step_definitions/client_steps.rb
|
316
306
|
- features/step_definitions/event_steps.rb
|
317
307
|
- features/step_definitions/navigation_steps.rb
|
318
308
|
- features/step_definitions/presence_steps.rb
|
309
|
+
- features/step_definitions/webhook_steps.rb
|
319
310
|
- features/support/application/public/javascripts/vendor/pusher-1.12.5.js
|
320
311
|
- features/support/application/views/index.erb
|
321
312
|
- features/support/application.rb
|
322
313
|
- features/support/environment.rb
|
323
314
|
- features/support/pusher-fake.rb
|
324
315
|
- features/support/wait.rb
|
316
|
+
- features/support/webhooks.rb
|
325
317
|
- spec/lib/pusher-fake/channel/presence_spec.rb
|
326
318
|
- spec/lib/pusher-fake/channel/private_spec.rb
|
327
319
|
- spec/lib/pusher-fake/channel/public_spec.rb
|
@@ -330,6 +322,7 @@ test_files:
|
|
330
322
|
- spec/lib/pusher-fake/connection_spec.rb
|
331
323
|
- spec/lib/pusher-fake/server/application_spec.rb
|
332
324
|
- spec/lib/pusher-fake/server_spec.rb
|
325
|
+
- spec/lib/pusher-fake/webhook_spec.rb
|
333
326
|
- spec/spec_helper.rb
|
334
327
|
- spec/support/have_configuration_option_matcher.rb
|
335
328
|
has_rdoc:
|