Skip to content

Commit

Permalink
Add a class to ask questions with custom behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
imobachgs committed Oct 20, 2017
1 parent 7b48387 commit 9156b58
Show file tree
Hide file tree
Showing 5 changed files with 319 additions and 2 deletions.
3 changes: 3 additions & 0 deletions package/autoyast2.spec
Expand Up @@ -288,6 +288,9 @@ rmdir $RPM_BUILD_ROOT/%{_prefix}/share/doc/packages/autoyast2/html/autoyast
%dir %{yast_libdir}/autoinstall
%{yast_libdir}/autoinstall/*.rb

%dir %{yast_libdir}/autoinstall/dialogs
%{yast_libdir}/autoinstall/dialogs/*.rb

# scripts
%{_prefix}/lib/YaST2/bin/fetch_image.sh
%{_prefix}/lib/YaST2/bin/autoyast-initscripts.sh
Expand Down
6 changes: 5 additions & 1 deletion src/Makefile.am
Expand Up @@ -74,6 +74,10 @@ ylib_DATA = \
lib/autoinstall/pkg_gpg_check_handler.rb \
lib/autoinstall/storage_proposal.rb

ydialogsdir = @ylibdir@/autoinstall/dialogs
ydialogs_DATA = \
lib/autoinstall/dialogs/question.rb

scrconf_DATA = \
scrconf/cfg_autoinstall.scr \
scrconf/autoinstall.scr
Expand Down Expand Up @@ -104,6 +108,6 @@ desktop_DATA = \
fillup_DATA = \
fillup/sysconfig.autoinstall

EXTRA_DIST = $(module_DATA) $(client_DATA) $(ynclude_DATA) $(scrconf_DATA) $(schemafiles_DATA) $(ybin_SCRIPTS) $(desktop_DATA) $(fillup_DATA) $(ylib_DATA)
EXTRA_DIST = $(module_DATA) $(client_DATA) $(ynclude_DATA) $(scrconf_DATA) $(schemafiles_DATA) $(ybin_SCRIPTS) $(desktop_DATA) $(fillup_DATA) $(ylib_DATA) $(ydialogs_DATA)

include $(top_srcdir)/Makefile.am.common
179 changes: 179 additions & 0 deletions src/lib/autoinstall/dialogs/question.rb
@@ -0,0 +1,179 @@
# encoding: utf-8

# Copyright (c) [2017] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "ui/dialog"

Yast.import "HTML"
Yast.import "Label"
Yast.import "UI"

module Y2Autoinstallation
module Dialogs
# Generic dialog for AutoYaST questions supporting RichText.
#
# It can behave in two different ways:
#
# * Ask the user if she/he wants to continue or abort the installation.
# * Display a message and only offer an 'Abort' button.
#
# In order to integrate nicely with AutoYaST, a timeout can be set.
# Bear in mind that the timeout will not be respected when a showing only
# the 'Abort' button.
#
# This dialog could be extended in the future in order to support other
# AutoYaST interaction which are not covered in the {Yast::Report} module.
class Question < UI::Dialog

# @return [String] Dialog's content
attr_reader :content

# Constructor
#
# @param content [String] Dialog's content
# @param timeout [Integer] Countdown (in seconds); 0 means no timeout.
# @param buttons_set [Symbol] Buttons set (:abort, :question)
def initialize(content, timeout: 10, buttons_set: :question)
@content = content
@remaining_time = timeout
@timed = timeout > 0
@buttons_set = buttons_set
end

# Return YaST terms for dialog's content
#
# @return [Yast::Term] Dialog's content
# @see UI::Dialog#dialog_content
def dialog_content
HBox(
VSpacing(20),
VBox(
HSpacing(70),
Left(Heading("Partitioning issues")),
VSpacing(1),
RichText(content),
timed? ? ReplacePoint(Id(:counter_replace), counter) : Empty(),
ButtonBox(*buttons)
)
)
end

# 'Continue' button handler
#
# When the 'Continue' button is pressed, the dialog will return the {:ok} value.
def ok_handler
finish_dialog(:ok)
end

# 'Abort' button handler
#
# When the 'Abort' button is pressed, the dialog will return the {:abort} value.
def abort_handler
finish_dialog(:abort)
end

# 'Stop' button handler
#
# When the 'Stop' button is pressed, the countdown will stop.
def stop_handler
@remaining_time = nil
Yast::UI.ChangeWidget(Id(:stop), :Enabled, false)
end

def timeout_handler
return finish_dialog(:ok) if @remaining_time.zero?
@remaining_time -= 1
Yast::UI.ReplaceWidget(Id(:counter_replace), counter)
end

private
# @return [Integer] Timeout
attr_reader :timeout

# @return [Symbol] Buttons set (:abort, :question)
attr_reader :buttons_set

# Determine whether it is a timed dialog or not
#
# @return [Boolean] True if the timeout is running; false otherwise
def timed?
@timed
end

# Return dialog buttons
#
# @see question_buttons
# @see abort_buttons
def buttons
send("#{buttons_set}_buttons")
end

# Return buttons to show when the user should be asked about what to do
#
# @return [Array<Yast::Term>]
def question_buttons
set = [
PushButton(Id(:ok), Opt(:okButton, :key_F9, :default), Yast::Label.ContinueButton),
abort_button
]

if timed?
set.unshift(
PushButton(Id(:stop), Opt(:customButton), Yast::Label.StopButton),
)
end

set
end

# Return buttons to show when abort is the only option
#
# @return [Array<Yast::Term>]
def abort_buttons
[abort_button]
end

# Return an 'Abort' button
#
# @return Yast::Term
def abort_button
PushButton(Id(:abort), Opt(:cancel_button, :key_F10), Yast::Label.AbortButton)
end

# Timeout counter
#
# @return [Yast::Term]
def counter
Label(Id(:counter), @remaining_time.to_s)
end

# Handle user input
#
# @return [Symbol] User input (:ok, :stop, :abort or :timeout)
def user_input
if timed? && @remaining_time
Yast::UI.TimeoutUserInput(1000)
else
Yast::UI.UserInput
end
end
end
end
end
3 changes: 2 additions & 1 deletion test/Makefile.am
Expand Up @@ -18,7 +18,8 @@ TESTS = \
include/ask_test.rb \
lib/module_config_builder_test.rb \
lib/pkg_gpg_check_handler_test.rb \
lib/storage_proposal_test.rb
lib/storage_proposal_test.rb \
lib/dialogs/question_test.rb

TEST_EXTENSIONS = .rb
RB_LOG_COMPILER = rspec
Expand Down
130 changes: 130 additions & 0 deletions test/lib/dialogs/question_test.rb
@@ -0,0 +1,130 @@
#!/usr/bin/env rspec
# encoding: utf-8

# Copyright (c) [2017] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require_relative "../../test_helper"
require "autoinstall/dialogs/question"
require "y2storage/autoinst_issues"

describe Y2Autoinstallation::Dialogs::Question do
subject(:dialog) { described_class.new(content, timeout: timeout, buttons_set: buttons_set) }

let(:timeout) { 0 }
let(:content) { "some content" }
let(:buttons_set) { :abort }

before do
allow(Yast::UI).to receive(:OpenDialog).and_return(true)
allow(Yast::UI).to receive(:CloseDialog).and_return(true)
end

describe "#dialog_content" do
it "displays the given content" do
expect(dialog.dialog_content.to_s).to include(content)
end

context "when buttons_set is set to :abort" do
let(:buttons_set) { :abort }

it "only shows the 'Abort' button" do
expect(dialog).to receive(:PushButton).with(Yast::Term.new(:id, :abort), anything, anything)
dialog.dialog_content
end

context "and a timeout was given" do
it "ignores the timeout" do
expect(dialog).to receive(:PushButton).with(Yast::Term.new(:id, :abort), anything, anything)
expect(dialog).to_not receive(:Id).with(:counter)
allow(dialog).to receive(:Id).and_call_original
dialog.dialog_content
end
end
end

context "when buttons_set is set to :question" do
let(:buttons_set) { :question }

it "shows the 'Continue' and 'Abort' buttons" do
expect(dialog).to receive(:PushButton).with(Yast::Term.new(:id, :abort), anything, anything)
expect(dialog).to receive(:PushButton).with(Yast::Term.new(:id, :ok), anything, anything)
dialog.dialog_content
end

context "and a timeout was given" do
let(:timeout) { 10 }

it "shows the 'Stop' button" do
expect(dialog).to receive(:PushButton).with(Yast::Term.new(:id, :stop), anything, anything)
allow(dialog).to receive(:PushButton).and_call_original
dialog.dialog_content
end
end
end
end

describe "#run" do
let(:timeout) { 10 }

context "when no timeout was given" do
let(:timeout) { 0 }

it "does not use a timeout when asking for user input" do
expect(Yast::UI).to receive(:UserInput).and_return(:ok)
dialog.run
end
end

context "when user pushes 'Abort'" do
let(:input) { :abort }

it "returns :abort" do
allow(Yast::UI).to receive(:TimeoutUserInput).and_return(:abort)
expect(dialog.run).to eq(:abort)
end
end

context "when user pushes 'Continue'" do
it "returns :ok" do
allow(Yast::UI).to receive(:TimeoutUserInput).and_return(:ok)
expect(dialog.run).to eq(:ok)
end
end

context "when user pushes 'Stop'" do
it "stops the countdown" do
allow(Yast::UI).to receive(:TimeoutUserInput).and_return(:stop)
allow(Yast::UI).to receive(:UserInput).and_return(:ok)
expect(Yast::UI).to receive(:ChangeWidget)
.with(Id(:stop), :Enabled, false)
dialog.run
end
end

context "when user input times-out" do
let(:timeout) { 1 }

it "returns :ok" do
allow(Yast::UI).to receive(:TimeoutUserInput).and_return(:timeout)
expect(dialog.run).to eq(:ok)
end
end
end
end

0 comments on commit 9156b58

Please sign in to comment.