I am trying to create an activity diagram with PlantUML that does contain arrows that go back to existing nodes.
What I am looking for is a diagram that includes the two arrows "Arrow 1" and "Arrow 2" in the picture below:
I have tried creating this using the PlantUML beta syntax for Activity diagrams.
I achieved the diagram below:
by writing the following PlantUML code:
#startuml
start
:new - please check;
while (check sucessful?) is (is an error)
:to solve;
:in progress;
:solved;
endwhile (not an error)
:erledigt;
note left
reason:
* done
* not an error
* not fixable
end note
stop
#enduml
Does anybody has a hint on how to achieve this? It does not matter to me if the result is achieved by using the beta syntax or the older syntax.
you cannot have several flows starting from an action nor several flows going to an action
for the UML point of view so you need to add :
a decision node after the action "to solve" to have your two flows, each with a guard
a decision node after the action "erledigt" to have your two flows, each with a guard
a merge node before the action "to solve" to receive the flows from the decision node "check successful" and the action "erledigt".
For (1) use a "if-else" or a "split" in PlantUML
start
:new - please check;
while (check sucessful?) is (is an error)
:to solve;
if (duration) then (long)
:in progress;
else (immediat)
endif
:solved;
endwhile (not an error)
:erledigt;
note left
reason:
* done
* not an error
* not fixable
end note
stop
#enduml
For (3) may be you can use a "repeat while" also managing (2), but not sure you can in PlantUML without duplicating the three actions (to solve - in progress - solved) or doing them in an other activity you call. In PlantUML the code is drawn from control-structure except "goto", that allows to not cross lines, but you need a "goto" crossing lines. Note there is no problem going to the decision "check sucessfull" rather than to the action "to solve"
Related
I created a activity diagram using the legacy PlantUML Activity syntax (https://plantuml.com/activity-diagram-legacy):
#startuml
(*) --> "Request created" as Request
Request --> "Review request" as Review
if "is Accepted?" then
--> [Yes] "Assign"
if "is Urgent" then
--> [Yes] "In Development`"
--> "Engineer Assigned"
else
--> [No] "Backlog" as Backlog
Backlog --> Review
endif
else
--> [No] "Won’t Do"
endif
#enduml
Which looks like this:
I am now trying to recreate this diagram using the new (beta) syntax (https://plantuml.com/activity-diagram-beta) but am having difficulty replicating the capabilities of Short Codes to reference and return to an earlier activity step.
#startuml
start
:Request created;
:Review request;
if (is Accepted?) then (Yes)
:Assign;
if (is Urgent) then (Yes)
:In Development;
:Engineer Assigned;
stop
else (No)
:Backlog;
' HERE I WOULD LIKE BACKLOG TO HAVE
' A BACKWARD ARROW POINTING UP TO
' "REVIEW REQUEST"
endif
else (No)
:Won’t Do;
stop
endif
#enduml
Is it possible to achieve the same diagram with the new syntax as I created with the old?
you can do that :
#startuml
start
:Request created;
while (Review request?) is (accepted)
:Assign;
if (is Urgent ?) then (Yes)
:In Development;
:Engineer Assigned;
stop
else (No)
:Backlog;
endif
endwhile (not accepted)
:Won't Do;
stop
#enduml
producing :
... which is as illegal as your first diagram because there are two input flows to Review request and we are not in the case where one is the decisionInputFlow and the other the primary incoming edge (see §15.3.3.6
Decision Nodes page 390 of formal/2017-12-05), a merge node must be used before the decision node.
I'm trying to write a step that will match the following steps that are similar and capture parameters:
Step1: And I delete the filter(s) using the "UI"
Step2: Then I delete the filter(s) using the "API" for "doc-browser" context
Step3: I delete the filter(s) using the "API" for "doc-browser" context with user "file_user2"
I don't want to create 3 separate steps, they all start with I delete the filter using the #{arg} and the last 2 just extend on that.
I thought this was going to accomplish it:
And(/^I delete the filter\(s\) using the "([^"]*)"(?: for "([^"]*)" context| with user "([^"]*)")?/) do |delete_method, context, user|
case delete_method
when 'API'
if user.nil?
SearchFilters.new.delete_global_local_filters(delete_method, api_context_val: context)
else
SearchFilters.new.delete_global_local_filters(delete_method, { api_context_val: context, username: user })
end
when 'UI'
SearchFilters.new.delete_global_local_filters(delete_method, filter_name: #filter_name)
end
end
However, I'm not capturing my username.
Is it possible to accomplish having one step definition that captures all 3 variations and still captures my arguments?
Just duplicating my mailing list answer in case anyone else is interested in this question.
I understand you don't want to use three different steps, but in this case you really should because
The implementation of each step is a clear one liner, so you are removing a case statement.
The regex for this step is horrible and you will be removing that
Each individual step provides the opportunity to simplify the parameters being passed, you have 2 API and one UI paramter which can just be removed entirely (if I understand the code correctly you can remove all the params and regexs)
This is a clear case where a little bit of repetition is a price well worth paying for a simpler implementation.
Reducing the number of step definitions by using regex's and params is often an anti-pattern.
All best
Andrew
It's a mess in my head right now. I've seen this video tutorial to understand Wit.ai logic : https://www.youtube.com/watch?v=yLAHVPaHWFA
It's a really good video for basic training. But I can't still understand the logic.
I want to create a story like that Human(H) / Robot(R):
(H) Hello
(R) Hello human, you can choose A action or B action
(H) A action
(R) Ok human, this is A action
It's really simple but I don't know what to declare in "understanding" section.
1 - Do I have to create a "Say Hello" intent
2 - If yes, Do I have to create an "hello" entity and feed it with other salutations like "Hi", "yo", "What's up?", ...
3 - Do I have to create a "choose action" intent or it's just one intent for one story ? This is exactly what I don't understand.
4 - If yes, A&B action are free-text like ("An hotel", "a restaurant"). How can I teach the bot to recognized them ?
I just need some enlightening about those points ! It's really hard for me to translate and understand correctly. Thank you for you help !
You may declare a "greeting" intent(Intents are just user-created entities). After that, you can validate(feed) it with many other words like "Hey buddy, Sup bud, Hellloooo, Hi bot, etc". Also, you can give values to that intent such as Negative or Positive values.
For instance:
" Hey dumb f* " >> "greeting" Intent + Negative value ❌
" Hi brother! " >> "greeting" Intent + Positive value ✅
So that you can decide between:
"Language, please... Anyway. Hey {user_name}"
or
"Hey {user_name} ! :) Really nice to see you here ! "
to respond to the user's simple hi text.
Other than that:
(R) Hello human, you can choose A action or B action
(H) A action
(R) Ok human, this is A action
This type of interaction needs the usage of /converse API
https://wit.ai/docs/recipes#converse-link
In this above link go down to the "Handle yes/no answers" section.
Also, you can use Quick Replies for letting the user choose between the A Action or B Action. Quick Replies are really useful for this type of interactions.
https://developers.facebook.com/docs/messenger-platform/send-api-reference/quick-replies
I would like to retrieve the scenario state in the "After" scenario hook. I noticed that the .failed? method does not consider pending steps as failed steps.
So How can I determine that a scenario did not execute completely, because it failed OR because some steps were not implemented/defined.
You can use status method. The default value of status is :skipped, the failed one is :failed and the passed step is :passed. So you can write something like this:
do sth if step.status != :passed
Also, if you use !step.passed? it does the same thing because it only checks for the :passed status.
http://cukes.info/api/cucumber/ruby/yardoc/Cucumber/Ast/Scenario.html#failed%3F-instance_method
On that subject, you can also take a look at this post about demoing your feature specs to your customers: http://multifaceted.io/2013/demo-feature-tests/
LiohAu, you can use the 'status' method on a scenario itself rather than on individual steps. Try this: In hooks, add
After do |scenario|
p scenario.status
end
This will give the statuses as follows:
Any step not implemented / defined, it'll give you :undefined
Scenario fails (when all steps are defined) :failed
Scenario passes :passed
Using the same hook, it'll give you the status for scenario outline, but for each example row (since for each example row, it is an individual scenario). So if at all you want the result of an entire outline, you'll need to capture result for all example rows and compute the final result accordingly.
Hope this helps.
I'm new to cucumber, but enjoying it.
I'm currently writing some Frank tests, and would like to reuse blocks of cucumber script across multiple features - I'd like to do this a the cucumber level if possible (not inside the ruby).
For example, I might have 4 scripts that all start by doing the same login steps:
given my app has started
then enter "guest" in "user-field"
and enter "1234" in "password-field"
and press "login"
then I will see "welcome"
then *** here's the work specific to each script ***
Is there any way to share these first 5 lines across multiple scripts? Some kind of "include" syntax?
Generally there are 2 approaches:
Backgrounds
If you want a set of steps to run before each of the scenarios in a feature file:
Background:
given my app has started
then enter "guest" in "user-field"
and enter "1234" in "password-field"
and press "login"
then I will see "welcome"
Scenario: Some scenario
then *** here's the work specific to this scenario ***
Scenario: Some other scenario
then *** here's the work specific to this scenario ***
Calling steps from step definitions
If you need the 'block' of steps to be used in different feature files, or a Background section is not suitable because some scenarios don't need it, then create a high-level step definition which calls the other ones:
Given /^I have logged in$/ do
steps %Q {
given my app has started
then enter "guest" in "user-field"
and enter "1234" in "password-field"
and press "login"
then I will see "welcome"
}
end
Also, in this case I'd be tempted not to implement your common steps as separate steps at all, but to create a single step definition: (assuming Capybara)
Given /^I have logged in$/ do
fill_in 'user-field', :with => 'guest'
fill_in 'password-field', :with => '1234'
click_button 'login'
end
This lends a little bit more meaning to your step definitions, rather than creating a sequence of page interactions which need to be mentally parsed before you realise 'oh, this section is logging me in'.
A better approach is suggested to use ruby level "methods" to code reuse instead of nested steps from code maintenance and debugging perspective.
Here is the link to more detail:
Reuse Cucumber steps
Description
The following method proposes an alternative approach to one of the solutions described in Jon M's answer.
Namely, instead of calling nested steps inside step definitions, such common blocks of steps can be extracted into external .feature files which can be included into your feature file (in a manner of speaking).
How-to
1. Expose utility / helper methods to be able to run steps parsed from a .feature file
# features/support/env.rb
# expose Cucumber runtime
InstallPlugin do |_, registry|
runtime = registry.instance_variable_get('#registry').instance_variable_get('#runtime')
Cucumber.define_singleton_method(:runtime) { runtime }
end
# extend current World with methods to run dynamic (already parsed) steps
Before do
step_invoker = Cucumber::Runtime::SupportCode::StepInvoker.new(Cucumber.runtime.support_code)
define_singleton_method(:dynamic_steps) do |steps|
steps.each do |step|
dynamic_step(step)
end
end
define_singleton_method(:dynamic_step) do |step|
LOGGER.info("Running template step: #{step[:text]}")
step_invoker.step(step)
end
end
2. Create a template file which will contain the steps to be shared
# features/templates/my_profile.template.feature
#template
Feature: Steps to navigate to my_profile_page
Scenario: login_page
Given my app has started on "login_page"
And I enter "guest" in "user-field" on "login_page"
And I enter "1234" in "password-field" on "login_page"
And I press "login" on "login_page" and go to "welcome_page"
Scenario: welcome_page
Given that I am on "welcome_page"
And I click "my_profile_button" on "welcome_page" and go to "my_profile_page"
Scenario: my_profile_page
...
3. Create an utility module which will parse steps from a .feature file
# features/support/template_parser.rb
require 'gherkin/parser'
require 'gherkin/pickles/compiler'
module TemplateParser
class << self
def read_from_template(template_path, from: nil, till: nil)
pickles = load_template(template_path)
flow = construct_flow(pickles)
slice_flow(flow, from, till)
end
private
def load_template(template_path)
source = {
uri: template_path,
data: File.read(template_path),
mediaType: 'text/x.cucumber.gherkin+plain'
}
def source.uri
self[:uri]
end
gherkin_document = Gherkin::Parser.new.parse(source[:data])
id_generator = Cucumber::Messages::IdGenerator::UUID.new
Gherkin::Pickles::Compiler.new(id_generator).compile(gherkin_document, source)
end
def construct_flow(pickles)
pickles.to_h do |pickle|
[
pickle.name,
pickle.steps.map(&:to_h).map { |step| step[:argument] ? step.merge(step[:argument]) : step }
]
end
end
def slice_flow(flow, from, till)
raise NameError, "From step '#{from}' does not exist!" unless from.nil? || flow.keys.include?(from)
raise NameError, "Till step '#{till}' does not exist!" unless till.nil? || flow.keys.include?(till)
from_idx = from.nil? ? 0 : flow.keys.index(from)
till_idx = till.nil? ? -1 : flow.keys.index(till)
flow.slice(*flow.keys[from_idx...till_idx])
end
end
end
4. Create a step definition that will load this template and inject the specified steps dynamically at runtime
And('I complete the {string} template from the {string} until the {string}') do |template, from, till|
template_path = "features/templates/#{template}.template.feature"
flow = TemplateParser.read_from_template(
template_path,
from: from.empty? ? nil : from,
till: till.empty? ? nil : till
)
flow.each_value { |steps| dynamic_steps(steps) }
end
5. Use this step inside your main feature file, by declaring which blocks of steps to use
# features/tests/welcome.feature
Feature: User is welcomed
Scenario: Verify that user sees welcome text
Given I complete the 'my_profile' template from the 'login_page' until the 'my_profile_page'
Then I see 'welcome' on 'welcome_page'
6. Make sure you omit the #template .feature files from being run in your tests
$ bundle exec cucumber --tags ~#template
Limitations
Con:
This method exposes some internals of the private API of cucumber-ruby, which may change in future.
Con:
This is a non-standard way of sharing steps between feature files.
Helper methods are the preferred way to achieve this, as per FAQ.
Pro:
The common blocks of steps are syntax-highlighted, and have proper IntelliSense support in your editor of choice.
Pro:
You can encode entire "workflows" easily this way, allowing you to encode your workflow expectations in a DRY way.
Namely, you can reuse those workflow steps by completing the first part of a workflow, change a few things on a single page as per your test requirements, resume those workflow steps from the follow-up page, and add an appropriate verification at the end of the workflow that covers those test requirements.