module Boty RSpec.describe Bot do include Boty subject(:bot) { described_class.new bot_info } let(:bot_info) do { "id" => "666", "name" => "jabber" } end describe "#on" do it "binds an event `'type'` to a block" do data = { "type" => "omg", "text" => "lol" } expect { |b| bot.on :omg, &b bot.event data }.to yield_with_args data end end describe "#off" do let(:data) do { "type" => "bbq", "text" => "omg. wtf." } end it "unbounds an event by type AND block, just if the block is the same" do @permanent_executed = false permanent_block = ->(_) do @permanent_executed = true end bot.on :bbq, &permanent_block @ephemeral_executed = false ephemeral_block = ->(_) do @ephemeral_executed = true end bot.on :bbq, &ephemeral_block bot.off :bbq, &ephemeral_block bot.event data expect(@permanent_executed).to eq true expect(@ephemeral_executed).to eq false end it "unbounds all events by type" do @first_block = @second_block = @third_block = false bot.on(:bbq) do |_| @first_block = true end bot.on(:bbq) do |_| @second_block = true end bot.on(:bbq) do |_| @third_block = true end bot.off(:bbq) bot.event data expect(@first_block).to eq false expect(@second_block).to eq false expect(@third_block).to eq false end end describe "#match", :users do let(:data) do { "type" => "message", "text" => "bbq omg lol" } end it "binds a regex to events of `type => message`" do message = nil bot.match(/omg/i) do message = self.message end bot.event data expect(message).to be_a Boty::Slack::Message expect(message.text).to eq "bbq omg lol" expect(message.match[0]).to eq "omg" end it "binds various actions to events of type message" do iterations = [] bot.match(/omg/i) do iterations << "first" end bot.match(/omg/i) do iterations << "second" end bot.event data expect(iterations).to eq %w(first second) end it "passes the matched strings as parameters to block action" do rspec = self bot.match(/(bbq) omg (lol)/i) do |bbq, lol| rspec.expect(bbq).to rspec.eq "bbq" rspec.expect(lol).to rspec.eq "lol" end bot.event data end it "evals the block in the context of the bot" do dsl = nil bot.match(/omg/) do dsl = self end bot.event data expect(dsl.bot).to eq bot end end describe "#respond", :users do let(:data) do { "type" => "message", "text" => "hey <@jabber>, omg me" } end it "binds actions for messages only when there is a mention (@bot)" do expect { |b| bot.respond(/omg me/, &b) bot.event data }.to yield_control end it "recognizes the id when it's mentioned" do data["text"] = "hey <@666>: omg me" expect { |b| bot.respond(/omg me/, &b) bot.event data }.to yield_control end it "does not bind when regex matches but there is no mention" do data["text"] = "hey <@other_bot>, omg me" expect { |b| bot.respond(/omg me/, &b) bot.event data }.to_not yield_control end it "ignores mentions from itself" do data["user"] = "666" expect { |b| bot.respond(/omg me/, &b) bot.event data }.to_not yield_control end it "evals the block in the context of the bot" do dsl = nil bot.respond(/omg me/) do dsl = self end bot.event data expect(dsl.bot).to eq bot end end context "removing binds for messages and responses", :session do before do start_session end describe "#no_match" do let(:data) do { "type" => "message", "text" => "bbq omg lol" } end it "removes a message based on the regex AND the block" do permanent_executed = false permanent_block = ->() { permanent_executed = true } bot.match(/omg/i, &permanent_block) ephemeral_executed = false ephemeral_block = ->() { ephemeral_executed = true } bot.match(/omg/i, &ephemeral_block) bot.no_match(/omg/i, &ephemeral_block) bot.event data expect(permanent_executed).to eq true expect(ephemeral_executed).to eq false end it "removes all binds for a specific regex" do first = second = false bot.match(/omg/i) do first = true end bot.match(/omg/i) do second = true end bot.no_match(/omg/i) bot.event data expect(first).to eq false expect(second).to eq false end end describe "#no_respond" do let(:data) do { "type" => "message", "text" => "hey <@#{bot.name}>, omg me" } end it "removes a message based on the regex AND the block" do permanent_executed = false permanent_block = ->() { permanent_executed = true } bot.respond(/omg/i, &permanent_block) ephemeral_executed = false ephemeral_block = ->() { ephemeral_executed = true } bot.respond(/omg/i, &ephemeral_block) bot.no_respond(/omg/i, &ephemeral_block) dsl.bot.event data expect(permanent_executed).to eq true expect(ephemeral_executed).to eq false end it "removes all binds for a specific regex" do first = second = false bot.respond(/omg/i) do first = true end bot.respond(/omg/i) do second = true end bot.no_respond(/omg/i) dsl.bot.event data expect(first).to eq false expect(second).to eq false end end end describe "#say" do it "sends a string using the slack api to general (default) channel" do expect(Slack.chat).to receive(:post_message) .with "something", channel: "#general" bot.say "something" end it "sends a message to the channel specified by parameter" do expect(Slack.chat).to receive(:post_message) .with "omg pugs!", channel: "#omg" bot.say "omg pugs!", channel: "#omg" end it "accepts extra parameters and send them to the slack api" do expect(Slack.chat).to receive(:post_message) .with "omg pugs!", channel: "#omg", omg: "lol" bot.say "omg pugs!", channel: "#omg", omg: "lol" end it "logs the response for the request sending the message" do logger = Boty::Logger.adapter = Boty::Logger::Memory.new allow(Slack.chat).to receive(:post_message).and_return "yay" bot.say "omg pugs!", channel: "#omg", omg: "lol" expect(logger.logs.last).to eq "Post response: yay." end end describe "#im" do before do user = Boty::Slack::User.new "id" => "U123", "name" => "julian" allow(Slack.users).to receive(:by_name).with("julian") .and_return user end it "sends a message in the back channel (particular, im...)" do expect(Boty::Slack.chat).to receive(:post_im).with "U4321", "lol" bot.im "lol", user_id: "U4321" end it "sends a message in the back channel to a particular user" do expect(Slack.chat).to receive(:post_im).with "U123", "lol" bot.im "lol", destiny: "julian" end it "logs that it will send an im", :logger do allow(Slack.chat).to receive(:post_im) bot.im "lol", destiny: "julian" expect(logger.logs.last).to eq "Sending lol to julian." end it "logs when fails on discover the destiny", :logger do allow(Slack.users).to receive(:by_name) .with("omg_doesnt_exists").and_return nil bot.im "lol", destiny: "omg_doesnt_exists" expect(logger.logs.last).to eq "User not found, refusing to send im." end end describe "#brain", :users do it "exposes a hash that lives while bot is alive. =)" do bot.brain[:something] = "going on" expect(bot.brain[:something]).to eq "going on" end end describe "#desc" do let(:know_how) { bot.know_how.map { |action| { action.desc.command => action.desc.description } } } it "describes the next command to be created" do bot.desc "omg", "lol all the way down" bot.respond(/omg/) expect(know_how).to include( "omg" => "lol all the way down" ) end it "describes the next match do be created" do bot.desc "ula", "babula" bot.match(/ula/) expect(know_how).to include( "ula" => "babula" ) end it "stores the description for a regex when no command usage is given" do bot.desc "just a description" bot.match(/just desc/i) expect(know_how).to include( "/just desc/i" => "just a description" ) end it "presents the commands without description as regexes" do bot.match(/without desc/i) expect(know_how).to include( "/without desc/i" => nil ) end it "knows when a regex is case insensitive" do bot.match(/without desc insensitive/) expect(know_how).to include( "/without desc insensitive/" => nil ) end end end end