summaryrefslogtreecommitdiffstats
path: root/features/step_definitions/pidgin.rb
diff options
context:
space:
mode:
Diffstat (limited to 'features/step_definitions/pidgin.rb')
-rw-r--r--features/step_definitions/pidgin.rb369
1 files changed, 324 insertions, 45 deletions
diff --git a/features/step_definitions/pidgin.rb b/features/step_definitions/pidgin.rb
index 23b48e26..3f5ed931 100644
--- a/features/step_definitions/pidgin.rb
+++ b/features/step_definitions/pidgin.rb
@@ -1,7 +1,210 @@
+# Extracts the secrets for the XMMP account `account_name`.
+def xmpp_account(account_name, required_options = [])
+ begin
+ account = $config["Pidgin"]["Accounts"]["XMPP"][account_name]
+ check_keys = ["username", "domain", "password"] + required_options
+ for key in check_keys do
+ assert(account.has_key?(key))
+ assert_not_nil(account[key])
+ assert(!account[key].empty?)
+ end
+ rescue NoMethodError, Test::Unit::AssertionFailedError
+ raise(
+<<EOF
+Your Pidgin:Accounts:XMPP:#{account} is incorrect or missing from your local configuration file (#{LOCAL_CONFIG_FILE}). See wiki/src/contribute/release_process/test/usage.mdwn for the format.
+EOF
+)
+ end
+ return account
+end
+
+def wait_and_focus(img, time = 10, window)
+ begin
+ @screen.wait(img, time)
+ rescue FindFailed
+ $vm.focus_window(window)
+ @screen.wait(img, time)
+ end
+end
+
+def focus_pidgin_irc_conversation_window(account)
+ if account == 'I2P'
+ # After connecting to Irc2P messages are sent from services. Most of the
+ # time the services will send their messages right away. If there's lag we
+ # may in fact join the channel _before_ the message is received. We'll look
+ # for a message from InfoServ first then default to looking for '#i2p'
+ try_for(20) do
+ begin
+ $vm.focus_window('InfoServ')
+ rescue ExecutionFailedInVM
+ $vm.focus_window('#i2p')
+ end
+ end
+ else
+ account = account.sub(/^irc\./, '')
+ try_for(20) do
+ $vm.focus_window(".*#{Regexp.escape(account)}$")
+ end
+ end
+end
+
+When /^I create my XMPP account$/ do
+ account = xmpp_account("Tails_account")
+ @screen.click("PidginAccountManagerAddButton.png")
+ @screen.wait("PidginAddAccountWindow.png", 20)
+ @screen.click_mid_right_edge("PidginAddAccountProtocolLabel.png")
+ @screen.click("PidginAddAccountProtocolXMPP.png")
+ # We first wait for some field that is shown for XMPP but not the
+ # default (IRC) since we otherwise may decide where we click before
+ # the GUI has updated after switching protocol.
+ @screen.wait("PidginAddAccountXMPPDomain.png", 5)
+ @screen.click_mid_right_edge("PidginAddAccountXMPPUsername.png")
+ @screen.type(account["username"])
+ @screen.click_mid_right_edge("PidginAddAccountXMPPDomain.png")
+ @screen.type(account["domain"])
+ @screen.click_mid_right_edge("PidginAddAccountXMPPPassword.png")
+ @screen.type(account["password"])
+ @screen.click("PidginAddAccountXMPPRememberPassword.png")
+ if account["connect_server"]
+ @screen.click("PidginAddAccountXMPPAdvancedTab.png")
+ @screen.click_mid_right_edge("PidginAddAccountXMPPConnectServer.png")
+ @screen.type(account["connect_server"])
+ end
+ @screen.click("PidginAddAccountXMPPAddButton.png")
+end
+
+Then /^Pidgin automatically enables my XMPP account$/ do
+ $vm.focus_window('Buddy List')
+ @screen.wait("PidginAvailableStatus.png", 60*3)
+end
+
+Given /^my XMPP friend goes online( and joins the multi-user chat)?$/ do |join_chat|
+ account = xmpp_account("Friend_account", ["otr_key"])
+ bot_opts = account.select { |k, v| ["connect_server"].include?(k) }
+ if join_chat
+ bot_opts["auto_join"] = [@chat_room_jid]
+ end
+ @friend_name = account["username"]
+ @chatbot = ChatBot.new(account["username"] + "@" + account["domain"],
+ account["password"], account["otr_key"], bot_opts)
+ @chatbot.start
+ add_after_scenario_hook { @chatbot.stop }
+ $vm.focus_window('Buddy List')
+ @screen.wait("PidginFriendOnline.png", 60)
+end
+
+When /^I start a conversation with my friend$/ do
+ $vm.focus_window('Buddy List')
+ # Clicking the middle, bottom of this image should query our
+ # friend, given it's the only subscribed user that's online, which
+ # we assume.
+ r = @screen.find("PidginFriendOnline.png")
+ bottom_left = r.getBottomLeft()
+ x = bottom_left.getX + r.getW/2
+ y = bottom_left.getY
+ @screen.doubleClick_point(x, y)
+ # Since Pidgin sets the window name to the contact, we have no good
+ # way to identify the conversation window. Let's just look for the
+ # expected menu bar.
+ @screen.wait("PidginConversationWindowMenuBar.png", 10)
+end
+
+And /^I say something to my friend( in the multi-user chat)?$/ do |multi_chat|
+ msg = "ping" + Sikuli::Key.ENTER
+ if multi_chat
+ $vm.focus_window(@chat_room_jid.split("@").first)
+ msg = @friend_name + ": " + msg
+ else
+ $vm.focus_window(@friend_name)
+ end
+ @screen.type(msg)
+end
+
+Then /^I receive a response from my friend( in the multi-user chat)?$/ do |multi_chat|
+ if multi_chat
+ $vm.focus_window(@chat_room_jid.split("@").first)
+ else
+ $vm.focus_window(@friend_name)
+ end
+ @screen.wait("PidginFriendExpectedAnswer.png", 20)
+end
+
+When /^I start an OTR session with my friend$/ do
+ $vm.focus_window(@friend_name)
+ @screen.click("PidginConversationOTRMenu.png")
+ @screen.hide_cursor
+ @screen.click("PidginOTRMenuStartSession.png")
+end
+
+Then /^Pidgin automatically generates an OTR key$/ do
+ @screen.wait("PidginOTRKeyGenPrompt.png", 30)
+ @screen.wait_and_click("PidginOTRKeyGenPromptDoneButton.png", 30)
+end
+
+Then /^an OTR session was successfully started with my friend$/ do
+ $vm.focus_window(@friend_name)
+ @screen.wait("PidginConversationOTRUnverifiedSessionStarted.png", 10)
+end
+
+# The reason the chat must be empty is to guarantee that we don't mix
+# up messages/events from other users with the ones we expect from the
+# bot.
+When /^I join some empty multi-user chat$/ do
+ $vm.focus_window('Buddy List')
+ @screen.click("PidginBuddiesMenu.png")
+ @screen.wait_and_click("PidginBuddiesMenuJoinChat.png", 10)
+ @screen.wait_and_click("PidginJoinChatWindow.png", 10)
+ @screen.click_mid_right_edge("PidginJoinChatRoomLabel.png")
+ account = xmpp_account("Tails_account")
+ if account.has_key?("chat_room") && \
+ !account["chat_room"].nil? && \
+ !account["chat_room"].empty?
+ chat_room = account["chat_room"]
+ else
+ chat_room = random_alnum_string(10, 15)
+ end
+ @screen.type(chat_room)
+
+ # We will need the conference server later, when starting the bot.
+ @screen.click_mid_right_edge("PidginJoinChatServerLabel.png")
+ @screen.type("a", Sikuli::KeyModifier.CTRL)
+ @screen.type("c", Sikuli::KeyModifier.CTRL)
+ conference_server =
+ $vm.execute_successfully("xclip -o", :user => LIVE_USER).stdout.chomp
+ @chat_room_jid = chat_room + "@" + conference_server
+
+ @screen.click("PidginJoinChatButton.png")
+ # The following will both make sure that the we joined the chat, and
+ # that it is empty. We'll also deal with the *potential* "Create New
+ # Room" prompt that Pidgin shows for some server configurations.
+ images = ["PidginCreateNewRoomPrompt.png",
+ "PidginChat1UserInRoom.png"]
+ image_found, _ = @screen.waitAny(images, 30)
+ if image_found == "PidginCreateNewRoomPrompt.png"
+ @screen.click("PidginCreateNewRoomAcceptDefaultsButton.png")
+ end
+ $vm.focus_window(@chat_room_jid)
+ @screen.wait("PidginChat1UserInRoom.png", 10)
+end
+
+# Since some servers save the scrollback, and sends it when joining,
+# it's safer to clear it so we do not get false positives from old
+# messages when looking for a particular response, or similar.
+When /^I clear the multi-user chat's scrollback$/ do
+ $vm.focus_window(@chat_room_jid)
+ @screen.click("PidginConversationMenu.png")
+ @screen.wait_and_click("PidginConversationMenuClearScrollback.png", 10)
+end
+
+Then /^I can see that my friend joined the multi-user chat$/ do
+ $vm.focus_window(@chat_room_jid)
+ @screen.wait("PidginChat2UsersInRoom.png", 60)
+end
+
def configured_pidgin_accounts
- accounts = []
- xml = REXML::Document.new(@vm.file_content('$HOME/.purple/accounts.xml',
- $live_user))
+ accounts = Hash.new
+ xml = REXML::Document.new($vm.file_content('$HOME/.purple/accounts.xml',
+ LIVE_USER))
xml.elements.each("account/account") do |e|
account = e.elements["name"].text
account_name, network = account.split("@")
@@ -9,14 +212,14 @@ def configured_pidgin_accounts
port = e.elements["settings/setting[@name='port']"].text
nickname = e.elements["settings/setting[@name='username']"].text
real_name = e.elements["settings/setting[@name='realname']"].text
- accounts.push({
- 'name' => account_name,
- 'network' => network,
- 'protocol' => protocol,
- 'port' => port,
- 'nickname' => nickname,
- 'real_name' => real_name,
- })
+ accounts[network] = {
+ 'name' => account_name,
+ 'network' => network,
+ 'protocol' => protocol,
+ 'port' => port,
+ 'nickname' => nickname,
+ 'real_name' => real_name,
+ }
end
return accounts
@@ -26,10 +229,17 @@ def chan_image (account, channel, image)
images = {
'irc.oftc.net' => {
'#tails' => {
- 'roaster' => 'PidginTailsChannelEntry',
+ 'roster' => 'PidginTailsChannelEntry',
'conversation_tab' => 'PidginTailsConversationTab',
'welcome' => 'PidginTailsChannelWelcome',
}
+ },
+ 'I2P' => {
+ '#i2p' => {
+ 'roster' => 'PidginI2PChannelEntry',
+ 'conversation_tab' => 'PidginI2PConversationTab',
+ 'welcome' => 'PidginI2PChannelWelcome',
+ }
}
}
return images[account][channel][image] + ".png"
@@ -38,21 +248,21 @@ end
def default_chan (account)
chans = {
'irc.oftc.net' => '#tails',
+ 'I2P' => '#i2p',
}
return chans[account]
end
def pidgin_otr_keys
- return @vm.file_content('$HOME/.purple/otr.private_key', $live_user)
+ return $vm.file_content('$HOME/.purple/otr.private_key', LIVE_USER)
end
Given /^Pidgin has the expected accounts configured with random nicknames$/ do
- next if @skip_steps_while_restoring_background
expected = [
["irc.oftc.net", "prpl-irc", "6697"],
["127.0.0.1", "prpl-irc", "6668"],
]
- configured_pidgin_accounts.each() do |account|
+ configured_pidgin_accounts.values.each() do |account|
assert(account['nickname'] != "XXX_NICK_XXX", "Nickname was no randomised")
assert_equal(account['nickname'], account['real_name'],
"Nickname and real name are not identical: " +
@@ -69,63 +279,118 @@ Given /^Pidgin has the expected accounts configured with random nicknames$/ do
end
When /^I start Pidgin through the GNOME menu$/ do
- next if @skip_steps_while_restoring_background
- @screen.wait_and_click("GnomeApplicationsMenu.png", 10)
- @screen.wait_and_click("GnomeApplicationsInternet.png", 10)
- @screen.wait_and_click("GnomeApplicationsPidgin.png", 20)
+ step 'I start "Pidgin" via the GNOME "Internet" applications menu'
end
When /^I open Pidgin's account manager window$/ do
- next if @skip_steps_while_restoring_background
- @screen.type("a", Sikuli::KeyModifier.CTRL) # shortcut for "manage accounts"
+ @screen.wait_and_click('PidginMenuAccounts.png', 20)
+ @screen.wait_and_click('PidginMenuManageAccounts.png', 20)
step "I see Pidgin's account manager window"
end
When /^I see Pidgin's account manager window$/ do
- next if @skip_steps_while_restoring_background
- @screen.wait("PidginAccountWindow.png", 20)
+ @screen.wait("PidginAccountWindow.png", 40)
end
When /^I close Pidgin's account manager window$/ do
- next if @skip_steps_while_restoring_background
@screen.wait_and_click("PidginAccountManagerCloseButton.png", 10)
end
-When /^I activate the "([^"]+)" Pidgin account$/ do |account|
- next if @skip_steps_while_restoring_background
+When /^I (de)?activate the "([^"]+)" Pidgin account$/ do |deactivate, account|
@screen.click("PidginAccount_#{account}.png")
@screen.type(Sikuli::Key.LEFT + Sikuli::Key.SPACE)
- # wait for the Pidgin to be connecting, otherwise sometimes the step
- # that closes the account management dialog happens before the account
- # is actually enabled
- @screen.wait("PidginConnecting.png", 5)
+ if deactivate
+ @screen.waitVanish('PidginAccountEnabledCheckbox.png', 5)
+ else
+ # wait for the Pidgin to be connecting, otherwise sometimes the step
+ # that closes the account management dialog happens before the account
+ # is actually enabled
+ @screen.waitAny(['PidginConnecting.png', 'PidginAvailableStatus.png'], 5)
+ end
end
+def deactivate_and_activate_pidgin_account(account)
+ debug_log("Deactivating and reactivating Pidgin account #{account}")
+ step "I open Pidgin's account manager window"
+ step "I deactivate the \"#{account}\" Pidgin account"
+ step "I close Pidgin's account manager window"
+ step "I open Pidgin's account manager window"
+ step "I activate the \"#{account}\" Pidgin account"
+ step "I close Pidgin's account manager window"
+end
+
+
+
Then /^Pidgin successfully connects to the "([^"]+)" account$/ do |account|
- next if @skip_steps_while_restoring_background
- expected_channel_entry = chan_image(account, default_chan(account), 'roaster')
- @screen.wait(expected_channel_entry, 60)
+ expected_channel_entry = chan_image(account, default_chan(account), 'roster')
+ reconnect_button = 'PidginReconnect.png'
+ recovery_on_failure = Proc.new do
+ if @screen.exists('PidginReconnect.png')
+ @screen.click('PidginReconnect.png')
+ else
+ deactivate_and_activate_pidgin_account(account)
+ end
+ end
+ retrier_method = account == 'I2P' ? method(:retry_i2p) : method(:retry_tor)
+ retrier_method.call(recovery_on_failure) do
+ begin
+ $vm.focus_window('Buddy List')
+ rescue ExecutionFailedInVM
+ # Sometimes focusing the window with xdotool will fail with the
+ # conversation window right on top of it. We'll try to close the
+ # conversation window. At worst, the test will still fail...
+ close_pidgin_conversation_window(account)
+ end
+ on_screen, _ = @screen.waitAny([expected_channel_entry, reconnect_button], 60)
+ unless on_screen == expected_channel_entry
+ raise "Connecting to account #{account} failed."
+ end
+ end
+end
+
+Then /^the "([^"]*)" account only responds to PING and VERSION CTCP requests$/ do |irc_server|
+ ctcp_cmds = [
+ "CLIENTINFO", "DATE", "ERRMSG", "FINGER", "PING", "SOURCE", "TIME",
+ "USERINFO", "VERSION"
+ ]
+ expected_ctcp_replies = {
+ "PING" => /^\d+$/,
+ "VERSION" => /^Purple IRC$/
+ }
+ spam_target = configured_pidgin_accounts[irc_server]["nickname"]
+ ctcp_check = CtcpChecker.new(irc_server, 6667, spam_target, ctcp_cmds,
+ expected_ctcp_replies)
+ ctcp_check.verify_ctcp_responses
end
Then /^I can join the "([^"]+)" channel on "([^"]+)"$/ do |channel, account|
- next if @skip_steps_while_restoring_background
- @screen.doubleClick( chan_image(account, channel, 'roaster'))
- @screen.wait_and_click(chan_image(account, channel, 'conversation_tab'), 10)
+ @screen.doubleClick( chan_image(account, channel, 'roster'))
+ @screen.hide_cursor
+ focus_pidgin_irc_conversation_window(account)
+ try_for(60) do
+ begin
+ @screen.wait_and_click(chan_image(account, channel, 'conversation_tab'), 5)
+ rescue FindFailed => e
+ # If the channel tab can't be found it could be because there were
+ # multiple connection attempts and the channel tab we want is off the
+ # screen. We'll try closing tabs until the one we want can be found.
+ @screen.type("w", Sikuli::KeyModifier.CTRL)
+ raise e
+ end
+ end
+ @screen.hide_cursor
@screen.wait( chan_image(account, channel, 'welcome'), 10)
end
Then /^I take note of the configured Pidgin accounts$/ do
- next if @skip_steps_while_restoring_background
@persistent_pidgin_accounts = configured_pidgin_accounts
end
Then /^I take note of the OTR key for Pidgin's "([^"]+)" account$/ do |account_name|
- next if @skip_steps_while_restoring_background
@persistent_pidgin_otr_keys = pidgin_otr_keys
end
Then /^Pidgin has the expected persistent accounts configured$/ do
- next if @skip_steps_while_restoring_background
current_accounts = configured_pidgin_accounts
assert(current_accounts <=> @persistent_pidgin_accounts,
"Currently configured Pidgin accounts do not match the persistent ones:\n" +
@@ -135,7 +400,6 @@ Then /^Pidgin has the expected persistent accounts configured$/ do
end
Then /^Pidgin has the expected persistent OTR keys$/ do
- next if @skip_steps_while_restoring_background
assert_equal(pidgin_otr_keys, @persistent_pidgin_otr_keys)
end
@@ -143,6 +407,7 @@ def pidgin_add_certificate_from (cert_file)
# Here, we need a certificate that is not already in the NSS database
step "I copy \"/usr/share/ca-certificates/spi-inc.org/spi-cacert-2008.crt\" to \"#{cert_file}\" as user \"amnesia\""
+ $vm.focus_window('Buddy List')
@screen.wait_and_click('PidginToolsMenu.png', 10)
@screen.wait_and_click('PidginCertificatesMenuItem.png', 10)
@screen.wait('PidginCertificateManagerDialog.png', 10)
@@ -162,20 +427,19 @@ def pidgin_add_certificate_from (cert_file)
end
Then /^I can add a certificate from the "([^"]+)" directory to Pidgin$/ do |cert_dir|
- next if @skip_steps_while_restoring_background
pidgin_add_certificate_from("#{cert_dir}/test.crt")
- @screen.wait('PidginCertificateAddHostnameDialog.png', 10)
+ wait_and_focus('PidginCertificateAddHostnameDialog.png', 10, 'Certificate Import')
@screen.type("XXX test XXX" + Sikuli::Key.ENTER)
- @screen.wait('PidginCertificateTestItem.png', 10)
+ wait_and_focus('PidginCertificateTestItem.png', 10, 'Certificate Manager')
end
Then /^I cannot add a certificate from the "([^"]+)" directory to Pidgin$/ do |cert_dir|
- next if @skip_steps_while_restoring_background
pidgin_add_certificate_from("#{cert_dir}/test.crt")
- @screen.wait('PidginCertificateImportFailed.png', 10)
+ wait_and_focus('PidginCertificateImportFailed.png', 10, 'Import Error')
end
When /^I close Pidgin's certificate manager$/ do
+ wait_and_focus('PidginCertificateManagerDialog.png', 10, 'Certificate Manager')
@screen.type(Sikuli::Key.ESC)
# @screen.wait_and_click('PidginCertificateManagerClose.png', 10)
@screen.waitVanish('PidginCertificateManagerDialog.png', 10)
@@ -186,3 +450,18 @@ When /^I close Pidgin's certificate import failure dialog$/ do
# @screen.wait_and_click('PidginCertificateManagerClose.png', 10)
@screen.waitVanish('PidginCertificateImportFailed.png', 10)
end
+
+When /^I see the Tails roadmap URL$/ do
+ try_for(60) do
+ begin
+ @screen.find('PidginTailsRoadmapUrl.png')
+ rescue FindFailed => e
+ @screen.type(Sikuli::Key.PAGE_UP)
+ raise e
+ end
+ end
+end
+
+When /^I click on the Tails roadmap URL$/ do
+ @screen.click('PidginTailsRoadmapUrl.png')
+end