Merge discourse-narrative-bot into core plugins.

This commit is contained in:
Guo Xiang Tan
2017-05-24 13:50:20 +08:00
parent 796a2967af
commit 7f0561b621
52 changed files with 7298 additions and 0 deletions

View File

@ -0,0 +1,658 @@
require 'rails_helper'
RSpec.describe DiscourseNarrativeBot::AdvancedUserNarrative do
let(:discobot_user) { User.find(-2) }
let(:first_post) { Fabricate(:post, user: discobot_user) }
let(:user) { Fabricate(:user) }
let(:topic) do
Fabricate(:private_message_topic, first_post: first_post,
topic_allowed_users: [
Fabricate.build(:topic_allowed_user, user: discobot_user),
Fabricate.build(:topic_allowed_user, user: user),
]
)
end
let(:post) { Fabricate(:post, topic: topic, user: user) }
let(:narrative) { described_class.new }
let(:other_topic) { Fabricate(:topic) }
let(:other_post) { Fabricate(:post, topic: other_topic) }
let(:skip_trigger) { DiscourseNarrativeBot::TrackSelector.skip_trigger }
let(:reset_trigger) { DiscourseNarrativeBot::TrackSelector.reset_trigger }
before do
SiteSetting.discourse_narrative_bot_enabled = true
end
describe '.can_start?' do
describe 'when user is a moderator' do
it 'should return true' do
user.update!(moderator: true)
expect(described_class.can_start?(user)).to eq(true)
end
end
end
describe '#notify_timeout' do
before do
narrative.set_data(user,
state: :tutorial_poll,
topic_id: topic.id,
last_post_id: post.id
)
end
it 'should create the right message' do
expect { narrative.notify_timeout(user) }.to change { Post.count }.by(1)
expect(Post.last.raw).to eq(I18n.t(
'discourse_narrative_bot.timeout.message',
username: user.username,
skip_trigger: skip_trigger,
reset_trigger: "#{reset_trigger} #{described_class.reset_trigger}",
))
end
end
describe '#reset_bot' do
before do
narrative.set_data(user, state: :tutorial_images, topic_id: topic.id)
end
context 'when trigger is initiated in a PM' do
let(:user) { Fabricate(:user) }
let(:topic) do
topic_allowed_user = Fabricate.build(:topic_allowed_user, user: user)
bot = Fabricate.build(:topic_allowed_user, user: discobot_user)
Fabricate(:private_message_topic, topic_allowed_users: [topic_allowed_user, bot])
end
let(:post) { Fabricate(:post, topic: topic) }
it 'should reset the bot' do
narrative.reset_bot(user, post)
expected_raw = I18n.t(
'discourse_narrative_bot.advanced_user_narrative.start_message',
username: user.username
)
expected_raw = <<~RAW
#{expected_raw}
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.edit.instructions')}
RAW
new_post = Post.offset(1).last
expect(narrative.get_data(user)).to eq({
"topic_id" => topic.id,
"state" => "tutorial_edit",
"last_post_id" => new_post.id,
"track" => described_class.to_s,
"tutorial_edit" => {
"post_id" => Post.last.id
}
})
expect(new_post.raw).to eq(expected_raw.chomp)
expect(new_post.topic.id).to eq(topic.id)
end
end
context 'when trigger is not initiated in a PM' do
it 'should start the new track in a PM' do
narrative.reset_bot(user, other_post)
expected_raw = I18n.t(
'discourse_narrative_bot.advanced_user_narrative.start_message',
username: user.username
)
expected_raw = <<~RAW
#{expected_raw}
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.edit.instructions')}
RAW
new_post = Post.offset(1).last
expect(narrative.get_data(user)).to eq({
"topic_id" => new_post.topic.id,
"state" => "tutorial_edit",
"last_post_id" => new_post.id,
"track" => described_class.to_s,
"tutorial_edit" => {
"post_id" => Post.last.id
}
})
expect(new_post.raw).to eq(expected_raw.chomp)
expect(new_post.topic.id).to_not eq(topic.id)
end
end
end
describe "#input" do
context 'edit tutorial' do
before do
narrative.set_data(user,
state: :tutorial_edit,
topic_id: topic.id,
track: described_class.to_s,
tutorial_edit: {
post_id: first_post.id
}
)
end
describe 'when post is not in the right topic' do
it 'should not do anything' do
other_post
narrative.expects(:enqueue_timeout_job).with(user).never
expect { narrative.input(:reply, user, post: other_post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_edit)
end
end
describe 'when user replies to the post' do
it 'should create the right reply' do
post
narrative.expects(:enqueue_timeout_job).with(user).once
expect { narrative.input(:reply, user, post: post) }
.to change { Post.count }.by(1)
expect(Post.last.raw).to eq(I18n.t(
'discourse_narrative_bot.advanced_user_narrative.edit.not_found',
url: first_post.url
))
end
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
post.update!(raw: "@#{discobot_user.username} #{skip_trigger}")
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.advanced_user_narrative.delete.instructions')
)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_delete)
end
end
end
describe 'when user edits the right post' do
let(:post_2) { Fabricate(:post, user: post.user, topic: post.topic) }
it 'should create the right reply' do
post_2
expect do
PostRevisor.new(post_2).revise!(post_2.user, raw: 'something new')
end.to change { Post.count }.by(1)
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.edit.reply')}
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.delete.instructions')}
RAW
expect(Post.last.raw).to eq(expected_raw.chomp)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_delete)
end
end
end
context 'delete tutorial' do
before do
narrative.set_data(user,
state: :tutorial_delete,
topic_id: topic.id,
track: described_class.to_s
)
end
describe 'when user replies to the topic' do
it 'should create the right reply' do
narrative.expects(:enqueue_timeout_job).with(user).once
narrative.input(:reply, user, post: post)
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.advanced_user_narrative.delete.not_found'
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_delete)
end
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
post.update!(raw: skip_trigger)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
new_post = Post.offset(1).last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.advanced_user_narrative.recover.instructions')
)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_recover)
end
end
end
describe 'when user destroys a post in a different topic' do
it 'should not do anything' do
other_post
narrative.expects(:enqueue_timeout_job).with(user).never
PostDestroyer.new(user, other_post).destroy
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_delete)
end
end
describe 'when user deletes a post in the right topic' do
it 'should create the right reply' do
post
expect { PostDestroyer.new(user, post).destroy }
.to change { Post.count }.by(2)
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.delete.reply')}
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.recover.instructions')}
RAW
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_recover)
expect(Post.offset(1).last.raw).to eq(expected_raw.chomp)
end
context 'when user is an admin' do
it 'should create the right reply' do
post
user.update!(admin: true)
expect { PostDestroyer.new(user, post).destroy }
.to_not change { Post.count }
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.delete.reply')}
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.recover.instructions')}
RAW
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_recover)
expect(Post.last.raw).to eq(expected_raw.chomp)
end
end
end
end
context 'undelete post tutorial' do
before do
narrative.set_data(user,
state: :tutorial_recover,
topic_id: topic.id,
track: described_class.to_s
)
end
describe 'when user replies to the topic' do
it 'should create the right reply' do
narrative.set_data(user, narrative.get_data(user).merge(
tutorial_recover: { post_id: '1' }
))
narrative.expects(:enqueue_timeout_job).with(user).once
narrative.input(:reply, user, post: post)
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.advanced_user_narrative.recover.not_found'
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_recover)
end
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
parent_category = Fabricate(:category, name: 'a')
category = Fabricate(:category, parent_category: parent_category, name: 'b')
post.update!(raw: skip_trigger)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.advanced_user_narrative.category_hashtag.instructions',
category: "#a:b"
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_category_hashtag)
end
end
end
describe 'when user recovers a post in a different topic' do
it 'should not do anything' do
other_post
narrative.expects(:enqueue_timeout_job).with(user).never
PostDestroyer.new(user, other_post).destroy
PostDestroyer.new(user, other_post).recover
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_recover)
end
end
describe 'when user recovers a post in the right topic' do
it 'should create the right reply' do
parent_category = Fabricate(:category, name: 'a')
category = Fabricate(:category, parent_category: parent_category, name: 'b')
post
PostDestroyer.new(user, post).destroy
expect { PostDestroyer.new(user, post).recover }
.to change { Post.count }.by(1)
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.recover.reply')}
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.category_hashtag.instructions', category: "#a:b")}
RAW
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_category_hashtag)
expect(Post.last.raw).to eq(expected_raw.chomp)
end
end
end
context 'category hashtag tutorial' do
before do
narrative.set_data(user,
state: :tutorial_category_hashtag,
topic_id: topic.id,
track: described_class.to_s
)
end
describe 'when post is not in the right topic' do
it 'should not do anything' do
other_post
narrative.expects(:enqueue_timeout_job).with(user).never
expect { narrative.input(:reply, user, post: other_post) }
.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym)
.to eq(:tutorial_category_hashtag)
end
end
describe 'when user replies to the topic' do
it 'should create the right reply' do
narrative.input(:reply, user, post: post)
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.advanced_user_narrative.category_hashtag.not_found'
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_category_hashtag)
end
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
post.update!(raw: skip_trigger)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.advanced_user_narrative.change_topic_notification_level.instructions'
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_change_topic_notification_level)
end
end
end
it 'should create the right reply' do
category = Fabricate(:category)
post.update!(raw: "Check out this ##{category.slug}")
narrative.input(:reply, user, post: post)
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.category_hashtag.reply')}
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.change_topic_notification_level.instructions')}
RAW
expect(Post.last.raw).to eq(expected_raw.chomp)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_change_topic_notification_level)
end
end
context 'topic notification level tutorial' do
before do
narrative.set_data(user,
state: :tutorial_change_topic_notification_level,
topic_id: topic.id,
track: described_class.to_s
)
end
describe 'when notification level is changed for another topic' do
it 'should not do anything' do
other_topic
user
narrative.expects(:enqueue_timeout_job).with(user).never
expect do
TopicUser.change(
user.id,
other_topic.id,
notification_level: TopicUser.notification_levels[:tracking]
)
end.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_change_topic_notification_level)
end
end
describe 'when user replies to the topic' do
it 'should create the right reply' do
narrative.input(:reply, user, post: post)
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.advanced_user_narrative.change_topic_notification_level.not_found'
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_change_topic_notification_level)
end
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
post.update!(raw: skip_trigger)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.advanced_user_narrative.poll.instructions')
)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_poll)
end
end
end
describe 'when user changed the topic notification level' do
it 'should create the right reply' do
TopicUser.change(
user.id,
topic.id,
notification_level: TopicUser.notification_levels[:tracking]
)
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.change_topic_notification_level.reply')}
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.poll.instructions')}
RAW
expect(Post.last.raw).to eq(expected_raw.chomp)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_poll)
end
end
end
context 'poll tutorial' do
before do
narrative.set_data(user,
state: :tutorial_poll,
topic_id: topic.id,
track: described_class.to_s
)
end
describe 'when post is not in the right topic' do
it 'should not do anything' do
other_post
narrative.expects(:enqueue_timeout_job).with(user).never
expect { narrative.input(:reply, user, post: other_post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_poll)
end
end
describe 'when user replies to the topic' do
it 'should create the right reply' do
narrative.input(:reply, user, post: post)
new_post = Post.last
expect(new_post.raw).to eq(I18n.t('discourse_narrative_bot.advanced_user_narrative.poll.not_found'))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_poll)
end
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
post.update!(raw: skip_trigger)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.advanced_user_narrative.details.instructions')
)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_details)
end
end
end
it 'should create the right reply' do
post.update!(raw: "[poll]\n* 1\n* 2\n[/poll]\n")
narrative.input(:reply, user, post: post)
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.poll.reply')}
#{I18n.t('discourse_narrative_bot.advanced_user_narrative.details.instructions')}
RAW
expect(Post.last.raw).to eq(expected_raw.chomp)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_details)
end
end
context "details tutorial" do
before do
narrative.set_data(user,
state: :tutorial_details,
topic_id: topic.id,
track: described_class.to_s
)
end
describe 'when post is not in the right topic' do
it 'should not do anything' do
other_post
narrative.expects(:enqueue_timeout_job).with(user).never
expect { narrative.input(:reply, user, post: other_post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_details)
end
end
describe 'when user replies to the topic' do
it 'should create the right reply' do
narrative.input(:reply, user, post: post)
expect(Post.last.raw).to eq(I18n.t('discourse_narrative_bot.advanced_user_narrative.details.not_found'))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_details)
end
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
post.update!(raw: skip_trigger)
expect do
DiscourseNarrativeBot::TrackSelector.new(
:reply, user, post_id: post.id
).select
end.to change { Post.count }.by(1)
expect(narrative.get_data(user)[:state].to_sym).to eq(:end)
end
end
end
it 'should create the right reply' do
post.update!(raw: "[details=\"This is a test\"]wooohoo[/details]")
narrative.input(:reply, user, post: post)
expect(Post.offset(1).last.raw).to eq(I18n.t(
'discourse_narrative_bot.advanced_user_narrative.details.reply'
))
expect(narrative.get_data(user)).to eq({
"state" => "end",
"topic_id" => topic.id,
"track" => described_class.to_s
})
expect(user.badges.where(name: DiscourseNarrativeBot::AdvancedUserNarrative::BADGE_NAME).exists?)
.to eq(true)
end
end
end
end

View File

@ -0,0 +1,895 @@
require 'rails_helper'
describe DiscourseNarrativeBot::NewUserNarrative do
let!(:welcome_topic) { Fabricate(:topic, title: 'Welcome to Discourse') }
let(:discobot_user) { User.find(-2) }
let(:first_post) { Fabricate(:post, user: discobot_user) }
let(:user) { Fabricate(:user) }
let(:topic) do
Fabricate(:private_message_topic, first_post: first_post,
topic_allowed_users: [
Fabricate.build(:topic_allowed_user, user: discobot_user),
Fabricate.build(:topic_allowed_user, user: user),
]
)
end
let(:post) { Fabricate(:post, topic: topic, user: user) }
let(:narrative) { described_class.new }
let(:other_topic) { Fabricate(:topic) }
let(:other_post) { Fabricate(:post, topic: other_topic) }
let(:profile_page_url) { "#{Discourse.base_url}/users/#{user.username}" }
let(:skip_trigger) { DiscourseNarrativeBot::TrackSelector.skip_trigger }
let(:reset_trigger) { DiscourseNarrativeBot::TrackSelector.reset_trigger }
before do
SiteSetting.discourse_narrative_bot_enabled = true
end
describe '#notify_timeout' do
before do
narrative.set_data(user,
state: :tutorial_images,
topic_id: topic.id,
last_post_id: post.id
)
end
it 'should create the right message' do
expect { narrative.notify_timeout(user) }.to change { Post.count }.by(1)
expect(Post.last.raw).to eq(I18n.t(
'discourse_narrative_bot.timeout.message',
username: user.username,
skip_trigger: skip_trigger,
reset_trigger: "#{reset_trigger} #{described_class.reset_trigger}",
))
end
end
describe '#reset_bot' do
before do
narrative.set_data(user, state: :tutorial_images, topic_id: topic.id)
end
context 'when trigger is initiated in a PM' do
let(:user) { Fabricate(:user) }
let(:topic) do
topic_allowed_user = Fabricate.build(:topic_allowed_user, user: user)
bot = Fabricate.build(:topic_allowed_user, user: discobot_user)
Fabricate(:private_message_topic, topic_allowed_users: [topic_allowed_user, bot])
end
let(:post) { Fabricate(:post, topic: topic) }
it 'should reset the bot' do
narrative.reset_bot(user, post)
expected_raw = I18n.t('discourse_narrative_bot.new_user_narrative.hello.message',
username: user.username, title: SiteSetting.title
)
expected_raw = <<~RAW
#{expected_raw}
#{I18n.t('discourse_narrative_bot.new_user_narrative.bookmark.instructions', profile_page_url: profile_page_url)}
RAW
new_post = Post.last
expect(narrative.get_data(user)).to eq({
"topic_id" => topic.id,
"state" => "tutorial_bookmark",
"last_post_id" => new_post.id,
"track" => described_class.to_s
})
expect(new_post.raw).to eq(expected_raw.chomp)
expect(new_post.topic.id).to eq(topic.id)
end
end
context 'when trigger is not initiated in a PM' do
it 'should start the new track in a PM' do
narrative.reset_bot(user, other_post)
expected_raw = I18n.t('discourse_narrative_bot.new_user_narrative.hello.message',
username: user.username, title: SiteSetting.title
)
expected_raw = <<~RAW
#{expected_raw}
#{I18n.t('discourse_narrative_bot.new_user_narrative.bookmark.instructions', profile_page_url: profile_page_url)}
RAW
new_post = Post.last
expect(narrative.get_data(user)).to eq({
"topic_id" => new_post.topic.id,
"state" => "tutorial_bookmark",
"last_post_id" => new_post.id,
"track" => described_class.to_s
})
expect(new_post.raw).to eq(expected_raw.chomp)
expect(new_post.topic.id).to_not eq(topic.id)
end
end
end
describe '#input' do
before do
SiteSetting.title = "This is an awesome site!"
narrative.set_data(user, state: :begin)
end
describe 'when an error occurs' do
before do
narrative.set_data(user, state: :tutorial_flag, topic_id: topic.id)
end
it 'should revert to the previous state' do
narrative.expects(:send).with('init_tutorial_search').raises(StandardError.new('some error'))
narrative.expects(:send).with(:reply_to_flag).returns(post)
expect { narrative.input(:flag, user, post: post) }.to raise_error(StandardError, 'some error')
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_flag)
end
end
describe 'when input does not have a valid transition from current state' do
before do
narrative.set_data(user, state: :begin)
end
it 'should raise the right error' do
expect(narrative.input(:something, user, post: post)).to eq(nil)
expect(narrative.get_data(user)[:state].to_sym).to eq(:begin)
end
end
describe 'when [:begin, :init]' do
it 'should create the right post' do
narrative.expects(:enqueue_timeout_job).never
narrative.input(:init, user, post: nil)
new_post = Post.last
expected_raw = I18n.t('discourse_narrative_bot.new_user_narrative.hello.message',
username: user.username, title: SiteSetting.title
)
expected_raw = <<~RAW
#{expected_raw}
#{I18n.t('discourse_narrative_bot.new_user_narrative.bookmark.instructions', profile_page_url: profile_page_url)}
RAW
expect(new_post.raw).to eq(expected_raw.chomp)
expect(narrative.get_data(user)[:state].to_sym)
.to eq(:tutorial_bookmark)
end
end
describe "bookmark tutorial" do
before do
narrative.set_data(user, state: :tutorial_bookmark, topic_id: topic.id)
end
describe 'when post is not in the right topic' do
it 'should not do anything' do
other_post.update!(user_id: -2)
narrative.expects(:enqueue_timeout_job).with(user).never
expect { narrative.input(:bookmark, user, post: other_post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_bookmark)
end
end
describe "when bookmark is not on bot's post" do
it 'should not do anything' do
narrative.expects(:enqueue_timeout_job).with(user).never
post
expect { narrative.input(:bookmark, user, post: post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_bookmark)
end
end
describe 'when user replies to the topic' do
it 'should create the right reply' do
narrative.expects(:enqueue_timeout_job).with(user).once
narrative.input(:reply, user, post: post)
new_post = Post.last
expect(new_post.raw).to eq(I18n.t('discourse_narrative_bot.new_user_narrative.bookmark.not_found'))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_bookmark)
end
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
post.update!(raw: "@#{discobot_user.username} #{skip_trigger}")
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.onebox.instructions')
)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_onebox)
end
end
end
it 'should create the right reply' do
post.update!(user: described_class.discobot_user)
narrative.expects(:enqueue_timeout_job).with(user)
narrative.input(:bookmark, user, post: post)
new_post = Post.last
profile_page_url = "#{Discourse.base_url}/u/#{user.username}"
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.new_user_narrative.bookmark.reply', profile_page_url: profile_page_url)}
#{I18n.t('discourse_narrative_bot.new_user_narrative.onebox.instructions')}
RAW
expect(new_post.raw).to eq(expected_raw.chomp)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_onebox)
end
end
describe 'onebox tutorial' do
before do
narrative.set_data(user, state: :tutorial_onebox, topic_id: topic.id)
end
describe 'when post is not in the right topic' do
it 'should not do anything' do
other_post
narrative.expects(:enqueue_timeout_job).with(user).never
expect { narrative.input(:reply, user, post: other_post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_onebox)
end
end
describe 'when post does not contain onebox' do
it 'should create the right reply' do
narrative.expects(:enqueue_timeout_job).with(user)
narrative.input(:reply, user, post: post)
new_post = Post.last
expect(new_post.raw).to eq(I18n.t('discourse_narrative_bot.new_user_narrative.onebox.not_found'))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_onebox)
end
end
describe "when user has not liked bot's post" do
it 'should create the right reply' do
narrative.expects(:enqueue_timeout_job).with(user)
narrative.input(:reply, user, post: post)
new_post = Post.last
expect(new_post.raw).to eq(I18n.t('discourse_narrative_bot.new_user_narrative.onebox.not_found'))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_onebox)
end
end
describe 'when user replies to the topic' do
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
post.update!(raw: skip_trigger)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.emoji.instructions')
)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_emoji)
end
end
end
it 'should create the right reply' do
post.update!(raw: 'https://en.wikipedia.org/wiki/ROT13')
narrative.expects(:enqueue_timeout_job).with(user)
narrative.input(:reply, user, post: post)
new_post = Post.last
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.new_user_narrative.onebox.reply')}
#{I18n.t('discourse_narrative_bot.new_user_narrative.emoji.instructions')}
RAW
expect(new_post.raw).to eq(expected_raw.chomp)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_emoji)
end
end
describe 'images tutorial' do
let(:post_2) { Fabricate(:post, topic: topic) }
before do
narrative.set_data(user,
state: :tutorial_images,
topic_id: topic.id,
last_post_id: post_2.id,
track: described_class.to_s
)
end
describe 'when post is not in the right topic' do
it 'should not do anything' do
other_post
narrative.expects(:enqueue_timeout_job).with(user).never
expect { narrative.input(:reply, user, post: other_post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_images)
end
end
describe 'when user replies to the topic' do
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
post.update!(raw: skip_trigger)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.flag.instructions',
guidelines_url: Discourse.base_url + '/guidelines',
about_url: Discourse.base_url + '/about'
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_flag)
end
end
end
context 'when image is not found' do
it 'should create the right replies' do
PostAction.act(user, post_2, PostActionType.types[:like])
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
expect(Post.last.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.images.not_found',
image_url: "#{Discourse.base_url}/images/dog-walk.gif"
))
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
new_post = Fabricate(:post,
user: user,
topic: topic,
raw: "<img src='https://i.ytimg.com/vi/tntOCGkgt98/maxresdefault.jpg'>"
)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: new_post.id).select
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.new_user_narrative.images.reply')}
#{I18n.t(
'discourse_narrative_bot.new_user_narrative.flag.instructions',
guidelines_url: "#{Discourse.base_url}/guidelines",
about_url: "#{Discourse.base_url}/about"
)}
RAW
expect(Post.last.raw).to eq(expected_raw.chomp)
post_action = PostAction.last
expect(post_action.post_action_type_id).to eq(PostActionType.types[:like])
expect(post_action.user).to eq(described_class.discobot_user)
expect(post_action.post).to eq(new_post)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_flag)
end
end
it 'should create the right replies' do
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
expect(Post.last.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.images.not_found',
image_url: "#{Discourse.base_url}/images/dog-walk.gif"
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_images)
new_post = Fabricate(:post,
user: user,
topic: topic,
raw: "<img src='https://i.ytimg.com/vi/tntOCGkgt98/maxresdefault.jpg'>"
)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: new_post.id).select
expect(Post.last.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.images.like_not_found',
url: post_2.url
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_images)
expect(narrative.get_data(user)[:tutorial_images][:post_id])
.to eq(new_post.id)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
PostAction.act(user, post_2, PostActionType.types[:like])
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.new_user_narrative.images.reply')}
#{I18n.t(
'discourse_narrative_bot.new_user_narrative.flag.instructions',
guidelines_url: "#{Discourse.base_url}/guidelines",
about_url: "#{Discourse.base_url}/about"
)}
RAW
expect(Post.last.raw).to eq(expected_raw.chomp)
post_action = PostAction.last
expect(post_action.post_action_type_id).to eq(PostActionType.types[:like])
expect(post_action.user).to eq(described_class.discobot_user)
expect(post_action.post).to eq(new_post)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_flag)
end
end
describe 'fomatting tutorial' do
before do
narrative.set_data(user, state: :tutorial_formatting, topic_id: topic.id)
end
describe 'when post is not in the right topic' do
it 'should not do anything' do
other_post
narrative.expects(:enqueue_timeout_job).with(user).never
expect { narrative.input(:reply, user, post: other_post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_formatting)
end
end
describe 'when post does not contain any formatting' do
it 'should create the right reply' do
narrative.expects(:enqueue_timeout_job).with(user)
narrative.input(:reply, user, post: post)
expect(Post.last.raw).to eq(I18n.t('discourse_narrative_bot.new_user_narrative.formatting.not_found'))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_formatting)
end
end
describe 'when user replies to the topic' do
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
post.update!(raw: skip_trigger)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.quoting.instructions',
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_quote)
end
end
end
["**bold**", "__italic__", "[b]bold[/b]", "[i]italic[/i]"].each do |raw|
it 'should create the right reply' do
post.update!(raw: raw)
narrative.expects(:enqueue_timeout_job).with(user)
narrative.input(:reply, user, post: post)
new_post = Post.last
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.new_user_narrative.formatting.reply')}
#{I18n.t('discourse_narrative_bot.new_user_narrative.quoting.instructions')}
RAW
expect(new_post.raw).to eq(expected_raw.chomp)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_quote)
end
end
end
describe 'quote tutorial' do
before do
narrative.set_data(user, state: :tutorial_quote, topic_id: topic.id)
end
describe 'when post is not in the right topic' do
it 'should not do anything' do
other_post
narrative.expects(:enqueue_timeout_job).with(user).never
expect { narrative.input(:reply, user, post: other_post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_quote)
end
end
describe 'when post does not contain any quotes' do
it 'should create the right reply' do
narrative.expects(:enqueue_timeout_job).with(user)
narrative.input(:reply, user, post: post)
expect(Post.last.raw).to eq(I18n.t('discourse_narrative_bot.new_user_narrative.quoting.not_found'))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_quote)
end
end
describe 'when user replies to the topic' do
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
post.update!(raw: skip_trigger)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.images.instructions',
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_images)
end
end
end
it 'should create the right reply' do
post.update!(
raw: '[quote="#{post.user}, post:#{post.post_number}, topic:#{topic.id}"]\n:monkey: :fries:\n[/quote]'
)
narrative.expects(:enqueue_timeout_job).with(user)
narrative.input(:reply, user, post: post)
new_post = Post.last
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.new_user_narrative.quoting.reply')}
#{I18n.t('discourse_narrative_bot.new_user_narrative.images.instructions')}
RAW
expect(new_post.raw).to eq(expected_raw.chomp)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_images)
end
end
describe 'emoji tutorial' do
before do
narrative.set_data(user, state: :tutorial_emoji, topic_id: topic.id)
end
describe 'when post is not in the right topic' do
it 'should not do anything' do
other_post
narrative.expects(:enqueue_timeout_job).with(user).never
expect { narrative.input(:reply, user, post: other_post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_emoji)
end
end
describe 'when post does not contain any emoji' do
it 'should create the right reply' do
narrative.expects(:enqueue_timeout_job).with(user)
narrative.input(:reply, user, post: post)
expect(Post.last.raw).to eq(I18n.t('discourse_narrative_bot.new_user_narrative.emoji.not_found'))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_emoji)
end
end
describe 'when user replies to the topic' do
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
post.update!(raw: skip_trigger)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.mention.instructions',
discobot_username: discobot_user.username
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_mention)
end
end
end
it 'should create the right reply' do
post.update!(
raw: ':monkey: :fries:'
)
narrative.expects(:enqueue_timeout_job).with(user)
narrative.input(:reply, user, post: post)
new_post = Post.last
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.new_user_narrative.emoji.reply')}
#{I18n.t('discourse_narrative_bot.new_user_narrative.mention.instructions',
discobot_username: discobot_user.username
)}
RAW
expect(new_post.raw).to eq(expected_raw.chomp)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_mention)
end
end
describe 'mention tutorial' do
before do
narrative.set_data(user, state: :tutorial_mention, topic_id: topic.id)
end
describe 'when post is not in the right topic' do
it 'should not do anything' do
other_post
narrative.expects(:enqueue_timeout_job).with(user).never
expect { narrative.input(:reply, user, post: other_post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_mention)
end
end
describe 'when post does not contain any mentions' do
it 'should create the right reply' do
narrative.expects(:enqueue_timeout_job).with(user)
narrative.input(:reply, user, post: post)
expect(Post.last.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.mention.not_found',
username: user.username,
discobot_username: discobot_user.username
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_mention)
end
end
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
post.update!(raw: skip_trigger)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.formatting.instructions',
discobot_username: discobot_user.username
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_formatting)
end
end
it 'should create the right reply' do
post.update!(
raw: '@discobot hello how are you doing today?'
)
narrative.expects(:enqueue_timeout_job).with(user)
narrative.input(:reply, user, post: post)
new_post = Post.last
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.new_user_narrative.mention.reply')}
#{I18n.t(
'discourse_narrative_bot.new_user_narrative.formatting.instructions'
)}
RAW
expect(new_post.raw).to eq(expected_raw.chomp)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_formatting)
end
end
describe 'flag tutorial' do
let(:post) { Fabricate(:post, user: described_class.discobot_user, topic: topic) }
let(:flag) { Fabricate(:flag, post: post, user: user) }
let(:other_post) { Fabricate(:post, user: user, topic: topic) }
before do
flag
narrative.set_data(user, state: :tutorial_flag, topic_id: topic.id)
end
describe 'when post flagged is not for the right topic' do
it 'should not do anything' do
narrative.expects(:enqueue_timeout_job).with(user).never
flag.update!(post: other_post)
expect { narrative.input(:flag, user, post: flag.post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_flag)
end
end
describe 'when post being flagged does not belong to discobot ' do
it 'should not do anything' do
narrative.expects(:enqueue_timeout_job).with(user).never
flag.update!(post: other_post)
expect { narrative.input(:flag, user, post: flag.post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_flag)
end
end
describe 'when user replies to the topic' do
it 'should create the right reply' do
narrative.input(:reply, user, post: other_post)
new_post = Post.last
expect(new_post.raw).to eq(I18n.t('discourse_narrative_bot.new_user_narrative.flag.not_found'))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_flag)
end
describe 'when reply contains the skip trigger' do
it 'should create the right reply' do
other_post.update!(raw: skip_trigger)
described_class.any_instance.expects(:enqueue_timeout_job).with(user)
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: other_post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.search.instructions'
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_search)
end
end
end
it 'should create the right reply' do
narrative.expects(:enqueue_timeout_job).with(user)
expect { narrative.input(:flag, user, post: flag.post) }.to change { PostAction.count }.by(-1)
new_post = Post.last
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.new_user_narrative.flag.reply')}
#{I18n.t(
'discourse_narrative_bot.new_user_narrative.search.instructions'
)}
RAW
expect(new_post.raw).to eq(expected_raw.chomp)
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_search)
end
end
describe 'search tutorial' do
before do
narrative.set_data(user, state: :tutorial_search, topic_id: topic.id)
end
describe 'when post is not in the right topic' do
it 'should not do anything' do
other_post
narrative.expects(:enqueue_timeout_job).with(user).never
expect { narrative.input(:reply, user, post: other_post) }.to_not change { Post.count }
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_search)
end
end
describe 'when post does not contain the right answer' do
it 'should create the right reply' do
narrative.expects(:enqueue_timeout_job).with(user)
narrative.input(:reply, user, post: post)
expect(Post.last.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.search.not_found'
))
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_search)
end
end
describe 'when post contain the right answer' do
let(:post) { Fabricate(:post, user: described_class.discobot_user, topic: topic) }
let(:flag) { Fabricate(:flag, post: post, user: user) }
before do
narrative.set_data(user,
state: :tutorial_flag,
topic_id: topic.id
)
DiscourseNarrativeBot::TrackSelector.new(:flag, user, post_id: flag.post_id).select
expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_search)
expect(post.reload.topic.first_post.raw).to include(I18n.t(
"discourse_narrative_bot.new_user_narrative.search.hidden_message"
))
end
it 'should clean up if the tutorial is skipped' do
post.update!(raw: skip_trigger)
expect do
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
end.to change { Post.count }.by(1)
expect(first_post.reload.raw).to eq('Hello world')
expect(narrative.get_data(user)[:state].to_sym).to eq(:end)
end
it 'should create the right reply' do
post.update!(
raw: "#{described_class::SEARCH_ANSWER} this is a capybara"
)
expect do
DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select
end.to change { Post.count }.by(2)
new_post = Post.offset(1).last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.new_user_narrative.search.reply',
search_url: "#{Discourse.base_url}/search"
).chomp)
expect(first_post.reload.raw).to eq('Hello world')
expect(narrative.get_data(user)).to include({
"state" => "end",
"topic_id" => new_post.topic_id,
"track" => described_class.to_s,
})
expect(user.badges.where(name: DiscourseNarrativeBot::NewUserNarrative::BADGE_NAME).exists?)
.to eq(true)
end
end
end
end
end

View File

@ -0,0 +1,28 @@
require 'rails_helper'
describe DiscourseNarrativeBot::Store do
describe '.set' do
it 'should set the right value in the plugin store' do
key = 'somekey'
described_class.set(key, 'yay')
plugin_store_row = PluginStoreRow.last
expect(plugin_store_row.value).to eq('yay')
expect(plugin_store_row.plugin_name).to eq(DiscourseNarrativeBot::PLUGIN_NAME)
expect(plugin_store_row.key).to eq(key)
end
end
describe '.get' do
it 'should get the right value from the plugin store' do
PluginStoreRow.create!(
plugin_name: DiscourseNarrativeBot::PLUGIN_NAME,
key: 'somekey',
value: 'yay',
type_name: 'string'
)
expect(described_class.get('somekey')).to eq('yay')
end
end
end

View File

@ -0,0 +1,704 @@
require 'rails_helper'
describe DiscourseNarrativeBot::TrackSelector do
let(:user) { Fabricate(:user) }
let(:discobot_user) { described_class.discobot_user }
let(:narrative) { DiscourseNarrativeBot::NewUserNarrative.new }
let(:random_mention_reply) do
I18n.t('discourse_narrative_bot.track_selector.random_mention.reply',
discobot_username: discobot_user.username,
help_trigger: described_class.help_trigger
)
end
let(:help_message) do
discobot_username = discobot_user.username
end_message = <<~RAW
#{I18n.t(
'discourse_narrative_bot.track_selector.random_mention.tracks',
discobot_username: discobot_username,
default_track: DiscourseNarrativeBot::NewUserNarrative.reset_trigger,
reset_trigger: described_class.reset_trigger,
tracks: DiscourseNarrativeBot::NewUserNarrative.reset_trigger
)}
#{I18n.t(
'discourse_narrative_bot.track_selector.random_mention.bot_actions',
discobot_username: discobot_username,
dice_trigger: described_class.dice_trigger,
quote_trigger: described_class.quote_trigger,
magic_8_ball_trigger: described_class.magic_8_ball_trigger
)}
RAW
end_message.chomp
end
describe '#select' do
context 'in a PM with discobot' do
let(:first_post) { Fabricate(:post, user: discobot_user) }
let(:topic) do
Fabricate(:private_message_topic, first_post: first_post,
topic_allowed_users: [
Fabricate.build(:topic_allowed_user, user: discobot_user),
Fabricate.build(:topic_allowed_user, user: user),
]
)
end
let(:post) { Fabricate(:post, topic: topic, user: user) }
context 'during a tutorial track' do
before do
narrative.set_data(user,
state: :tutorial_formatting,
topic_id: topic.id,
track: "DiscourseNarrativeBot::NewUserNarrative"
)
end
context 'when bot is mentioned' do
it 'should select the right track' do
post.update!(raw: '@discobot show me what you can do')
described_class.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
"discourse_narrative_bot.new_user_narrative.formatting.not_found"
))
end
end
context 'when bot is replied to' do
it 'should select the right track' do
post.update!(
raw: 'show me what you can do',
reply_to_post_number: first_post.post_number
)
described_class.new(:reply, user, post_id: post.id).select
expect(Post.last.raw).to eq(I18n.t(
"discourse_narrative_bot.new_user_narrative.formatting.not_found"
))
described_class.new(:reply, user, post_id: post.id).select
expected_raw = <<~RAW
#{I18n.t(
'discourse_narrative_bot.track_selector.do_not_understand.first_response',
reset_trigger: "#{described_class.reset_trigger} #{DiscourseNarrativeBot::NewUserNarrative.reset_trigger}",
)}
#{I18n.t(
'discourse_narrative_bot.track_selector.do_not_understand.track_response',
reset_trigger: "#{described_class.reset_trigger} #{DiscourseNarrativeBot::NewUserNarrative.reset_trigger}",
skip_trigger: described_class.skip_trigger
)}
RAW
expect(Post.last.raw).to eq(expected_raw.chomp)
end
end
describe 'when user thank the bot' do
it 'should like the post' do
post.update!(raw: 'thanks!')
expect { described_class.new(:reply, user, post_id: post.id).select }
.to change { PostAction.count }.by(1)
post_action = PostAction.last
expect(post_action.post).to eq(post)
expect(post_action.post_action_type_id).to eq(PostActionType.types[:like])
post = Post.last
expect(Post.last).to eq(post)
expect(DiscourseNarrativeBot::NewUserNarrative.new.get_data(user)['state'])
.to eq(nil)
end
end
context 'when reply contains a reset trigger' do
it 'should reset the track' do
post.update!(
raw: "#{described_class.reset_trigger} #{DiscourseNarrativeBot::NewUserNarrative.reset_trigger}"
)
described_class.new(:reply, user, post_id: post.id).select
expect(DiscourseNarrativeBot::NewUserNarrative.new.get_data(user)['state'])
.to eq("tutorial_bookmark")
end
describe 'reset trigger in surrounded by quotes' do
it 'should reset the track' do
post.update!(
raw: "'#{described_class.reset_trigger} #{DiscourseNarrativeBot::NewUserNarrative.reset_trigger}'"
)
described_class.new(:reply, user, post_id: post.id).select
expect(DiscourseNarrativeBot::NewUserNarrative.new.get_data(user)['state'])
.to eq("tutorial_bookmark")
end
end
describe 'reset trigger in a middle of a sentence' do
describe 'when post is less than reset trigger exact match limit' do
it 'should reset the track' do
post.update!(
raw: "I would like to #{described_class.reset_trigger} #{DiscourseNarrativeBot::NewUserNarrative.reset_trigger} now"
)
described_class.new(:reply, user, post_id: post.id).select
expect(DiscourseNarrativeBot::NewUserNarrative.new.get_data(user)['state'])
.to eq("tutorial_bookmark")
end
end
describe 'when post exceeds reset trigger exact match limit' do
it 'should not reset the track' do
post.update!(
raw: "I would like to #{described_class.reset_trigger} #{DiscourseNarrativeBot::NewUserNarrative.reset_trigger} now #{'a' * described_class::RESET_TRIGGER_EXACT_MATCH_LENGTH}"
)
expect { described_class.new(:reply, user, post_id: post.id).select }
.to change { Post.count }.by(1)
expect(DiscourseNarrativeBot::NewUserNarrative.new.get_data(user)['state'])
.to eq("tutorial_formatting")
end
end
end
context 'start/reset advanced track' do
before do
post.update!(
raw: "@#{discobot_user.username} #{described_class.reset_trigger} #{DiscourseNarrativeBot::AdvancedUserNarrative.reset_trigger}"
)
end
context 'when new user track has not been completed' do
it 'should not start the track' do
described_class.new(:reply, user, post_id: post.id).select
expect(DiscourseNarrativeBot::Store.get(user.id)['track'])
.to eq(DiscourseNarrativeBot::NewUserNarrative.to_s)
end
end
context 'when new user track has been completed' do
it 'should start the track' do
BadgeGranter.grant(
Badge.find_by(name: DiscourseNarrativeBot::NewUserNarrative::BADGE_NAME),
user
)
described_class.new(:reply, user, post_id: post.id).select
expect(DiscourseNarrativeBot::Store.get(user.id)['track'])
.to eq(DiscourseNarrativeBot::AdvancedUserNarrative.to_s)
end
end
end
end
end
context 'at the end of a tutorial track' do
before do
narrative.set_data(user,
state: :end,
topic_id: topic.id,
track: "DiscourseNarrativeBot::NewUserNarrative"
)
end
context 'generic replies' do
after do
$redis.del("#{described_class::GENERIC_REPLIES_COUNT_PREFIX}#{user.id}")
end
it 'should create the right generic do not understand responses' do
described_class.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.track_selector.do_not_understand.first_response',
reset_trigger: "#{described_class.reset_trigger} #{DiscourseNarrativeBot::NewUserNarrative.reset_trigger}",
))
described_class.new(:reply, user, post_id: Fabricate(:post,
topic: new_post.topic,
user: user,
reply_to_post_number: new_post.post_number
).id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
'discourse_narrative_bot.track_selector.do_not_understand.second_response',
reset_trigger: "#{described_class.reset_trigger} #{DiscourseNarrativeBot::NewUserNarrative.reset_trigger}",
))
new_post = Fabricate(:post,
topic: new_post.topic,
user: user,
reply_to_post_number: new_post.post_number
)
expect { described_class.new(:reply, user, post_id: new_post.id).select }
.to_not change { Post.count }
end
end
context 'when discobot is mentioned at the end of a track' do
it 'should create the right reply' do
post.update!(raw: 'Show me what you can do @discobot')
described_class.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(random_mention_reply)
end
describe 'when asking discobot for help' do
it 'should create the right reply' do
post.update!(raw: 'show me what you can do @discobot display help')
described_class.new(:reply, user, post_id: post.id).select
expect(Post.last.raw).to include(help_message)
end
describe 'as an admin or moderator' do
it 'should include the commands to start the advanced user track' do
user.update!(moderator: true)
post.update!(raw: 'Show me what you can do @discobot display help')
described_class.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to include(
DiscourseNarrativeBot::AdvancedUserNarrative.reset_trigger
)
end
end
describe 'as a user that has completed the new user track' do
it 'should include the commands to start the advanced user track' do
narrative.set_data(user,
state: :end,
topic_id: post.topic.id,
track: "DiscourseNarrativeBot::NewUserNarrative",
)
BadgeGranter.grant(
Badge.find_by(name: DiscourseNarrativeBot::NewUserNarrative::BADGE_NAME),
user
)
post.update!(raw: 'Show me what you can do @discobot display help')
described_class.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to include(
DiscourseNarrativeBot::AdvancedUserNarrative.reset_trigger
)
end
end
end
describe 'when discobot is asked to roll dice' do
before do
narrative.set_data(user,
state: :end,
topic_id: topic.id
)
end
it 'should create the right reply' do
post.update!(raw: 'roll 2d1')
described_class.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(I18n.t(
"discourse_narrative_bot.dice.results", results: '1, 1'
))
end
describe 'when range of dice request is too high' do
before do
srand(1)
end
it 'should create the right reply' do
stub_request(:get, "https://www.wired.com/2016/05/mathematical-challenge-of-designing-the-worlds-most-complex-120-sided-dice")
.to_return(status: 200, body: "", headers: {})
post.update!(raw: "roll 1d#{DiscourseNarrativeBot::Dice::MAXIMUM_RANGE_OF_DICE + 1}")
described_class.new(:reply, user, post_id: post.id).select
new_post = Post.last
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.dice.out_of_range')}
#{I18n.t('discourse_narrative_bot.dice.results', results: '38')}
RAW
expect(new_post.raw).to eq(expected_raw.chomp)
end
end
describe 'when number of dice to roll is too high' do
it 'should create the right reply' do
post.update!(raw: "roll #{DiscourseNarrativeBot::Dice::MAXIMUM_NUM_OF_DICE + 1}d1")
described_class.new(:reply, user, post_id: post.id).select
new_post = Post.last
expected_raw = <<~RAW
#{I18n.t('discourse_narrative_bot.dice.not_enough_dice', num_of_dice: DiscourseNarrativeBot::Dice::MAXIMUM_NUM_OF_DICE)}
#{I18n.t('discourse_narrative_bot.dice.results', results: '1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1')}
RAW
expect(new_post.raw).to eq(expected_raw.chomp)
end
end
describe 'when dice combination is invalid' do
it 'should create the right reply' do
post.update!(raw: "roll 0d1")
described_class.new(:reply, user, post_id: post.id).select
expect(Post.last.raw).to eq(I18n.t(
'discourse_narrative_bot.dice.invalid'
))
end
end
end
end
end
context 'when in a normal PM with discobot' do
describe 'when discobot is replied to' do
it 'should create the right reply' do
SiteSetting.discourse_narrative_bot_disable_public_replies = true
post.update!(raw: 'Show me what you can do @discobot')
described_class.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(random_mention_reply)
end
it 'should not rate limit help message' do
post.update!(raw: '@discobot')
other_post = Fabricate(:post, raw: 'discobot', topic: post.topic)
[post, other_post].each do |reply|
described_class.new(:reply, user, post_id: reply.id).select
expect(Post.last.raw).to eq(random_mention_reply)
end
end
end
end
end
context 'random discobot mentions' do
let(:topic) { Fabricate(:topic) }
let(:post) { Fabricate(:post, topic: topic, user: user) }
describe 'when discobot public replies are disabled' do
before do
SiteSetting.discourse_narrative_bot_disable_public_replies = true
end
describe 'when discobot is mentioned' do
it 'should not reply' do
post.update!(raw: 'Show me what you can do @discobot')
expect do
described_class.new(:reply, user, post_id: post.id).select
end.to_not change { Post.count }
end
end
end
describe 'when discobot is mentioned' do
it 'should create the right reply' do
post.update!(raw: 'Show me what you can do @discobot')
described_class.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(random_mention_reply)
end
describe 'rate limiting random reply message in public topic' do
let(:topic) { Fabricate(:topic) }
let(:other_post) { Fabricate(:post, raw: '@discobot show me something', topic: topic) }
let(:post) { Fabricate(:post, topic: topic) }
after do
$redis.flushall
end
describe 'when random reply massage has been displayed in the last 6 hours' do
it 'should not do anything' do
$redis.set(
"#{described_class::PUBLIC_DISPLAY_BOT_HELP_KEY}:#{other_post.topic_id}",
post.post_number - 11
)
$redis.class.any_instance.expects(:ttl).returns(19.hours.to_i)
user
post.update!(raw: "Show me what you can do @discobot")
expect { described_class.new(:reply, user, post_id: post.id).select }
.to_not change { Post.count }
end
end
describe 'when random reply message has not been displayed in the last 6 hours' do
it 'should create the right reply' do
$redis.set(
"#{described_class::PUBLIC_DISPLAY_BOT_HELP_KEY}:#{other_post.topic_id}",
post.post_number - 11
)
$redis.class.any_instance.expects(:ttl).returns(7.hours.to_i)
user
post.update!(raw: "Show me what you can do @discobot")
described_class.new(:reply, user, post_id: post.id).select
expect(Post.last.raw).to eq(random_mention_reply)
end
end
describe 'when random reply message has been displayed in the last 10 replies' do
it 'should not do anything' do
described_class.new(:reply, user, post_id: other_post.id).select
expect(Post.last.raw).to eq(random_mention_reply)
expect($redis.get(
"#{described_class::PUBLIC_DISPLAY_BOT_HELP_KEY}:#{other_post.topic_id}"
).to_i).to eq(other_post.post_number.to_i)
user
post.update!(raw: "Show me what you can do @discobot")
expect do
described_class.new(:reply, user, post_id: post.id).select
end.to_not change { Post.count }
end
end
end
describe 'when asking discobot for help' do
it 'should create the right reply' do
post.update!(raw: '@discobot display help')
described_class.new(:reply, user, post_id: post.id).select
expect(Post.last.raw).to eq(help_message)
end
end
describe 'when asking discobot to start new user track' do
describe 'invalid text' do
it 'should not trigger the bot' do
post.update!(raw: '`@discobot start new user track`')
expect { described_class.new(:reply, user, post_id: post.id).select }
.to_not change { Post.count }
end
end
end
describe 'when discobot is asked to roll dice' do
it 'should create the right reply' do
post.update!(raw: '@discobot roll 2d1')
described_class.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(
I18n.t("discourse_narrative_bot.dice.results",
results: '1, 1'
))
end
describe 'when dice roll is requested incorrectly' do
it 'should create the right reply' do
post.update!(raw: 'roll 2d1 @discobot')
described_class.new(:reply, user, post_id: post.id).select
expect(Post.last.raw).to eq(random_mention_reply)
end
end
describe 'when roll dice command is present inside a quote' do
it 'should ignore the command' do
user
post.update!(raw: '[quote="Donkey, post:6, topic:1"]@discobot roll 2d1[/quote]')
expect { described_class.new(:reply, user, post_id: post.id).select }
.to_not change { Post.count }
end
end
end
describe 'when a quote is requested' do
it 'should create the right reply' do
Excon.stub({ method: :get, hostname: 'api.forismatic.com' },
status: 200,
body: "{\"quoteText\":\"Be Like Water\",\"quoteAuthor\":\"Bruce Lee\"}"
)
['@discobot quote', 'hello @discobot quote there'].each do |raw|
post.update!(raw: raw)
described_class.new(:reply, user, post_id: post.id).select
new_post = Post.last
expect(new_post.raw).to eq(
I18n.t("discourse_narrative_bot.quote.results",
quote: "Be Like Water", author: "Bruce Lee"
))
end
end
describe 'when quote is requested incorrectly' do
it 'should create the right reply' do
post.update!(raw: 'quote @discobot')
described_class.new(:reply, user, post_id: post.id).select
expect(Post.last.raw).to eq(random_mention_reply)
end
end
describe 'when quote command is present inside a onebox or quote' do
it 'should ignore the command' do
user
post.update!(raw: '[quote="Donkey, post:6, topic:1"]@discobot quote[/quote]')
expect { described_class.new(:reply, user, post_id: post.id).select }
.to_not change { Post.count }
end
end
describe 'when user requesting quote has a preferred locale' do
before do
SiteSetting.allow_user_locale = true
user.update!(locale: 'it')
srand(1)
end
it 'should create the right reply' do
post.update!(raw: '@discobot quote')
described_class.new(:reply, user, post_id: post.id).select
key = "discourse_narrative_bot.quote.6"
expect(Post.last.raw).to eq(I18n.t('discourse_narrative_bot.quote.results',
quote: I18n.t("#{key}.quote"), author: I18n.t("#{key}.author")
))
end
end
end
describe 'when magic 8 ball is requested' do
before do
srand(1)
end
it 'should create the right reply' do
post.update!(raw: '@discobot fortune')
described_class.new(:reply, user, post_id: post.id).select
expect(Post.last.raw).to eq(I18n.t('discourse_narrative_bot.magic_8_ball.result',
result: I18n.t("discourse_narrative_bot.magic_8_ball.answers.6")
))
end
end
describe 'when bot is asked to reset/start a track' do
describe 'when user likes a post containing a reset trigger' do
it 'should not start the track' do
another_post = Fabricate(:post,
user: Fabricate(:user),
topic: topic,
raw: "@discobot start new user"
)
user
expect do
PostAction.act(user, another_post, PostActionType.types[:like])
end.to_not change { Post.count }
end
end
end
end
end
context 'pm to self' do
let(:other_topic) do
topic_allowed_user = Fabricate.build(:topic_allowed_user, user: user)
Fabricate(:private_message_topic, topic_allowed_users: [topic_allowed_user])
end
let(:other_post) { Fabricate(:post, topic: other_topic) }
describe 'when a new message is made' do
it 'should not do anything' do
other_post
expect { described_class.new(:reply, user, post_id: other_post.id).select }
.to_not change { Post.count }
end
end
end
context 'pms to bot' do
let(:other_topic) do
topic_allowed_user = Fabricate.build(:topic_allowed_user, user: user)
bot = Fabricate.build(:topic_allowed_user, user: discobot_user)
Fabricate(:private_message_topic, topic_allowed_users: [topic_allowed_user, bot])
end
let(:other_post) { Fabricate(:post, topic: other_topic) }
describe 'when a new like is made' do
it 'should not do anything' do
other_post
expect { described_class.new(:like, user, post_id: other_post.id).select }
.to_not change { Post.count }
end
end
describe 'when a new message is made' do
it 'should create the right reply' do
described_class.new(:reply, user, post_id: other_post.id).select
expect(Post.last.raw).to eq(random_mention_reply)
end
end
describe 'when user thanks the bot' do
it 'should like the post' do
other_post.update!(raw: 'thanks!')
expect { described_class.new(:reply, user, post_id: other_post.id).select }
.to change { PostAction.count }.by(1)
post_action = PostAction.last
expect(post_action.post).to eq(other_post)
expect(post_action.post_action_type_id).to eq(PostActionType.types[:like])
end
end
end
end
end