├── doc
├── images
│ ├── info-block.png
│ ├── ditaa-support.png
│ ├── warning-block.png
│ └── jlatexmath-support.png
└── sample-yamex-report.pdf
├── tzatziki-core
└── src
│ ├── test
│ ├── resources
│ │ ├── test-settings.properties
│ │ ├── tzatziki
│ │ │ ├── analysis
│ │ │ │ ├── exec
│ │ │ │ │ ├── gson
│ │ │ │ │ │ ├── steps-w-embedding.feature
│ │ │ │ │ │ └── comments-n-description-interleaved.feature
│ │ │ │ │ └── feature
│ │ │ │ │ │ └── feature-w-outline.feature
│ │ │ │ └── step
│ │ │ │ │ ├── subdomain
│ │ │ │ │ ├── extra-hot.feature
│ │ │ │ │ └── running-out.feature
│ │ │ │ │ └── going-into-business.feature
│ │ │ └── junit
│ │ │ │ └── coffeemachine
│ │ │ │ ├── 03-extra-hot.feature
│ │ │ │ ├── 05-running-out.feature
│ │ │ │ ├── 01-making-drinks.feature
│ │ │ │ ├── 04-making-money.feature
│ │ │ │ └── 02-going-into-business.feature
│ │ └── logback-test.xml
│ └── java
│ │ └── tzatziki
│ │ ├── analysis
│ │ ├── exec
│ │ │ ├── tag
│ │ │ │ ├── TagFilterTest.java
│ │ │ │ └── TagViewTest.java
│ │ │ └── gson
│ │ │ │ └── JsonIOTest.java
│ │ └── java
│ │ │ ├── MethodEntryTest.java
│ │ │ └── stepdefs
│ │ │ └── OptionStepdefs.java
│ │ ├── util
│ │ ├── ExceptionUtilsTest.java
│ │ ├── PackagePathTest.java
│ │ ├── LoadJson.java
│ │ ├── MemoizableIteratorTest.java
│ │ └── JsonPath.java
│ │ ├── TestSettings.java
│ │ └── exploratory
│ │ └── cucumber
│ │ └── TagExpressionTest.java
│ └── main
│ └── java
│ └── tzatziki
│ ├── util
│ ├── Filter.java
│ ├── Consumer.java
│ ├── New.java
│ ├── Equal.java
│ ├── ExceptionUtils.java
│ ├── MemoizableIterator.java
│ ├── PackagePath.java
│ ├── PropertiesLoader.java
│ └── Filters.java
│ └── analysis
│ ├── check
│ ├── TagChecker.java
│ ├── CucumberPart.java
│ ├── CheckAtLeastOneTagsExist.java
│ └── CheckAllTagsExist.java
│ ├── exec
│ ├── model
│ │ ├── HasTags.java
│ │ ├── HasComments.java
│ │ ├── Status.java
│ │ ├── LineRange.java
│ │ ├── BackgroundExec.java
│ │ ├── ExamplesRow.java
│ │ ├── Embedded.java
│ │ ├── EmbeddingAndWriteContainer.java
│ │ ├── ScenarioRef.java
│ │ ├── MatchExec.java
│ │ ├── DataTable.java
│ │ ├── ScenarioExec.java
│ │ ├── ExamplesExec.java
│ │ └── StepContainer.java
│ ├── tag
│ │ ├── TagExpressionPredicate.java
│ │ ├── Tags.java
│ │ └── TagFilter.java
│ ├── gson
│ │ ├── ScenarioExecSerializer.java
│ │ ├── ScenarioOutlineExecSerializer.java
│ │ ├── JsonIO.java
│ │ ├── StepContainerDeserializer.java
│ │ └── JsonEmitterReport.java
│ └── ExecutionFilter.java
│ ├── java
│ ├── Describable.java
│ ├── KeywordBasedPattern.java
│ ├── GrammarVisitor.java
│ ├── Parameter.java
│ ├── GrammarParserListener.java
│ ├── GrammarParserStatisticsListener.java
│ ├── GrammarParserListenerAdapter.java
│ ├── UsedBy.java
│ ├── HumanReadableRegex.java
│ ├── ClassEntry.java
│ ├── ConsoleOutputListener.java
│ └── Grammar.java
│ ├── step
│ ├── Background.java
│ ├── FeatureVisitor.java
│ ├── FeatureVisitorAdapter.java
│ ├── Features.java
│ ├── Step.java
│ ├── Scenario.java
│ ├── ScenarioOutline.java
│ ├── TagCollector.java
│ ├── Feature.java
│ └── FeatureParser.java
│ ├── tag
│ ├── Tag.java
│ ├── TagDictionaryLoader.java
│ └── TagDictionary.java
│ └── GrammarConsolidation.java
├── tzatziki-pdf
└── src
│ ├── test
│ ├── resources
│ │ ├── test-settings.properties
│ │ ├── tzatziki
│ │ │ └── pdf
│ │ │ │ ├── images
│ │ │ │ ├── coins.jpeg
│ │ │ │ ├── customer.jpeg
│ │ │ │ ├── coffee-cup.jpeg
│ │ │ │ ├── coffee-800x700.png
│ │ │ │ └── flickr.com-photos-audcrane.png
│ │ │ │ ├── tags.properties
│ │ │ │ └── feature
│ │ │ │ └── 01-sample.feature
│ │ └── logback-test.xml
│ └── java
│ │ └── tzatziki
│ │ └── pdf
│ │ ├── feature
│ │ └── RunFeature.java
│ │ └── TestSettings.java
│ └── main
│ └── java
│ └── tzatziki
│ └── pdf
│ ├── support
│ └── StylesPostProcessor.java
│ ├── Settings.java
│ ├── model
│ ├── Tags.java
│ ├── Steps.java
│ └── ScenarioOutlineWithResolved.java
│ ├── Comments.java
│ ├── Margin.java
│ ├── emitter
│ ├── EmbeddedEmitter.java
│ ├── TagsEmitter.java
│ ├── DefaultPdfEmitters.java
│ ├── StatusMarker.java
│ ├── ScenarioEmitter.java
│ └── StepContainerEmitter.java
│ └── EmitterContext.java
├── tzatziki-samples
└── src
│ ├── test
│ ├── resources
│ │ ├── test-settings.properties
│ │ └── logback-test.xml
│ └── java
│ │ └── samples
│ │ ├── coffeemachine
│ │ ├── RunAllCucumberTest.java
│ │ ├── CoffeeMachineFeatureTest.java
│ │ ├── CoffeeMachineWipFeatureTest.java
│ │ ├── CoffeeMachineTagCheckTest.java
│ │ └── CoffeeMachineDrinkTagCheckTest.java
│ │ └── TestSettings.java
│ └── main
│ ├── resources
│ └── samples
│ │ ├── dinovet
│ │ ├── README.md
│ │ └── features
│ │ │ ├── view_patient_history.feature
│ │ │ └── manage_diagnosis_events.feature
│ │ ├── stylius
│ │ ├── README.md
│ │ ├── frontend
│ │ │ ├── homepage.feature
│ │ │ ├── user_login_via_oauth.feature
│ │ │ ├── account_homepage.feature
│ │ │ ├── currencies.feature
│ │ │ ├── cart_inclusive_tax.feature
│ │ │ ├── checkout_finalize.feature
│ │ │ ├── user_login.feature
│ │ │ ├── checkout_addressing.feature
│ │ │ ├── account_password.feature
│ │ │ ├── checkout_taxation.feature
│ │ │ ├── checkout_payment.feature
│ │ │ ├── products.feature
│ │ │ ├── cart_promotions_dates.feature
│ │ │ ├── cart_tax_categories.feature
│ │ │ ├── user_registration.feature
│ │ │ ├── cart_promotions_complex.feature
│ │ │ └── checkout_start.feature
│ │ └── backend
│ │ │ ├── taxation_settings.feature
│ │ │ ├── dashboard.feature
│ │ │ └── products_filter.feature
│ │ ├── coffeemachine
│ │ ├── images
│ │ │ ├── coins.jpeg
│ │ │ ├── customer.jpeg
│ │ │ ├── coffee-cup.jpeg
│ │ │ ├── coffee-800x700.png
│ │ │ └── flickr.com-photos-audcrane.png
│ │ ├── 00-meta.properties
│ │ ├── tags.properties
│ │ ├── 03-extra-hot.feature
│ │ ├── 05-running-out.feature
│ │ ├── 06-background.feature
│ │ ├── 01-making-drinks.feature
│ │ ├── 04-making-money.feature
│ │ └── 02-going-into-business.feature
│ │ ├── sample
│ │ ├── game-of-life.feature
│ │ └── countries.feature
│ │ └── game-of-life
│ │ ├── board.feature
│ │ └── game-of-life.feature
│ └── java
│ └── samples
│ └── coffeemachine
│ ├── DrinkMaker.java
│ ├── Hooks.java
│ ├── MoneySteps.java
│ ├── Context.java
│ ├── Gateway.java
│ └── TakeOrderSteps.java
├── tzatziki-web
├── src
│ └── main
│ │ └── java
│ │ └── tzatziki
│ │ └── web
│ │ ├── ScenarioDAO.java
│ │ ├── GrammarDAO.java
│ │ ├── ScenarioResource.java
│ │ ├── AppConfiguration.java
│ │ ├── GrammarDAOHealthCheck.java
│ │ ├── ScenarioDAOHealthCheck.java
│ │ ├── GrammarResource.java
│ │ └── App.java
└── conf
│ └── config.yml
├── .gitignore
├── release.md
└── LICENSE
/doc/images/info-block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/doc/images/info-block.png
--------------------------------------------------------------------------------
/doc/images/ditaa-support.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/doc/images/ditaa-support.png
--------------------------------------------------------------------------------
/doc/images/warning-block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/doc/images/warning-block.png
--------------------------------------------------------------------------------
/doc/sample-yamex-report.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/doc/sample-yamex-report.pdf
--------------------------------------------------------------------------------
/doc/images/jlatexmath-support.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/doc/images/jlatexmath-support.png
--------------------------------------------------------------------------------
/tzatziki-core/src/test/resources/test-settings.properties:
--------------------------------------------------------------------------------
1 | buildDir=${project.build.directory}
2 | baseDir=${project.basedir}
--------------------------------------------------------------------------------
/tzatziki-pdf/src/test/resources/test-settings.properties:
--------------------------------------------------------------------------------
1 | buildDir=${project.build.directory}
2 | baseDir=${project.basedir}
--------------------------------------------------------------------------------
/tzatziki-samples/src/test/resources/test-settings.properties:
--------------------------------------------------------------------------------
1 | buildDir=${project.build.directory}
2 | baseDir=${project.basedir}
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/dinovet/README.md:
--------------------------------------------------------------------------------
1 | Copied from [dinovet](https://github.com/dinocore/dinovet/tree/master/features)
2 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/README.md:
--------------------------------------------------------------------------------
1 | Copied from [Stylius-test](https://github.com/lensky84/sylius-test/tree/master/features)
2 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/test/resources/tzatziki/pdf/images/coins.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/tzatziki-pdf/src/test/resources/tzatziki/pdf/images/coins.jpeg
--------------------------------------------------------------------------------
/tzatziki-pdf/src/test/resources/tzatziki/pdf/images/customer.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/tzatziki-pdf/src/test/resources/tzatziki/pdf/images/customer.jpeg
--------------------------------------------------------------------------------
/tzatziki-pdf/src/test/resources/tzatziki/pdf/images/coffee-cup.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/tzatziki-pdf/src/test/resources/tzatziki/pdf/images/coffee-cup.jpeg
--------------------------------------------------------------------------------
/tzatziki-pdf/src/test/resources/tzatziki/pdf/images/coffee-800x700.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/tzatziki-pdf/src/test/resources/tzatziki/pdf/images/coffee-800x700.png
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/coffeemachine/images/coins.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/tzatziki-samples/src/main/resources/samples/coffeemachine/images/coins.jpeg
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/coffeemachine/images/customer.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/tzatziki-samples/src/main/resources/samples/coffeemachine/images/customer.jpeg
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/coffeemachine/images/coffee-cup.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/tzatziki-samples/src/main/resources/samples/coffeemachine/images/coffee-cup.jpeg
--------------------------------------------------------------------------------
/tzatziki-pdf/src/test/resources/tzatziki/pdf/images/flickr.com-photos-audcrane.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/tzatziki-pdf/src/test/resources/tzatziki/pdf/images/flickr.com-photos-audcrane.png
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/coffeemachine/images/coffee-800x700.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/tzatziki-samples/src/main/resources/samples/coffeemachine/images/coffee-800x700.png
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/util/Filter.java:
--------------------------------------------------------------------------------
1 | package tzatziki.util;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public interface Filter {
7 | T filter(T value);
8 | }
9 |
--------------------------------------------------------------------------------
/tzatziki-web/src/main/java/tzatziki/web/ScenarioDAO.java:
--------------------------------------------------------------------------------
1 | package tzatziki.web;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public interface ScenarioDAO {
7 | void check();
8 | }
9 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/util/Consumer.java:
--------------------------------------------------------------------------------
1 | package tzatziki.util;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public interface Consumer {
7 | void consume(T value);
8 | }
9 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/coffeemachine/00-meta.properties:
--------------------------------------------------------------------------------
1 | version=Version ${project.version}
2 | image-root-path=${project.basedir}/src/test/resources/sample/coffeemachine/images
3 | working-dir=${pom.build.directory}
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/coffeemachine/images/flickr.com-photos-audcrane.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Arnauld/tzatziki/HEAD/tzatziki-samples/src/main/resources/samples/coffeemachine/images/flickr.com-photos-audcrane.png
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/java/samples/coffeemachine/DrinkMaker.java:
--------------------------------------------------------------------------------
1 | package samples.coffeemachine;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public interface DrinkMaker {
7 | void executeCommand(String command);
8 | }
9 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/check/TagChecker.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.check;
2 |
3 | import java.util.List;
4 |
5 | import tzatziki.analysis.tag.TagDictionary;
6 |
7 | public interface TagChecker {
8 | void evaluate(TagDictionary dictionary, List tags);
9 | }
--------------------------------------------------------------------------------
/tzatziki-web/src/main/java/tzatziki/web/GrammarDAO.java:
--------------------------------------------------------------------------------
1 | package tzatziki.web;
2 |
3 | import tzatziki.analysis.java.Grammar;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public interface GrammarDAO {
9 | Grammar getGrammar();
10 | }
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 | *.pdf
4 | *.pdf.tmp
5 |
6 | .DS_Store
7 |
8 | # Package Files #
9 | *.jar
10 | *.war
11 | *.ear
12 | tmp/
13 | target/
14 | cucumber-contrib.iml
15 | .classpath
16 | .project
17 | .settings/
18 | .idea/
19 | *.iml
20 | .sass-cache/
21 | pom.xml.versionsBackup
22 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/check/CucumberPart.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.check;
2 |
3 | /**
4 | * Cucumber part to check
5 | * @author pverdage
6 | *
7 | */
8 | public enum CucumberPart {
9 | Feature,
10 | Scenario,
11 | ScenarioOutline;
12 |
13 | }
14 |
15 |
16 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/util/New.java:
--------------------------------------------------------------------------------
1 | package tzatziki.util;
2 |
3 | import java.util.Stack;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public class New {
9 | public static Stack newStack() {
10 | return new Stack();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/main/java/tzatziki/pdf/support/StylesPostProcessor.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf.support;
2 |
3 | import gutenberg.itext.Styles;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public interface StylesPostProcessor {
9 | void postProcess(Styles styles);
10 | }
11 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/HasTags.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | import com.google.common.collect.FluentIterable;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public interface HasTags {
9 | FluentIterable tags();
10 | }
11 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/java/samples/coffeemachine/Hooks.java:
--------------------------------------------------------------------------------
1 | package samples.coffeemachine;
2 |
3 | import cucumber.api.java.Before;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public class Hooks {
9 |
10 | @Before
11 | public void setUpMocks() {
12 |
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/HasComments.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | import com.google.common.collect.FluentIterable;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public interface HasComments {
9 | FluentIterable comments();
10 | }
11 |
--------------------------------------------------------------------------------
/tzatziki-web/src/main/java/tzatziki/web/ScenarioResource.java:
--------------------------------------------------------------------------------
1 | package tzatziki.web;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class ScenarioResource {
7 | private final ScenarioDAO scenarioDAO;
8 |
9 | public ScenarioResource(ScenarioDAO scenarioDAO) {
10 | this.scenarioDAO = scenarioDAO;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/main/java/tzatziki/pdf/Settings.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class Settings {
7 | public static final String PRIMARY_COLOR = "primary-color";
8 | public static final String EMPHASIZE_COLOR = "emphasize-color";
9 | public static final String META_FONT = "meta-font";
10 | }
11 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/frontend/homepage.feature:
--------------------------------------------------------------------------------
1 | @homepage
2 | Feature: Store homepage
3 | In order to access and browse the store
4 | As a visitor
5 | I want to be able to see the homepage
6 |
7 | Scenario: Viewing the homepage at website root
8 | When I go to the website root
9 | Then I should be on the homepage
10 | And I should see "Welcome to Sylius"
11 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/java/Describable.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class Describable {
7 | private String comment;
8 |
9 | public void describeWith(String comment) {
10 | this.comment = comment;
11 | }
12 |
13 | public String comment() {
14 | return comment;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/step/Background.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.step;
2 |
3 | import com.google.common.collect.Lists;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * @author @aloyer
9 | */
10 | public class Background {
11 | private List stepList = Lists.newArrayList();
12 |
13 | public void add(Step step) {
14 | stepList.add(step);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/util/Equal.java:
--------------------------------------------------------------------------------
1 | package tzatziki.util;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class Equal {
7 | public static boolean areEquals(Object strOne, Object strTwo) {
8 | if (strOne == null) {
9 | return strTwo == null;
10 | } else {
11 | return strTwo != null && strOne.equals(strTwo);
12 | }
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/dinovet/features/view_patient_history.feature:
--------------------------------------------------------------------------------
1 | Feature: View Patient History
2 | In order to safely treat patients
3 | An employee
4 | needs to be able to view a patient's medical history
5 |
6 | Scenario: View all patient history
7 | Given I have added a client and patient
8 | And I have made a diagnosis
9 | When I go to the patient events page
10 | Then I should see a list of events
11 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/java/samples/coffeemachine/MoneySteps.java:
--------------------------------------------------------------------------------
1 | package samples.coffeemachine;
2 |
3 | import cucumber.api.java.en.Given;
4 |
5 | import java.math.BigDecimal;
6 |
7 | /**
8 | * @author @aloyer
9 | */
10 | public class MoneySteps {
11 |
12 |
13 | @Given("^I've inserted (\\d+)€ in the machine$")
14 | public void I_ve_inserted_€_in_the_machine(BigDecimal amount) throws Throwable {
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/test/java/tzatziki/pdf/feature/RunFeature.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf.feature;
2 |
3 | import cucumber.api.CucumberOptions;
4 | import cucumber.api.junit.Cucumber;
5 | import org.junit.runner.RunWith;
6 |
7 | /**
8 | * @author @aloyer
9 | */
10 | @RunWith(Cucumber.class)
11 | @CucumberOptions(
12 | format = "tzatziki.analysis.exec.gson.JsonEmitterReport:target/tz-pdf")
13 | public class RunFeature {
14 | }
15 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/main/java/tzatziki/pdf/model/Tags.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf.model;
2 |
3 | import com.google.common.collect.FluentIterable;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public class Tags {
9 | private final FluentIterable tags;
10 |
11 | public Tags(FluentIterable tags) {
12 | this.tags = tags;
13 | }
14 |
15 | public FluentIterable tags() {
16 | return tags;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/test/java/samples/coffeemachine/RunAllCucumberTest.java:
--------------------------------------------------------------------------------
1 | package samples.coffeemachine;
2 |
3 | import cucumber.api.CucumberOptions;
4 | import cucumber.api.junit.Cucumber;
5 | import org.junit.runner.RunWith;
6 |
7 | /**
8 | * @author @aloyer
9 | */
10 | @RunWith(Cucumber.class)
11 | @CucumberOptions(
12 | format = "tzatziki.analysis.exec.gson.JsonEmitterReport:target/report/all"
13 | )
14 | public class RunAllCucumberTest {
15 | }
16 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/resources/tzatziki/analysis/exec/gson/steps-w-embedding.feature:
--------------------------------------------------------------------------------
1 | Feature: Embedding at the step level
2 |
3 | Scenario: Step should be able to emit embedding
4 |
5 | Given a series of 10 values named 'xs'
6 | When I apply the following formula:
7 | """
8 | result = xs.reduce(0, (x, sum) -> sum + x) / xs.size()
9 | """
10 | Then the result should be greater or equal to the lowest value of 'xs'
11 | And the result should be lower or equal to the highest value of 'xs'
--------------------------------------------------------------------------------
/tzatziki-pdf/src/main/java/tzatziki/pdf/model/Steps.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf.model;
2 |
3 | import tzatziki.analysis.exec.model.StepExec;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public class Steps {
9 | private final Iterable extends StepExec> steps;
10 |
11 | public Steps(Iterable extends StepExec> steps) {
12 | this.steps = steps;
13 | }
14 |
15 | public Iterable extends StepExec> steps() {
16 | return steps;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/test/resources/tzatziki/pdf/tags.properties:
--------------------------------------------------------------------------------
1 | wip=Work in progress
2 |
3 | takeOrder=Take order
4 | protocol=Command serialization
5 | payment=Payment
6 |
7 | notEnoughMoney=Not enough money
8 | tooMuchMoney=Too much money
9 |
10 |
11 | coffee=Coffee
12 | tea=Tea
13 | chocolate=Chocolate
14 | orangeJuice=Orange Juice
15 |
16 | sugar=Sugar
17 | noSugar=No sugar
18 | extraHot=Xtra Hot
19 |
20 | message=Message
21 | reporting=Reporting
22 |
23 | notification=Notification
24 | runningOut=Running out some drink
25 |
26 |
27 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/Status.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public enum Status {
7 | Passed,
8 | Skipped,
9 | Undefined,
10 | Failed,
11 | Pending;
12 |
13 | public static Status fromString(String status) {
14 | for (Status s : values()) {
15 | if(s.name().equalsIgnoreCase(status))
16 | return s;
17 | }
18 | return null;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/resources/tzatziki/analysis/exec/gson/comments-n-description-interleaved.feature:
--------------------------------------------------------------------------------
1 | Feature: Comments and description interleaved
2 |
3 | # Feature description (within comments)
4 |
5 | Scenario: First Scenario
6 |
7 | # This is the first scenario
8 |
9 | Given a series of 10 values named 'xs'
10 |
11 | # This is the end of the first scenario
12 |
13 | Scenario: Second Scenario
14 |
15 | # This is the second scenario
16 |
17 | Given a series of 10 values named 'xs'
18 |
19 | # This is the end of the second scenario
20 |
21 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/test/java/samples/coffeemachine/CoffeeMachineFeatureTest.java:
--------------------------------------------------------------------------------
1 | package samples.coffeemachine;
2 |
3 | import cucumber.api.CucumberOptions;
4 | import cucumber.api.junit.Cucumber;
5 | import org.junit.AfterClass;
6 | import org.junit.runner.RunWith;
7 |
8 | /**
9 | * @author @aloyer
10 | */
11 | @RunWith(Cucumber.class)
12 | @CucumberOptions(
13 | format = "tzatziki.analysis.exec.gson.JsonEmitterReport:target/samples/coffeemachine"
14 | )
15 | public class CoffeeMachineFeatureTest {
16 | }
17 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/LineRange.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class LineRange {
7 | public final Integer first;
8 | public final Integer last;
9 |
10 | public LineRange(Integer first, Integer last) {
11 | this.first = first;
12 | this.last = last;
13 | }
14 |
15 | @Override
16 | public String toString() {
17 | return "LineRange[" + first + ", " + last + ']';
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/test/java/samples/coffeemachine/CoffeeMachineWipFeatureTest.java:
--------------------------------------------------------------------------------
1 | package samples.coffeemachine;
2 |
3 | import cucumber.api.CucumberOptions;
4 | import cucumber.api.junit.Cucumber;
5 | import org.junit.runner.RunWith;
6 |
7 | /**
8 | * @author @aloyer
9 | */
10 | @RunWith(Cucumber.class)
11 | @CucumberOptions(
12 | tags = {"@wip"},
13 | format = "tzatziki.analysis.exec.gson.JsonEmitterReport:target/samples/coffeemachine/wip"
14 | )
15 | public class CoffeeMachineWipFeatureTest {
16 | }
17 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/resources/tzatziki/analysis/exec/feature/feature-w-outline.feature:
--------------------------------------------------------------------------------
1 | Feature: A feature with an Outline
2 |
3 | @wip
4 | Scenario Outline: Template scenario
5 |
6 | Given a simple step
7 | When I activate the
8 | Then I should have a log that indicate the activation of the
9 |
10 | @activated
11 | Examples: Activated Features
12 | | behavior |
13 | | print |
14 | | login |
15 |
16 | @deactivated
17 | Examples: Deactivated Features
18 | | behavior |
19 | | delete |
20 | | clone |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/step/FeatureVisitor.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.step;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public interface FeatureVisitor {
7 | void enterFeature(Feature feature);
8 | void exitFeature(Feature feature);
9 |
10 | void enterScenario(Scenario scenario);
11 | void exitScenario(Scenario scenario);
12 |
13 | void enterScenarioOutline(ScenarioOutline scenario);
14 | void exitScenarioOutline(ScenarioOutline scenario);
15 |
16 | void visitStep(Step step);
17 | }
18 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/coffeemachine/tags.properties:
--------------------------------------------------------------------------------
1 | wip=Work in progress
2 | wip1=
3 | wip2=
4 |
5 | unusedTag=
6 |
7 | takeOrder=Take order
8 | protocol=Command serialization
9 | payment=Payment
10 |
11 | notEnoughMoney=Not enough money
12 | tooMuchMoney=Too much money
13 |
14 |
15 | coffee=Coffee
16 | tea=Tea
17 | chocolate=Chocolate
18 | orangeJuice=Orange Juice
19 |
20 | sugar=Sugar
21 | noSugar=No sugar
22 | extraHot=Xtra Hot
23 |
24 | message=Message
25 | reporting=Reporting
26 |
27 | notification=Notification
28 | runningOut=Running out some drink
29 |
30 |
31 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/java/KeywordBasedPattern.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class KeywordBasedPattern {
7 | private final String keyword;
8 | private final String pattern;
9 |
10 | public KeywordBasedPattern(String keyword, String pattern) {
11 | this.keyword = keyword;
12 | this.pattern = pattern;
13 | }
14 |
15 | public String getKeyword() {
16 | return keyword;
17 | }
18 |
19 | public String getPattern() {
20 | return pattern;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/util/ExceptionUtils.java:
--------------------------------------------------------------------------------
1 | package tzatziki.util;
2 |
3 | import java.io.PrintWriter;
4 | import java.io.StringWriter;
5 |
6 | /**
7 | * @author @aloyer
8 | */
9 | public class ExceptionUtils {
10 | public static String toString(Throwable error) {
11 | if (error == null)
12 | return null;
13 |
14 | StringWriter stringWriter = new StringWriter();
15 | PrintWriter printWriter = new PrintWriter(stringWriter);
16 | error.printStackTrace(printWriter);
17 | return stringWriter.toString();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/frontend/user_login_via_oauth.feature:
--------------------------------------------------------------------------------
1 | @users @oauth
2 | Feature: Sign in to the store via OAuth
3 | In order to view my orders list
4 | As a visitor with an OAuth account
5 | I need to be able to log in to the store
6 |
7 | Background:
8 | Given I am not logged in
9 | And I am on the store homepage
10 |
11 | Scenario Outline: Get to the OAuth login page
12 | When I follow "Login"
13 | Then I should see the connect with "" button
14 |
15 | Examples:
16 | | oauth |
17 | | Amazon |
18 | | Facebook |
19 | | Google |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/java/GrammarVisitor.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class GrammarVisitor {
7 | public void enter(PackageEntry packageEntry, ClassEntry classEntry) {
8 | }
9 |
10 | public void leave(PackageEntry packageEntry, ClassEntry classEntry) {
11 | }
12 |
13 | public void visit(PackageEntry packageEntry, ClassEntry classEntry, MethodEntry methodEntry) {
14 | }
15 |
16 | public void enter(PackageEntry packageEntry) {
17 | }
18 |
19 | public void leave(PackageEntry packageEntry) {
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/tag/Tag.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.tag;
2 |
3 |
4 | /**
5 | * @author @aloyer
6 | */
7 | public class Tag {
8 | private final String tag;
9 | private String description;
10 |
11 | public Tag(String tag) {
12 | this.tag = tag;
13 | }
14 |
15 | public String getTag() {
16 | return tag;
17 | }
18 |
19 |
20 | public Tag declareDescription(String description) {
21 | this.description = description;
22 | return this;
23 | }
24 |
25 | public String getDescription() {
26 | return description;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/sample/game-of-life.feature:
--------------------------------------------------------------------------------
1 | @noui
2 | Feature: The game of life should...
3 | 1. Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
4 | 2. Any live cell with more than three live neighbours dies, as if by overcrowding.
5 | 3. Any live cell with two or three live neighbours lives on to the next generation.
6 | 4. Any dead cell with exactly three live neighbours becomes a live cell.
7 |
8 | @underpopulation
9 | Scenario: Cell has no neighbors
10 |
11 | Given Cell is alive
12 | And Cell has "0" neighbors
13 | When I go to the next generation
14 | Then Cell should be dead
15 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/main/java/tzatziki/pdf/Comments.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf;
2 |
3 | import java.util.regex.Pattern;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public class Comments {
9 | public static final String NL = "\n";
10 | private static final String COMMENT = "#";
11 | private static final Pattern COMMENT_PATTERN = Pattern.compile("^\\s*" + COMMENT);
12 |
13 | public static String discardCommentChar(String value) {
14 | return COMMENT_PATTERN.matcher(value).replaceAll("");
15 | }
16 |
17 | public static boolean startsWithComment(String text) {
18 | return text.startsWith(COMMENT);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/java/samples/coffeemachine/Context.java:
--------------------------------------------------------------------------------
1 | package samples.coffeemachine;
2 |
3 | import org.mockito.Mockito;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public class Context {
9 |
10 | private DrinkMaker drinkMaker;
11 | private Gateway gateway;
12 |
13 | public Context() {
14 | drinkMaker = Mockito.mock(DrinkMaker.class);
15 | }
16 |
17 | public DrinkMaker getDrinkMaker() {
18 | return drinkMaker;
19 | }
20 |
21 | public Gateway getGateway() {
22 | if (gateway == null)
23 | gateway = new Gateway(drinkMaker);
24 | return gateway;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/tzatziki-web/src/main/java/tzatziki/web/AppConfiguration.java:
--------------------------------------------------------------------------------
1 | package tzatziki.web;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import io.dropwizard.Configuration;
5 | import io.dropwizard.db.DataSourceFactory;
6 |
7 | import javax.validation.Valid;
8 | import javax.validation.constraints.NotNull;
9 |
10 | /**
11 | * @author @aloyer
12 | */
13 | public class AppConfiguration extends Configuration {
14 | @Valid
15 | @NotNull
16 | @JsonProperty
17 | private DataSourceFactory database = new DataSourceFactory();
18 |
19 | public DataSourceFactory getDataSourceFactory() {
20 | return database;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tzatziki-web/src/main/java/tzatziki/web/GrammarDAOHealthCheck.java:
--------------------------------------------------------------------------------
1 | package tzatziki.web;
2 |
3 | import com.codahale.metrics.health.HealthCheck;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public class GrammarDAOHealthCheck extends HealthCheck {
9 | private final GrammarDAO grammarDAO;
10 |
11 | public GrammarDAOHealthCheck(GrammarDAO grammarDAO) {
12 | this.grammarDAO = grammarDAO;
13 | }
14 |
15 | @Override
16 | protected Result check() throws Exception {
17 | if (grammarDAO.getGrammar() == null)
18 | return Result.unhealthy("No grammar available");
19 | return Result.healthy();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/frontend/account_homepage.feature:
--------------------------------------------------------------------------------
1 | @account
2 | Feature: User account homepage
3 | In order to access and manage my personal information
4 | As a logged user
5 | I want to be able to see my account homepage
6 |
7 | Scenario: Displaying the my account section only to logged users
8 | Given I am on the store homepage
9 | Then I should not see "My account"
10 |
11 | Scenario: Viewing the homepage of my account
12 | Given I am on the store homepage
13 | And I am logged in user
14 | When I follow "My account"
15 | Then I should be on my account homepage
16 | And I should see "Welcome to your space"
17 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/java/Parameter.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class Parameter {
7 | private final int index;
8 | private final String name;
9 | private final String parameterDoc;
10 |
11 | public Parameter(int index, String name, String parameterDoc) {
12 | this.index = index;
13 | this.name = name;
14 | this.parameterDoc = parameterDoc;
15 | }
16 |
17 | public int getIndex() {
18 | return index;
19 | }
20 |
21 | public String getName() {
22 | return name;
23 | }
24 |
25 | public String getDoc() {
26 | return parameterDoc;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tzatziki-web/src/main/java/tzatziki/web/ScenarioDAOHealthCheck.java:
--------------------------------------------------------------------------------
1 | package tzatziki.web;
2 |
3 | import com.codahale.metrics.health.HealthCheck;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public class ScenarioDAOHealthCheck extends HealthCheck {
9 | private final ScenarioDAO scenarioDAO;
10 |
11 | public ScenarioDAOHealthCheck(ScenarioDAO scenarioDAO) {
12 | this.scenarioDAO = scenarioDAO;
13 | }
14 |
15 |
16 | @Override
17 | protected Result check() throws Exception {
18 | try {
19 | scenarioDAO.check();
20 | return Result.healthy();
21 | } catch (Exception e) {
22 | return Result.unhealthy(e.getMessage());
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tzatziki-web/conf/config.yml:
--------------------------------------------------------------------------------
1 | # Database settings.
2 | database:
3 |
4 | # the name of your JDBC driver
5 | driverClass: org.h2.Driver
6 |
7 | # the username
8 | user: sa
9 |
10 | # the password
11 | password: sa
12 |
13 | # the JDBC URL
14 | url: jdbc:h2:./target/example
15 |
16 | server:
17 | type: simple
18 | applicationContextPath: /api/* # Default value*
19 |
20 | # Logging settings.
21 | logging:
22 |
23 | # The default level of all loggers. Can be OFF, ERROR, WARN, INFO, DEBUG, TRACE, or ALL.
24 | level: INFO
25 |
26 | # Logger-specific levels.
27 | loggers:
28 |
29 | # Sets the level for 'com.example.app' to DEBUG.
30 | com.example.app: DEBUG
31 |
32 | org.hibernate.SQL: ALL
33 |
34 | appenders:
35 | - type: console
--------------------------------------------------------------------------------
/tzatziki-web/src/main/java/tzatziki/web/GrammarResource.java:
--------------------------------------------------------------------------------
1 | package tzatziki.web;
2 |
3 | import com.codahale.metrics.annotation.Timed;
4 | import tzatziki.analysis.java.Grammar;
5 |
6 | import javax.ws.rs.GET;
7 | import javax.ws.rs.Path;
8 | import javax.ws.rs.Produces;
9 | import javax.ws.rs.core.MediaType;
10 |
11 | /**
12 | * @author @aloyer
13 | */
14 | @Path("/grammar")
15 | @Produces(MediaType.APPLICATION_JSON)
16 | public class GrammarResource {
17 |
18 | private final GrammarDAO grammarDAO;
19 |
20 | public GrammarResource(GrammarDAO grammarDAO) {
21 | this.grammarDAO = grammarDAO;
22 | }
23 |
24 | @GET
25 | @Timed
26 | public Grammar grammar() {
27 | return grammarDAO.getGrammar();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/java/tzatziki/analysis/exec/tag/TagFilterTest.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.tag;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.assertj.core.api.Assertions.assertThat;
6 |
7 | public class TagFilterTest {
8 |
9 |
10 | @Test
11 | public void simple_tag_filtering() {
12 | TagFilter tagFilter = TagFilter.from("~@wip", "~@default", "@specs");
13 |
14 | assertThat(tagFilter.apply(Tags.from("@wip"))).isFalse();
15 | assertThat(tagFilter.apply(Tags.from("@wip", "@option"))).isFalse();
16 | assertThat(tagFilter.apply(Tags.from("@defaults"))).isFalse();
17 | assertThat(tagFilter.apply(Tags.from("@defaults", "@option"))).isFalse();
18 | assertThat(tagFilter.apply(Tags.from("@defaults", "@specs"))).isTrue();
19 | }
20 | }
--------------------------------------------------------------------------------
/tzatziki-core/src/test/java/tzatziki/util/ExceptionUtilsTest.java:
--------------------------------------------------------------------------------
1 | package tzatziki.util;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.assertj.core.api.Assertions.assertThat;
6 |
7 | public class ExceptionUtilsTest {
8 |
9 | @Test
10 | public void toString_should_return_null__when_null_is_provided() {
11 | assertThat(ExceptionUtils.toString(null)).isNull();
12 | }
13 |
14 | @Test
15 | public void toString_should_return_the_stacktrace() {
16 | Exception ex = new Exception("Erf");
17 | String str = ExceptionUtils.toString(ex);
18 | assertThat(str).startsWith("" +
19 | "java.lang.Exception: Erf\n" +
20 | "\tat tzatziki.util.ExceptionUtilsTest.toString_should_return_the_stacktrace(ExceptionUtilsTest.java:");
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/BackgroundExec.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class BackgroundExec extends StepContainer {
7 |
8 | private final String keyword;
9 | private final String name;
10 |
11 | public BackgroundExec(String keyword, String name) {
12 | this.keyword = keyword;
13 | this.name = name;
14 | }
15 |
16 | public String keyword() {
17 | return keyword;
18 | }
19 |
20 | public String name() {
21 | return name;
22 | }
23 |
24 | public BackgroundExec recursiveCopy() {
25 | BackgroundExec copy = new BackgroundExec(keyword, name);
26 | recursiveCopy(copy);
27 | return copy;
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/check/CheckAtLeastOneTagsExist.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.check;
2 |
3 | import java.util.List;
4 |
5 | import org.junit.Assert;
6 |
7 | import tzatziki.analysis.tag.TagDictionary;
8 |
9 | /**
10 | * Check that at least on declared tag exist in the TagDictionary.
11 | * It can be used to check that there is no orphan test regarding a categorization.
12 | *
13 | * @author pverdage
14 | *
15 | */
16 | public class CheckAtLeastOneTagsExist implements TagChecker {
17 |
18 | @Override
19 | public void evaluate(TagDictionary dictionary, List tags) {
20 | for (String tag : tags) {
21 | if (dictionary.containsTag(tag))
22 | return;
23 | }
24 | Assert.fail("No tag(s) in dictionary amongst: " + tags);
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/tzatziki-core/src/test/java/tzatziki/analysis/java/MethodEntryTest.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.Arrays;
6 |
7 | import static org.assertj.core.api.Assertions.assertThat;
8 |
9 | public class MethodEntryTest {
10 |
11 | @Test
12 | public void should_return_patterns() {
13 | MethodEntry entry = new MethodEntry("bookADeal", Arrays.asList("java.lang.String"));
14 | entry.declarePattern("Given", "^a standard deal (.*) with no specifics");
15 | entry.declarePattern("Given", "^I have booked a standard deal (.*) with no specifics");
16 |
17 | assertThat(entry.patterns().toList()).containsExactly(
18 | "^a standard deal (.*) with no specifics",
19 | "^I have booked a standard deal (.*) with no specifics");
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/check/CheckAllTagsExist.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.check;
2 |
3 | import java.util.List;
4 |
5 | import org.junit.Assert;
6 |
7 | import com.google.common.collect.Lists;
8 |
9 | import tzatziki.analysis.tag.TagDictionary;
10 |
11 | /**
12 | * Check that all tags are declared in the TagDictionary
13 | * @author pverdage
14 | *
15 | */
16 | public class CheckAllTagsExist implements TagChecker {
17 |
18 | @Override
19 | public void evaluate(TagDictionary dictionary, List tags) {
20 | List unknown = Lists.newArrayList();
21 | for (String tag : tags) {
22 | if (!dictionary.containsTag(tag))
23 | unknown.add(tag);
24 | }
25 | if (!unknown.isEmpty())
26 | Assert.fail("Unknown tag(s): " + unknown);
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/sample/countries.feature:
--------------------------------------------------------------------------------
1 | @addressing @select
2 | Feature: Countries and provinces
3 | In order to create tax and shipping zones
4 | As a store owner
5 | I want to be able to manage countries and their provinces
6 |
7 | Background:
8 | Given I am logged in as administrator
9 | And there are following countries:
10 | | name | provinces |
11 | | France | Lyon, Toulouse, Rennes, Nancy |
12 | | China | |
13 | | Ukraine | Kiev, Odessa, Cherkasy, Kharkiv |
14 |
15 | Scenario: Seeing index of all countries
16 | Given I am on the dashboard page
17 | When I follow "Countries"
18 | Then I should be on the country index page
19 | And I should see 3 countries in the list
20 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/java/GrammarParserListener.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java;
2 |
3 | import com.thoughtworks.qdox.model.JavaClass;
4 | import com.thoughtworks.qdox.model.JavaMethod;
5 | import com.thoughtworks.qdox.model.JavaPackage;
6 |
7 | import java.util.Collection;
8 |
9 | /**
10 | * @author @aloyer
11 | */
12 | public interface GrammarParserListener {
13 | void aboutToParsePackages(Collection packages);
14 |
15 | void aboutToParseClasses(Collection classes);
16 |
17 | void enteringPackage(JavaPackage pkg);
18 |
19 | void exitingPackage(JavaPackage pkg);
20 |
21 | void enteringClass(JavaClass klazz);
22 |
23 | void exitingClass(JavaClass klazz);
24 |
25 | void enteringMethod(JavaMethod method);
26 |
27 | void exitingMethod(JavaMethod method);
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/step/FeatureVisitorAdapter.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.step;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class FeatureVisitorAdapter implements FeatureVisitor {
7 |
8 | @Override
9 | public void enterFeature(Feature feature) {
10 | }
11 |
12 | @Override
13 | public void exitFeature(Feature feature) {
14 | }
15 |
16 | @Override
17 | public void enterScenario(Scenario scenario) {
18 | }
19 |
20 | @Override
21 | public void exitScenario(Scenario scenario) {
22 | }
23 |
24 | @Override
25 | public void enterScenarioOutline(ScenarioOutline scenario) {
26 | }
27 |
28 | @Override
29 | public void exitScenarioOutline(ScenarioOutline scenario) {
30 | }
31 |
32 | @Override
33 | public void visitStep(Step step) {
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/ExamplesRow.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | import com.google.common.collect.FluentIterable;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * @author @aloyer
9 | */
10 | public class ExamplesRow {
11 | private final List comments;
12 | private final List cells;
13 | private final Integer line;
14 |
15 | public ExamplesRow(List comments, List cells, Integer line) {
16 | this.comments = comments;
17 | this.cells = cells;
18 | this.line = line;
19 | }
20 |
21 | public FluentIterable comments() {
22 | return FluentIterable.from(comments);
23 | }
24 |
25 | public FluentIterable cells() {
26 | return FluentIterable.from(cells);
27 | }
28 |
29 | public Integer getLine() {
30 | return line;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/Embedded.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class Embedded {
7 | private final String mimeType;
8 | private final byte[] data;
9 | private final String text;
10 |
11 | public Embedded(String mimeType, byte[] data) {
12 | this.mimeType = mimeType;
13 | this.data = data;
14 | this.text = null;
15 | }
16 |
17 | public Embedded(String text) {
18 | this.mimeType = "plain/text";
19 | this.data = null;
20 | this.text = text;
21 | }
22 |
23 | public boolean isText() {
24 | return text != null;
25 | }
26 |
27 | public String mimeType() {
28 | return mimeType;
29 | }
30 |
31 | public String text() {
32 | return text;
33 | }
34 |
35 | public byte[] data() {
36 | return data;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/EmbeddingAndWriteContainer.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | import com.google.common.collect.FluentIterable;
4 | import com.google.common.collect.Lists;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * @author @aloyer
10 | */
11 | public class EmbeddingAndWriteContainer {
12 | private List embeddedList = Lists.newArrayList();
13 |
14 | public void embedding(String mimeType, byte[] data) {
15 | embeddedList.add(new Embedded(mimeType, data));
16 | }
17 |
18 | public void text(String text) {
19 | embeddedList.add(new Embedded(text));
20 | }
21 |
22 | protected void recursiveCopy(EmbeddingAndWriteContainer copy) {
23 | copy.embeddedList.addAll(embeddedList);
24 | }
25 |
26 | public FluentIterable embeddeds() {
27 | return FluentIterable.from(embeddedList);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/step/Features.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.step;
2 |
3 | import com.google.common.collect.FluentIterable;
4 | import com.google.common.collect.Lists;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * @author @aloyer
10 | */
11 | public class Features {
12 | private final List featureList = Lists.newArrayList();
13 |
14 | public Features() {
15 | }
16 |
17 | public void add(Feature feature) {
18 | featureList.add(feature);
19 | }
20 |
21 | public void traverse(FeatureVisitor visitor) {
22 | for (Feature feature : featureList) {
23 | feature.traverse(visitor);
24 | }
25 | }
26 |
27 | public FluentIterable features() {
28 | return FluentIterable.from(featureList);
29 | }
30 |
31 | @Override
32 | public String toString() {
33 | return "Features{" + featureList + '}';
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/test/resources/tzatziki/pdf/feature/01-sample.feature:
--------------------------------------------------------------------------------
1 | Feature: A really basic feature
2 |
3 | Scenario Outline: A template scenario
4 |
5 | Given a element of
6 | When I shake it
7 | Then it should be
8 |
9 | Examples: Thinks
10 | | type | shaked-or-not |
11 | | car | shaked |
12 | | building | not shaked |
13 |
14 | Examples: Animals
15 | | type | shaked-or-not |
16 | | ant | shaked |
17 | | bee | shaked |
18 |
19 |
20 | Scenario Outline: An other template scenario
21 |
22 | Given a element of
23 | When I shake it
24 | Then it should be
25 |
26 | Examples: Thinks
27 | | type | shaked-or-not |
28 | | car | shaked |
29 | | building | not shaked |
30 |
31 | Examples: Animals
32 | | type | shaked-or-not |
33 | | ant | shaked |
34 | | bee | shaked |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/main/java/tzatziki/pdf/Margin.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class Margin {
7 |
8 | public static Margin create(float margin) {
9 | return new Margin(margin);
10 | }
11 |
12 | public final float marginLeft;
13 | public final float marginRight;
14 | public final float marginTop;
15 | public final float marginBottom;
16 |
17 | public Margin(float margin) {
18 | this(margin, margin, margin, margin);
19 | }
20 |
21 | public Margin(float marginLeftRight, float marginTopBottom) {
22 | this(marginLeftRight, marginLeftRight, marginTopBottom, marginTopBottom);
23 | }
24 |
25 | public Margin(float marginLeft, float marginRight, float marginTop, float marginBottom) {
26 | this.marginLeft = marginLeft;
27 | this.marginRight = marginRight;
28 | this.marginTop = marginTop;
29 | this.marginBottom = marginBottom;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/step/Step.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.step;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class Step {
7 | private final String keyword;
8 | private final String text;
9 | private int grammarMatchCount;
10 |
11 | public Step(String keyword, String text) {
12 | this.keyword = keyword.trim();
13 | this.text = text;
14 | }
15 |
16 | public String getKeyword() {
17 | return keyword;
18 | }
19 |
20 | public String getText() {
21 | return text;
22 | }
23 |
24 | public void traverse(FeatureVisitor visitor) {
25 | visitor.visitStep(this);
26 | }
27 |
28 | public void grammarMatchCount(int nb) {
29 | grammarMatchCount = nb;
30 | }
31 |
32 | @Override
33 | public String toString() {
34 | return "Step{" +
35 | "@" + keyword + '(' + text + ')' +
36 | ", matchCount=" + grammarMatchCount +
37 | '}';
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/resources/tzatziki/analysis/step/subdomain/extra-hot.feature:
--------------------------------------------------------------------------------
1 | Feature: Extra Hot and Orange Juice
2 |
3 | # **In order to** provide more choice and to attract more customer
4 | #
5 | # **As a** shopkeeper
6 | #
7 | # **I want to** be able to make orange juice and to deliver extra hot drinks
8 |
9 | @ProtocolOrder
10 | Scenario: An extra hot tea with 1 sugar
11 |
12 | When I order an extra hot "Tea" with 1 sugar
13 | Then the instruction generated should be "Th:1:0"
14 |
15 | @ProtocolOrder
16 | Scenario: An Orange juice
17 |
18 | When I order an "Orange Juice"
19 | Then the instruction generated should be "O::"
20 |
21 | @ProtocolOrder
22 | @wip
23 | Scenario: Extra sugar with Orange Juice is ignored
24 |
25 | When I order an "Orange Juice" with 1 sugar
26 | Then the instruction generated should be "O::"
27 |
28 | @ProtocolOrder
29 | Scenario: Extra hot with Orange Juice is ignored
30 |
31 | When I order an extra hot "Orange Juice" with 1 sugar
32 | Then the instruction generated should be "O::"
33 |
--------------------------------------------------------------------------------
/release.md:
--------------------------------------------------------------------------------
1 | Release
2 | -------
3 |
4 | ## Jdk6 Compilation
5 |
6 | ```
7 | $ docker pull jamesdbloom/docker-java6-maven
8 | $ docker run --rm -v $(pwd)/:/wk -w /wk -it jamesdbloom/docker-java6-maven /bin/bash
9 | [ root@4291b084fd0c:/local/git ]$ export LC_ALL="C.UTF-8"
10 | [ root@4291b084fd0c:/local/git ]$ export LANG="$LC_ALL"
11 | [ root@4291b084fd0c:/local/git ]$ git clone https://github.com/Arnauld/tzatziki.git
12 | [ root@4291b084fd0c:/local/git ]$ cd tzatziki && mvn clean test
13 | ```
14 |
15 | ## Release
16 |
17 | First check for **snapshot** dependencies:
18 |
19 | ```bash
20 | fgrep -i SNAPSHOT **/pom.xml
21 | ```
22 |
23 | [Maven Release Plugin: The Final Nail in the Coffin](http://axelfontaine.com/blog/final-nail.html)
24 |
25 | ```bash
26 | mvn versions:set -DnewVersion=1.0.1
27 | mvn clean deploy scm:tag -Psign-artifacts
28 | git status
29 | git add .
30 | git commit -m "gutenberg 1.0.1"
31 | mvn versions:set -DnewVersion=1.0.2-SNAPSHOT
32 | git add .
33 | git commit -m "gutenberg 1.0.2-snapshot"
34 | git push
35 | ```
36 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/main/java/tzatziki/pdf/model/ScenarioOutlineWithResolved.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf.model;
2 |
3 | import com.google.common.collect.FluentIterable;
4 | import tzatziki.analysis.exec.model.ScenarioExec;
5 | import tzatziki.analysis.exec.model.ScenarioOutlineExec;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * @author @aloyer
12 | */
13 | public class ScenarioOutlineWithResolved {
14 | private final ScenarioOutlineExec outlineExec;
15 | private final List resolvedExec = new ArrayList();
16 |
17 | public ScenarioOutlineWithResolved(ScenarioOutlineExec outlineExec) {
18 | this.outlineExec = outlineExec;
19 | }
20 |
21 | public void declareScenario(ScenarioExec scenario) {
22 | resolvedExec.add(scenario);
23 | }
24 |
25 | public ScenarioOutlineExec outline() {
26 | return outlineExec;
27 | }
28 |
29 | public FluentIterable resolved() {
30 | return FluentIterable.from(resolvedExec);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/tag/TagExpressionPredicate.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.tag;
2 |
3 | import com.google.common.base.Predicate;
4 | import com.google.common.collect.Lists;
5 | import gherkin.TagExpression;
6 | import gherkin.formatter.model.Tag;
7 |
8 | import java.util.Collection;
9 | import java.util.List;
10 |
11 | /**
12 | * @author @aloyer
13 | */
14 | public class TagExpressionPredicate implements Predicate {
15 | private final TagExpression tagExpression;
16 |
17 | public TagExpressionPredicate(List tagExprs) {
18 | this.tagExpression = new TagExpression(tagExprs);
19 | }
20 |
21 | @Override
22 | public boolean apply(Tags input) {
23 | return tagExpression.evaluate(toGherkinTags(input.toList()));
24 | }
25 |
26 | private static Collection toGherkinTags(List strings) {
27 | List tags = Lists.newArrayList();
28 | for (String str : strings) {
29 | tags.add(new Tag(str, -1));
30 | }
31 | return tags;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/tag/Tags.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.tag;
2 |
3 | import com.google.common.collect.Sets;
4 |
5 | import java.util.ArrayList;
6 | import java.util.Arrays;
7 | import java.util.Collection;
8 | import java.util.List;
9 | import java.util.Set;
10 |
11 | /**
12 | * @author @aloyer
13 | */
14 | public class Tags {
15 |
16 | public static Tags from(String... tags) {
17 | return new Tags(Arrays.asList(tags));
18 | }
19 |
20 | public static Tags from(Collection tags) {
21 | return new Tags(tags);
22 | }
23 |
24 | private final Collection tags;
25 |
26 | public Tags(Collection tags) {
27 | this.tags = tags;
28 | }
29 |
30 |
31 | public List toList() {
32 | return new ArrayList(tags);
33 | }
34 |
35 | public Tags completeWith(Collection tags) {
36 | Set merged = Sets.newHashSet();
37 | merged.addAll(this.tags);
38 | merged.addAll(tags);
39 | return Tags.from(merged);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Arnauld Loyer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/step/Scenario.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.step;
2 |
3 | import com.google.common.collect.Lists;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * @author @aloyer
9 | */
10 | public class Scenario {
11 | private String visualName;
12 | private List tags = Lists.newArrayList();
13 | private List stepList = Lists.newArrayList();
14 |
15 | public void setVisualName(String visualName) {
16 | this.visualName = visualName;
17 | }
18 |
19 | public String getVisualName() {
20 | return visualName;
21 | }
22 |
23 | public void add(Step step) {
24 | stepList.add(step);
25 | }
26 |
27 | public void traverse(FeatureVisitor visitor) {
28 | visitor.enterScenario(this);
29 | for (Step step : stepList)
30 | step.traverse(visitor);
31 | visitor.exitScenario(this);
32 | }
33 |
34 | public void addTags(List tags) {
35 | this.tags.addAll(tags);
36 | }
37 |
38 | public List getTags() {
39 | return tags;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/gson/ScenarioExecSerializer.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.gson;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.JsonElement;
5 | import com.google.gson.JsonObject;
6 | import com.google.gson.JsonSerializationContext;
7 | import com.google.gson.JsonSerializer;
8 |
9 | import java.lang.reflect.Type;
10 |
11 | import static tzatziki.analysis.exec.gson.StepContainerDeserializer.SCENARIO;
12 | import static tzatziki.analysis.exec.gson.StepContainerDeserializer.TYPE;
13 |
14 | /**
15 | * @author @aloyer
16 | */
17 | public class ScenarioExecSerializer implements JsonSerializer {
18 |
19 | private final Gson delegate;
20 |
21 | public ScenarioExecSerializer(Gson delegate) {
22 | this.delegate = delegate;
23 | }
24 |
25 | @Override
26 | public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) {
27 | JsonObject serialized = delegate.toJsonTree(src, typeOfSrc).getAsJsonObject();
28 | serialized.addProperty(TYPE, SCENARIO);
29 | return serialized;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/coffeemachine/03-extra-hot.feature:
--------------------------------------------------------------------------------
1 | Feature: Extra Hot and Orange Juice
2 |
3 | # **In order to** provide more choice and to attract more customer
4 | #
5 | # **As a** shopkeeper
6 | #
7 | # **I want to** be able to make orange juice and to deliver extra hot drinks
8 |
9 | @takeOrder @tea @sugar @extraHot @protocol
10 | Scenario: An extra hot tea with 1 sugar
11 |
12 | When I order an extra hot "Tea" with 1 sugar
13 | Then the instruction generated should be "Th:1:0"
14 |
15 | @takeOrder @orangeJuice @protocol
16 | Scenario: An Orange juice
17 |
18 | When I order an "Orange Juice"
19 | Then the instruction generated should be "O::"
20 |
21 | @takeOrder @orangeJuice @sugar @protocol
22 | @wip
23 | Scenario: Extra sugar with Orange Juice is ignored
24 |
25 | When I order an "Orange Juice" with 1 sugar
26 | Then the instruction generated should be "O::"
27 |
28 | @takeOrder @orangeJuice @sugar @extraHot @protocol
29 | Scenario: Extra hot with Orange Juice is ignored
30 |
31 | When I order an extra hot "Orange Juice" with 1 sugar
32 | Then the instruction generated should be "O::"
33 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/resources/tzatziki/junit/coffeemachine/03-extra-hot.feature:
--------------------------------------------------------------------------------
1 | Feature: Extra Hot and Orange Juice
2 |
3 | # **In order to** provide more choice and to attract more customer
4 | #
5 | # **As a** shopkeeper
6 | #
7 | # **I want to** be able to make orange juice and to deliver extra hot drinks
8 |
9 | @takeOrder @tea @sugar @extraHot @protocol
10 | Scenario: An extra hot tea with 1 sugar
11 |
12 | When I order an extra hot "Tea" with 1 sugar
13 | Then the instruction generated should be "Th:1:0"
14 |
15 | @takeOrder @orangeJuice @protocol
16 | Scenario: An Orange juice
17 |
18 | When I order an "Orange Juice"
19 | Then the instruction generated should be "O::"
20 |
21 | @takeOrder @orangeJuice @sugar @protocol
22 | @wip
23 | Scenario: Extra sugar with Orange Juice is ignored
24 |
25 | When I order an "Orange Juice" with 1 sugar
26 | Then the instruction generated should be "O::"
27 |
28 | @takeOrder @orangeJuice @sugar @extraHot @protocol
29 | Scenario: Extra hot with Orange Juice is ignored
30 |
31 | When I order an extra hot "Orange Juice" with 1 sugar
32 | Then the instruction generated should be "O::"
33 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/game-of-life/board.feature:
--------------------------------------------------------------------------------
1 | Feature: Petri board of the game of life should...
2 |
3 |
4 | In order to play a game of life
5 | As a computer scientist
6 | I need to be able to setup the board and click start
7 |
8 | @ui @toggle
9 | Scenario: Click on a dead cell
10 | Given a board
11 | When I click on a dead cell
12 | Then it should come to life
13 |
14 |
15 | @ui @toggle
16 | Scenario: Click on an alive cell
17 | Given a board
18 | When I click on an alive cell
19 | Then it should kill the cell
20 |
21 | @toggle
22 | Scenario: Toggle a cell on the board
23 |
24 | Given a 5 by 5 game
25 | When I toggle the cell at (2, 3)
26 | Then the grid should look like
27 | """.....
28 | .....
29 | .....
30 | ..X..
31 | ....."""
32 | When I toggle the cell at (2, 4)
33 | Then the grid should look like
34 | """.....
35 | .....
36 | .....
37 | ..X..
38 | ..X.."""
39 | When I toggle the cell at (2, 3)
40 | Then the grid should look like
41 | """.....
42 | .....
43 | .....
44 | .....
45 | ..X.."""
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/java/GrammarParserStatisticsListener.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java;
2 |
3 | import com.thoughtworks.qdox.model.JavaClass;
4 | import com.thoughtworks.qdox.model.JavaMethod;
5 | import com.thoughtworks.qdox.model.JavaPackage;
6 |
7 | /**
8 | * @author @aloyer
9 | */
10 | public class GrammarParserStatisticsListener extends GrammarParserListenerAdapter {
11 | private int packagesParsed = 0;
12 | private int classesParsed = 0;
13 | private int methodsParsed = 0;
14 |
15 | public int numberOfClassesParsed() {
16 | return classesParsed;
17 | }
18 |
19 | public int numberOfPackagesParsed() {
20 | return packagesParsed;
21 | }
22 |
23 | public int numberOfMethodsParsed() {
24 | return methodsParsed;
25 | }
26 |
27 | @Override
28 | public void exitingPackage(JavaPackage pkg) {
29 | packagesParsed++;
30 | }
31 |
32 | @Override
33 | public void exitingClass(JavaClass klazz) {
34 | classesParsed++;
35 | }
36 |
37 | @Override
38 | public void exitingMethod(JavaMethod method) {
39 | methodsParsed++;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/java/GrammarParserListenerAdapter.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java;
2 |
3 | import com.thoughtworks.qdox.model.JavaClass;
4 | import com.thoughtworks.qdox.model.JavaMethod;
5 | import com.thoughtworks.qdox.model.JavaPackage;
6 |
7 | import java.util.Collection;
8 |
9 | /**
10 | * @author @aloyer
11 | */
12 | public class GrammarParserListenerAdapter implements GrammarParserListener {
13 | @Override
14 | public void aboutToParsePackages(Collection packages) {
15 | }
16 |
17 | @Override
18 | public void aboutToParseClasses(Collection classes) {
19 | }
20 |
21 | @Override
22 | public void enteringPackage(JavaPackage pkg) {
23 | }
24 |
25 | @Override
26 | public void exitingPackage(JavaPackage pkg) {
27 | }
28 |
29 | @Override
30 | public void enteringClass(JavaClass klazz) {
31 | }
32 |
33 | @Override
34 | public void exitingClass(JavaClass klazz) {
35 | }
36 |
37 | @Override
38 | public void enteringMethod(JavaMethod method) {
39 | }
40 |
41 | @Override
42 | public void exitingMethod(JavaMethod method) {
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/java/tzatziki/util/PackagePathTest.java:
--------------------------------------------------------------------------------
1 | package tzatziki.util;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.assertj.core.api.Assertions.assertThat;
6 | import static tzatziki.util.PackagePath.directSubPackageOf;
7 |
8 |
9 | public class PackagePathTest {
10 |
11 | @Test
12 | public void directSubPackageOf_should_work___() {
13 | assertThat(directSubPackageOf("", "tzatziki.util.matcher.string")).isEqualTo("tzatziki");
14 | assertThat(directSubPackageOf("tzatziki", "tzatziki.util")).isEqualTo("util");
15 | assertThat(directSubPackageOf("tzatziki.util", "tzatziki.util.matcher")).isEqualTo("matcher");
16 | assertThat(directSubPackageOf("tzatziki.util", "tzatziki.util.matcher.string")).isEqualTo("matcher");
17 | }
18 |
19 | @Test
20 | public void directSubPackageOf_should_return_null_when_same_packages_are_provided() {
21 | assertThat(directSubPackageOf("tzatziki.pdf", "tzatziki.pdf")).isNull();
22 | }
23 |
24 | @Test(expected = IllegalArgumentException.class)
25 | public void directSubPackageOf_should_throw_when_packages_does_not_belong_to_the_same_tree() {
26 | directSubPackageOf("tzatziki.pdf", "tzatziki.util");
27 | }
28 | }
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/step/ScenarioOutline.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.step;
2 |
3 | import com.google.common.collect.Lists;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * @author @aloyer
9 | */
10 | public class ScenarioOutline {
11 |
12 | private String visualName;
13 | private List tags = Lists.newArrayList();
14 | private List exampleScenarios = Lists.newArrayList();
15 |
16 | public void setVisualName(String visualName) {
17 | this.visualName = visualName;
18 | }
19 |
20 | public String getVisualName() {
21 | return visualName;
22 | }
23 |
24 | public void add(Scenario exampleScenario) {
25 | exampleScenarios.add(exampleScenario);
26 | }
27 |
28 | public void traverse(FeatureVisitor visitor) {
29 | visitor.enterScenarioOutline(this);
30 | for (Scenario scenario : exampleScenarios)
31 | scenario.traverse(visitor);
32 | visitor.exitScenarioOutline(this);
33 | }
34 |
35 | public void addTags(List tags) {
36 | this.tags.addAll(tags);
37 | }
38 |
39 | public List getTags() {
40 | return tags;
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/tag/TagDictionaryLoader.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.tag;
2 |
3 | import org.apache.commons.io.IOUtils;
4 | import tzatziki.util.PropertiesLoader;
5 |
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.io.UnsupportedEncodingException;
9 | import java.net.URL;
10 | import java.util.Properties;
11 |
12 | /**
13 | * @author @aloyer
14 | */
15 | public class TagDictionaryLoader {
16 | public TagDictionary fromUTF8PropertiesResource(String resourcePath) throws IOException {
17 | URL resource = PropertiesLoader.class.getResource(resourcePath);
18 | if (resource == null)
19 | throw new IllegalArgumentException("Resource not found '" + resourcePath + "'");
20 |
21 | InputStream stream = resource.openStream();
22 | try {
23 | Properties properties = new PropertiesLoader().loadFromUTF8Stream(stream);
24 |
25 | return new TagDictionary().declareTags(properties);
26 | } catch (UnsupportedEncodingException e) {
27 | throw new RuntimeException("UTF8 not supported...", e);
28 | } finally {
29 | IOUtils.closeQuietly(stream);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/util/MemoizableIterator.java:
--------------------------------------------------------------------------------
1 | package tzatziki.util;
2 |
3 | import java.util.Iterator;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public class MemoizableIterator implements Iterator {
9 |
10 | public static MemoizableIterator wrap(Iterator iterator) {
11 | return new MemoizableIterator(iterator);
12 | }
13 |
14 |
15 | private final Iterator delegate;
16 | private boolean currentInitialized = false;
17 | private T current;
18 |
19 | public MemoizableIterator(Iterator delegate) {
20 | this.delegate = delegate;
21 | }
22 |
23 | @Override
24 | public boolean hasNext() {
25 | return delegate.hasNext();
26 | }
27 |
28 | @Override
29 | public T next() {
30 | current = delegate.next();
31 | currentInitialized = true;
32 | return current;
33 | }
34 |
35 | @Override
36 | public void remove() {
37 | throw new UnsupportedOperationException();
38 | }
39 |
40 | public T current() {
41 | if(!currentInitialized)
42 | throw new IllegalStateException("Invoke next() at least once");
43 | return current;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/java/tzatziki/TestSettings.java:
--------------------------------------------------------------------------------
1 | package tzatziki;
2 |
3 | import org.apache.commons.io.IOUtils;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.util.Properties;
8 |
9 | /**
10 | * @author @aloyer
11 | */
12 | public class TestSettings {
13 |
14 | private Properties properties;
15 |
16 | public TestSettings() {
17 | }
18 |
19 | public String getBuildDir() {
20 | return getProperties().getProperty("buildDir");
21 | }
22 |
23 | public String getBaseDir() {
24 | return getProperties().getProperty("baseDir");
25 | }
26 |
27 | public Properties getProperties() {
28 | if (properties == null) {
29 | properties = new Properties();
30 | InputStream stream = null;
31 | try {
32 | stream = getClass().getResourceAsStream("/test-settings.properties");
33 | properties.load(stream);
34 | } catch (IOException e) {
35 | throw new RuntimeException("Failed to open settings", e);
36 | } finally {
37 | IOUtils.closeQuietly(stream);
38 | }
39 | }
40 | return properties;
41 | }
42 | }
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/util/PackagePath.java:
--------------------------------------------------------------------------------
1 | package tzatziki.util;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class PackagePath {
7 |
8 | public static boolean isSubPackageOf(String parent, String packageName) {
9 | return packageName.startsWith(parent)
10 | && !areSamePackage(parent, packageName);
11 | }
12 |
13 | public static String directSubPackageOf(String parent, String packageName) {
14 | if (areSamePackage(parent, packageName))
15 | return null;
16 | if (!isSubPackageOf(parent, packageName))
17 | throw new IllegalArgumentException("Package '" + packageName + "' is not a subPackage of '" + parent + "'");
18 |
19 | int dec = parent.length();
20 | if (dec > 0)
21 | dec++; // add '.'
22 |
23 | String subTree = packageName.substring(dec);
24 | int nextPkg = subTree.indexOf('.');
25 | if (nextPkg < 0)
26 | return subTree;
27 | else
28 | return subTree.substring(0, nextPkg);
29 | }
30 |
31 | public static boolean areSamePackage(String packageName1, String packageName2) {
32 | return packageName1.equals(packageName2);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/test/java/tzatziki/pdf/TestSettings.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf;
2 |
3 | import org.apache.commons.io.IOUtils;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.util.Properties;
8 |
9 | /**
10 | * @author @aloyer
11 | */
12 | public class TestSettings {
13 |
14 | private Properties properties;
15 |
16 | public TestSettings() {
17 | }
18 |
19 | public String getBuildDir() {
20 | return getProperties().getProperty("buildDir");
21 | }
22 |
23 | public String getBaseDir() {
24 | return getProperties().getProperty("baseDir");
25 | }
26 |
27 | public Properties getProperties() {
28 | if (properties == null) {
29 | properties = new Properties();
30 | InputStream stream = null;
31 | try {
32 | stream = getClass().getResourceAsStream("/test-settings.properties");
33 | properties.load(stream);
34 | } catch (IOException e) {
35 | throw new RuntimeException("Failed to open settings", e);
36 | } finally {
37 | IOUtils.closeQuietly(stream);
38 | }
39 | }
40 | return properties;
41 | }
42 | }
--------------------------------------------------------------------------------
/tzatziki-samples/src/test/java/samples/TestSettings.java:
--------------------------------------------------------------------------------
1 | package samples;
2 |
3 | import org.apache.commons.io.IOUtils;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.util.Properties;
8 |
9 | /**
10 | * @author @aloyer
11 | */
12 | public class TestSettings {
13 |
14 | private Properties properties;
15 |
16 | public TestSettings() {
17 | }
18 |
19 | public String getBuildDir() {
20 | return getProperties().getProperty("buildDir");
21 | }
22 |
23 | public String getBaseDir() {
24 | return getProperties().getProperty("baseDir");
25 | }
26 |
27 | public Properties getProperties() {
28 | if (properties == null) {
29 | properties = new Properties();
30 | InputStream stream = null;
31 | try {
32 | stream = getClass().getResourceAsStream("/test-settings.properties");
33 | properties.load(stream);
34 | } catch (IOException e) {
35 | throw new RuntimeException("Failed to open settings", e);
36 | } finally {
37 | IOUtils.closeQuietly(stream);
38 | }
39 | }
40 | return properties;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/gson/ScenarioOutlineExecSerializer.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.gson;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.JsonElement;
5 | import com.google.gson.JsonObject;
6 | import com.google.gson.JsonSerializationContext;
7 | import com.google.gson.JsonSerializer;
8 | import tzatziki.analysis.exec.model.ScenarioOutlineExec;
9 |
10 | import java.lang.reflect.Type;
11 |
12 | import static tzatziki.analysis.exec.gson.StepContainerDeserializer.SCENARIO_OUTLINE;
13 | import static tzatziki.analysis.exec.gson.StepContainerDeserializer.TYPE;
14 |
15 | /**
16 | * @author @aloyer
17 | */
18 | public class ScenarioOutlineExecSerializer implements JsonSerializer {
19 |
20 | private final Gson delegate;
21 |
22 | public ScenarioOutlineExecSerializer(Gson delegate) {
23 | this.delegate = delegate;
24 | }
25 |
26 | @Override
27 | public JsonElement serialize(ScenarioOutlineExec src, Type typeOfSrc, JsonSerializationContext context) {
28 | JsonObject serialized = delegate.toJsonTree(src, typeOfSrc).getAsJsonObject();
29 | serialized.addProperty(TYPE, SCENARIO_OUTLINE);
30 | return serialized;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/java/tzatziki/util/LoadJson.java:
--------------------------------------------------------------------------------
1 | package tzatziki.util;
2 |
3 | import com.fasterxml.jackson.databind.JsonNode;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import org.apache.commons.io.IOUtils;
6 |
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.io.InputStreamReader;
10 | import java.io.Reader;
11 | import java.io.StringReader;
12 |
13 | /**
14 | * @author @aloyer
15 | */
16 | public class LoadJson {
17 | private String charsetName = "UTF8";
18 |
19 | public JsonNode loadFromResource(String resourceName) throws IOException {
20 | InputStream in = getClass().getResourceAsStream(resourceName);
21 | try {
22 | Reader reader = new InputStreamReader(in, charsetName);
23 | ObjectMapper mapper = new ObjectMapper();
24 | return mapper.readValue(reader, JsonNode.class);
25 | } finally {
26 | IOUtils.closeQuietly(in);
27 | }
28 | }
29 |
30 | public JsonNode loadFromString(String content) throws IOException {
31 | Reader reader = new StringReader(content);
32 | ObjectMapper mapper = new ObjectMapper();
33 | return mapper.readValue(reader, JsonNode.class);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/java/samples/coffeemachine/Gateway.java:
--------------------------------------------------------------------------------
1 | package samples.coffeemachine;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class Gateway {
7 |
8 | private DrinkMaker drinkMaker;
9 |
10 | public Gateway(DrinkMaker drinkMaker) {
11 | this.drinkMaker = drinkMaker;
12 | }
13 |
14 |
15 | public void order(String drinkType, int nbSugar, boolean b) {
16 | StringBuilder c = new StringBuilder();
17 |
18 | if (drinkType.equalsIgnoreCase("Coffee"))
19 | c.append("C");
20 | else if (drinkType.equalsIgnoreCase("Tea"))
21 | c.append("T");
22 | else if (drinkType.equalsIgnoreCase("Chocolate"))
23 | c.append("H");
24 | else if (drinkType.equalsIgnoreCase("Orange Juice"))
25 | c.append("O");
26 |
27 |
28 | if (nbSugar > 0) {
29 | c.append(":").append(nbSugar).append(":0");
30 | } else
31 | c.append("::");
32 |
33 | drinkMaker.executeCommand(c.toString());
34 | }
35 |
36 | public void publish(String message) {
37 | if(message.contains("enough"))
38 | throw new IllegalArgumentException();
39 | drinkMaker.executeCommand("M:" + message);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/main/java/tzatziki/pdf/emitter/EmbeddedEmitter.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf.emitter;
2 |
3 | import com.itextpdf.text.Paragraph;
4 | import gutenberg.itext.Emitter;
5 | import gutenberg.itext.ITextContext;
6 | import gutenberg.itext.model.SourceCode;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import tzatziki.analysis.exec.model.Embedded;
10 |
11 | /**
12 | * @author @aloyer
13 | */
14 | public class EmbeddedEmitter implements Emitter {
15 |
16 | private Logger log = LoggerFactory.getLogger(EmbeddedEmitter.class);
17 |
18 | @Override
19 | public void emit(Embedded value, ITextContext emitterContext) {
20 | String mimeType = value.mimeType();
21 | if (mimeType.equalsIgnoreCase(SourceCode.MIME_TYPE)) {
22 | SourceCode sourceCode = SourceCode.fromBytes(value.data());
23 | emitterContext.emit(sourceCode);
24 | } else if (mimeType.startsWith("plain/text")) {
25 | String text = value.isText() ? value.text() : new String(value.data());
26 | Paragraph p = new Paragraph(text);
27 | emitterContext.emit(p);
28 | } else {
29 | log.warn("Unsupported mime type {}, data discarded", mimeType);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/main/java/tzatziki/pdf/EmitterContext.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf;
2 |
3 | import com.itextpdf.text.Document;
4 | import com.itextpdf.text.pdf.PdfWriter;
5 | import gutenberg.itext.ITextContext;
6 | import gutenberg.itext.Sections;
7 | import gutenberg.itext.Styles;
8 |
9 | /**
10 | * @author @aloyer
11 | */
12 | public class EmitterContext {
13 | private final ITextContext context;
14 | private final Settings settings;
15 | private final Sections sections;
16 | private final Styles styles;
17 |
18 | public EmitterContext(ITextContext context,
19 | Settings settings,
20 | Sections sections,
21 | Styles styles) {
22 | this.context = context;
23 | this.settings = settings;
24 | this.sections = sections;
25 | this.styles = styles;
26 | }
27 |
28 | public ITextContext iTextContext() {
29 | return context;
30 | }
31 |
32 | public Styles styles() {
33 | return styles;
34 | }
35 |
36 | public Document getDocument() {
37 | return context.getDocument();
38 | }
39 |
40 | public PdfWriter getPdfWriter() {
41 | return context.getPdfWriter();
42 | }
43 |
44 |
45 | public Settings getSettings() {
46 | return settings;
47 | }
48 |
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/dinovet/features/manage_diagnosis_events.feature:
--------------------------------------------------------------------------------
1 | Feature: Manage diagnosis events
2 | In order to know what treatment will be necessary for a patient
3 | An employee
4 | wants to be able to add diagnoses for the patient
5 |
6 | Scenario: Add a new diagnosis event
7 | Given I have added a client and patient
8 | And I have created a diagnosis
9 | And I am on the new diagnosis event page
10 | And I am an employee
11 | When I select "Rabies" from "Diagnosis"
12 | And I fill in "Comment" with "This is a bizzare case of mutant Rabies"
13 | And I press "Make Diagnosis"
14 | Then I should see "Diagnosis recorded"
15 | And I should see "Rabies"
16 |
17 | Scenario: Add two diagnosis of the same type
18 | Given I have added a client and patient
19 | And I have created a diagnosis
20 | And I am on the new diagnosis event page
21 | And I am an employee
22 | When I select "Rabies" from "Diagnosis"
23 | And I fill in "Comment" with "This is a bizzare case of mutant Rabies"
24 | And I press "Make Diagnosis"
25 | And I go to the new diagnosis event page
26 | And I select "Rabies" from "Diagnosis"
27 | And I fill in "Comment" with "This is a bizzare case of mutant Rabies"
28 | And I press "Make Diagnosis"
29 | Then I should see "Diagnosis recorded"
30 | And I should see "Rabies"
31 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/ScenarioRef.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | /**
4 | * @author @aloyer
5 | */
6 | public class ScenarioRef {
7 | private final String featureUri;
8 | private final String featureName;
9 | private final String scenarioName;
10 | private final LineRange lineRange;
11 |
12 | public ScenarioRef(String featureUri, String featureName, LineRange lineRange, String scenarioName) {
13 | this.featureUri = featureUri;
14 | this.featureName = featureName;
15 | this.scenarioName = scenarioName;
16 | this.lineRange = lineRange;
17 | }
18 |
19 | public String featureUri() {
20 | return featureUri;
21 | }
22 |
23 | public String featureName() {
24 | return featureName;
25 | }
26 |
27 | public String scenarioName() {
28 | return scenarioName;
29 | }
30 |
31 | public LineRange scenarioLineRange() {
32 | return lineRange;
33 | }
34 |
35 | @Override
36 | public String toString() {
37 | return "ScenarioRef{" +
38 | "featureUri='" + featureUri + '\'' +
39 | ", featureName='" + featureName + '\'' +
40 | ", scenarioName='" + scenarioName + '\'' +
41 | ", lineRange=" + lineRange +
42 | '}';
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/MatchExec.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | import com.google.common.collect.FluentIterable;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * @author @aloyer
9 | */
10 | public class MatchExec {
11 | private final String location;
12 | private final List arguments;
13 |
14 | public MatchExec(String location, List arguments) {
15 | this.location = location;
16 | this.arguments = arguments;
17 | }
18 |
19 | public FluentIterable getArgs() {
20 | return FluentIterable.from(arguments);
21 | }
22 |
23 | public String getLocation() {
24 | return location;
25 | }
26 |
27 | public MatchExec recursiveCopy() {
28 | // TODO find a suitable to ensure this is still valid
29 | // or a real copy is made if a field becomes mutable
30 | return this;
31 | }
32 |
33 | public static class Arg {
34 | public final String val;
35 | public final Integer offset;
36 |
37 | public Arg(String val, Integer offset) {
38 |
39 | this.val = val;
40 | this.offset = offset;
41 | }
42 |
43 | public Integer getOffset() {
44 | return offset;
45 | }
46 |
47 | public String getVal() {
48 | return val;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/backend/taxation_settings.feature:
--------------------------------------------------------------------------------
1 | @taxation
2 | Feature: Taxation settings
3 | In order configure my store taxation system
4 | As a store owner
5 | I want to be able to edit taxation configuration
6 |
7 | Background:
8 | Given I am logged in as administrator
9 | And the following zones are defined:
10 | | name | type | members |
11 | | German lands | country | Germany, Austria, Switzerland |
12 | | USA | country | United States |
13 |
14 | Scenario: Accessing the settings form
15 | Given I am on the dashboard page
16 | When I follow "Taxation settings"
17 | Then I should be on the taxation settings page
18 |
19 | Scenario: Saving the configuration
20 | Given I am on the taxation settings page
21 | When I press "Save changes"
22 | Then I should still be on the taxation settings page
23 | And I should see "Settings have been successfully updated."
24 |
25 | Scenario: Editing the default tax zone
26 | Given I am on the taxation settings page
27 | When I select "USA" from "Default tax zone"
28 | And I press "Save changes"
29 | Then I should still be on the taxation settings page
30 | And I should see "Settings have been successfully updated."
31 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/resources/tzatziki/analysis/step/subdomain/running-out.feature:
--------------------------------------------------------------------------------
1 | Feature: Running Out
2 |
3 | # **In order to** never run out beverage
4 | #
5 | # **As a** shop keeper
6 | #
7 | # **I want to** be informed that there is a shortage and to send a email notification
8 | # to the company so that they can come and refill the machine.
9 | #
10 | # The users of the coffee machine are complaining that there is often shortages
11 | # of water and/or milk. It takes weeks before the machine is refilled.
12 | # Your product owner wants to you to take advantage of the machine capabilities
13 | # to inform the user that there is a shortage
14 |
15 | @Notification
16 | Scenario: Last Coffee
17 | Given no more "Coffee" remaining in the machine
18 | When I order a "Coffee" with 1 sugar
19 | Then a mail should have been sent indicating "Coffee" is running out
20 |
21 | @Notification
22 | Scenario Outline: Last beverage
23 | Given no more "" remaining in the machine
24 | When I order a ""
25 | Then a mail should have been sent indicating "" is running out
26 |
27 | Examples:
28 | | drink |
29 | | Orange juice |
30 | | Tea |
31 | | Chocolate |
32 |
33 | @manual
34 | @Notification
35 | Scenario: Manually send an email
36 | Given an empty machine
37 | When I click on the "Send Test Email" button
38 | Then a test mail should have been sent
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/ExecutionFilter.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec;
2 |
3 | import com.google.common.base.Optional;
4 | import com.google.common.base.Predicate;
5 | import com.google.common.collect.FluentIterable;
6 | import tzatziki.analysis.exec.model.FeatureExec;
7 | import tzatziki.analysis.exec.tag.TagFilter;
8 | import tzatziki.analysis.exec.tag.Tags;
9 |
10 | import java.util.Set;
11 |
12 | /**
13 | * @author @aloyer
14 | */
15 | public class ExecutionFilter {
16 |
17 | private final TagFilter tagFilter;
18 |
19 | public ExecutionFilter(TagFilter tagFilter) {
20 | this.tagFilter = tagFilter;
21 | }
22 |
23 | public Optional filter(FeatureExec feature) {
24 | FluentIterable inheritedTags = feature.tags();
25 | FeatureExec copy = feature.recursiveCopy(matching(inheritedTags.toSet(), tagFilter));
26 |
27 | if (copy.scenario().isEmpty())
28 | return Optional.absent();
29 | else
30 | return Optional.of(copy);
31 | }
32 |
33 | private static Predicate matching(final Set inheritedTags, final TagFilter tagFilter) {
34 | return new Predicate() {
35 | @Override
36 | public boolean apply(Tags tags) {
37 | return tagFilter.apply(tags.completeWith(inheritedTags));
38 | }
39 | };
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/util/PropertiesLoader.java:
--------------------------------------------------------------------------------
1 | package tzatziki.util;
2 |
3 | import org.apache.commons.io.IOUtils;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.io.InputStreamReader;
8 | import java.io.UnsupportedEncodingException;
9 | import java.net.URL;
10 | import java.util.Properties;
11 |
12 | /**
13 | * @author @aloyer
14 | */
15 | public class PropertiesLoader {
16 | public Properties loadFromUTF8Resource(String resourcePath) throws IOException {
17 | URL resource = PropertiesLoader.class.getResource(resourcePath);
18 | if (resource == null)
19 | throw new IllegalArgumentException("Resource not found " + resource);
20 |
21 | InputStream stream = resource.openStream();
22 | try {
23 | return loadFromUTF8Stream(stream);
24 | } catch (UnsupportedEncodingException e) {
25 | throw new RuntimeException("UTF8 not supported...", e);
26 | } finally {
27 | IOUtils.closeQuietly(stream);
28 | }
29 | }
30 |
31 | public Properties loadFromUTF8Stream(InputStream stream) throws IOException {
32 | if (stream == null)
33 | throw new IllegalArgumentException("No stream provided");
34 |
35 | Properties properties = new Properties();
36 | properties.load(new InputStreamReader(stream, "UTF8"));
37 | return properties;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/main/java/tzatziki/pdf/emitter/TagsEmitter.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf.emitter;
2 |
3 | import com.google.common.collect.FluentIterable;
4 | import com.itextpdf.text.Chunk;
5 | import com.itextpdf.text.Font;
6 | import com.itextpdf.text.Paragraph;
7 | import gutenberg.itext.Emitter;
8 | import gutenberg.itext.ITextContext;
9 | import gutenberg.itext.Styles;
10 | import tzatziki.pdf.Settings;
11 | import tzatziki.pdf.model.Tags;
12 |
13 | /**
14 | * @author @aloyer
15 | */
16 | public class TagsEmitter implements Emitter {
17 | public static final String TAG_FONT = "tag-font";
18 |
19 | @Override
20 | public void emit(Tags tagContainer, ITextContext emitterContext) {
21 | FluentIterable tags = tagContainer.tags();
22 | if (tags.isEmpty())
23 | return;
24 |
25 | Styles styles = emitterContext.keyValues().getNullable(Styles.class).get();
26 |
27 | Paragraph pTags = new Paragraph("Tags: ", styles.getFontOrDefault(Settings.META_FONT));
28 | boolean first = true;
29 | Font tagFont = styles.getFontOrDefault(TAG_FONT);
30 | for (String text : tags) {
31 | if (first) {
32 | first = false;
33 | } else {
34 | text = ", " + text;
35 | }
36 |
37 | pTags.add(new Chunk(text, tagFont));
38 | }
39 |
40 | emitterContext.append(pTags);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/util/Filters.java:
--------------------------------------------------------------------------------
1 | package tzatziki.util;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public class Filters {
9 | public static Filter identity() {
10 | return new Filter() {
11 | @Override
12 | public T filter(T value) {
13 | return value;
14 | }
15 | };
16 | }
17 |
18 | public static Filter chain(final Filter... filters) {
19 | if (filters.length == 1)
20 | return filters[0];
21 | return new Filter() {
22 | @Override
23 | public T filter(T value) {
24 | T processed = value;
25 | for (Filter filter : filters) {
26 | processed = filter.filter(processed);
27 | }
28 | return processed;
29 | }
30 | };
31 | }
32 |
33 | public static Filter chain(final List> filters) {
34 | if (filters.size() == 1)
35 | return filters.get(0);
36 | return new Filter() {
37 | @Override
38 | public T filter(T value) {
39 | T processed = value;
40 | for (Filter filter : filters) {
41 | processed = filter.filter(processed);
42 | }
43 | return processed;
44 | }
45 | };
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/DataTable.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | import com.google.common.collect.FluentIterable;
4 | import com.google.common.collect.Lists;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * @author @aloyer
10 | */
11 | public class DataTable {
12 | public List rows = Lists.newArrayList();
13 |
14 | public void declareRow(Row row) {
15 | rows.add(row);
16 | }
17 |
18 | public boolean isEmpty() {
19 | return rows.isEmpty();
20 | }
21 |
22 | public int nbColumns() {
23 | if (isEmpty())
24 | throw new IllegalStateException("Table is empty");
25 | return rows.get(0).nbColumns();
26 | }
27 |
28 | public FluentIterable rows() {
29 | return FluentIterable.from(rows);
30 | }
31 |
32 | public static class Row {
33 | private List cells;
34 | private List comments;
35 |
36 | public Row(List cells, List comments) {
37 | this.cells = cells;
38 | this.comments = comments;
39 | }
40 |
41 | public FluentIterable cells() {
42 | return FluentIterable.from(cells);
43 | }
44 |
45 | public FluentIterable comments() {
46 | return FluentIterable.from(comments);
47 | }
48 |
49 | public int nbColumns() {
50 | return cells.size();
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/java/tzatziki/analysis/exec/tag/TagViewTest.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.tag;
2 |
3 | import org.junit.Test;
4 | import tzatziki.analysis.exec.gson.JsonIO;
5 | import tzatziki.analysis.exec.model.FeatureExec;
6 | import tzatziki.analysis.exec.support.TagView;
7 |
8 | import java.io.InputStream;
9 | import java.io.UnsupportedEncodingException;
10 | import java.util.List;
11 |
12 | import static org.assertj.core.api.Assertions.assertThat;
13 |
14 | public class TagViewTest {
15 |
16 | @Test
17 | public void usecase() throws Exception {
18 | List features = loadSample();
19 |
20 | TagView tagView = new TagView("Non wip about tea", TagFilter.from("~@wip", "@tea"));
21 | for (FeatureExec featureExec : features) {
22 | tagView.consolidateView(featureExec);
23 | }
24 |
25 | assertThat(tagView.scenarioMatching().size()).isEqualTo(11);
26 | assertThat(tagView.scenarioFailed().size()).isEqualTo(1);
27 | assertThat(tagView.scenarioPassed().size()).isEqualTo(1);
28 | assertThat(tagView.scenarioPending().size()).isEqualTo(1);
29 | assertThat(tagView.scenarioUndefined().size()).isEqualTo(8);
30 | assertThat(tagView.scenarioSkipped().size()).isEqualTo(0);
31 | }
32 |
33 | private List loadSample() throws UnsupportedEncodingException {
34 | InputStream in = getClass().getResourceAsStream("/tzatziki/analysis/exec/tag/coffeemachine-exec.json");
35 | return new JsonIO().load(in);
36 | }
37 | }
--------------------------------------------------------------------------------
/tzatziki-core/src/test/resources/tzatziki/junit/coffeemachine/05-running-out.feature:
--------------------------------------------------------------------------------
1 | Feature: Running Out
2 |
3 | # **In order to** never run out beverage
4 | #
5 | # **As a** shop keeper
6 | #
7 | # **I want to** be informed that there is a shortage and to send a email notification
8 | # to the company so that they can come and refill the machine.
9 | #
10 | # The users of the coffee machine are complaining that there is often shortages
11 | # of water and/or milk. It takes weeks before the machine is refilled.
12 | # Your product owner wants to you to take advantage of the machine capabilities
13 | # to inform the user that there is a shortage
14 |
15 | @notification @runningOut
16 | @coffee @sugar @takeOrder
17 | Scenario: Last Coffee
18 | Given no more "Coffee" remaining in the machine
19 | When I order a "Coffee" with 1 sugar
20 | Then a mail should have been sent indicating "Coffee" is running out
21 |
22 | @notification @runningOut
23 | @orangeJuice @tea @chocolate @takeOrder
24 | Scenario Outline: Last beverage
25 | Given no more "" remaining in the machine
26 | When I order a ""
27 | Then a mail should have been sent indicating "" is running out
28 |
29 | Examples:
30 | | drink |
31 | | Orange juice |
32 | | Tea |
33 | | Chocolate |
34 |
35 | @manual
36 | @notification @noDrink
37 | Scenario: Manually send an email
38 | Given an empty machine
39 | When I click on the "Send Test Email" button
40 | Then a test mail should have been sent
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/coffeemachine/05-running-out.feature:
--------------------------------------------------------------------------------
1 | Feature: Running Out
2 |
3 | # **In order to** never run out beverage
4 | #
5 | # **As a** shop keeper
6 | #
7 | # **I want to** be informed that there is a shortage and to send a email notification
8 | # to the company so that they can come and refill the machine.
9 | #
10 | # The users of the coffee machine are complaining that there is often shortages
11 | # of water and/or milk. It takes weeks before the machine is refilled.
12 | # Your product owner wants to you to take advantage of the machine capabilities
13 | # to inform the user that there is a shortage
14 |
15 | @notification @runningOut
16 | @coffee @sugar @takeOrder
17 | Scenario: Last Coffee
18 | Given no more "Coffee" remaining in the machine
19 | When I order a "Coffee" with 1 sugar
20 | Then a mail should have been sent indicating "Coffee" is running out
21 |
22 | @notification @runningOut
23 | @orangeJuice @tea @chocolate @takeOrder
24 | Scenario Outline: Last beverage
25 | Given no more "" remaining in the machine
26 | When I order a ""
27 | Then a mail should have been sent indicating "" is running out
28 |
29 | Examples:
30 | | drink |
31 | | Orange juice |
32 | | Tea |
33 | | Chocolate |
34 |
35 | @manual
36 | @notification @noDrink
37 | Scenario: Manually send an email
38 | Given an empty machine
39 | When I click on the "Send Test Email" button
40 | Then a test mail should have been sent
--------------------------------------------------------------------------------
/tzatziki-pdf/src/main/java/tzatziki/pdf/emitter/DefaultPdfEmitters.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf.emitter;
2 |
3 | import gutenberg.itext.ITextContext;
4 | import tzatziki.analysis.exec.model.BackgroundExec;
5 | import tzatziki.analysis.exec.model.Embedded;
6 | import tzatziki.analysis.exec.model.FeatureExec;
7 | import tzatziki.analysis.exec.model.ScenarioExec;
8 | import tzatziki.analysis.exec.support.TagViews;
9 | import tzatziki.analysis.java.Grammar;
10 | import tzatziki.analysis.tag.TagDictionary;
11 | import tzatziki.pdf.model.ScenarioOutlineWithResolved;
12 | import tzatziki.pdf.model.Steps;
13 | import tzatziki.pdf.model.Tags;
14 |
15 | /**
16 | * @author @aloyer
17 | */
18 | public class DefaultPdfEmitters {
19 | public void registerDefaults(ITextContext context) {
20 | context.register(FeatureExec.class, new FeatureEmitter());
21 | context.register(ScenarioExec.class, new ScenarioEmitter());
22 | context.register(BackgroundExec.class, new BackgroundEmitter());
23 | context.register(ScenarioOutlineWithResolved.class, new ScenarioOutlineEmitter());
24 | context.register(Steps.class, new StepsEmitter());
25 | context.register(Tags.class, new TagsEmitter());
26 | context.register(TagDictionary.class, new TagDictionaryEmitter());
27 | context.register(TagViews.class, new TagViewsEmitter());
28 | context.register(Embedded.class, new EmbeddedEmitter());
29 | context.register(Grammar.class, new GrammarEmitter());
30 | }
31 |
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/frontend/currencies.feature:
--------------------------------------------------------------------------------
1 | @currencies
2 | Feature: Currencies
3 | In order to buy products paying in different currencies
4 | As a visitor or as a logged in user
5 | I need to be able to switch between multiple currencies
6 |
7 | Background:
8 | Given there are following taxonomies defined:
9 | | name |
10 | | Category |
11 | And taxonomy "Category" has following taxons:
12 | | Clothing > PHP T-Shirts |
13 | And the following products exist:
14 | | name | price | taxons |
15 | | PHP Top | 5.99 | PHP T-Shirts |
16 | And there are following exchange rates:
17 | | currency | rate |
18 | | EUR | 1 |
19 | | USD | 0.76496 |
20 | | GBP | 1.13986 |
21 |
22 | Scenario: Switching currency as visitor
23 | Given I am on the store homepage
24 | When I follow "£"
25 | Then I should see product prices in "£"
26 | When I follow "$"
27 | Then I should see product prices in "$"
28 | When I follow "€"
29 | Then I should see product prices in "€"
30 |
31 | Scenario: Switching currency as logged in user
32 | Given I am logged in user
33 | And I am on the store homepage
34 | When I follow "£"
35 | Then I should see product prices in "£"
36 | When I follow "$"
37 | Then I should see product prices in "$"
38 | When I follow "£"
39 | Then I should see product prices in "£"
40 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/ScenarioExec.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | import com.google.common.base.Optional;
4 | import com.google.common.base.Predicate;
5 | import com.google.common.base.Predicates;
6 | import tzatziki.analysis.exec.tag.Tags;
7 |
8 | import static tzatziki.analysis.exec.model.StepExec.statusPassed;
9 |
10 | /**
11 | * @author @aloyer
12 | */
13 | public class ScenarioExec extends StepContainer {
14 | private final String keyword;
15 | private final String name;
16 |
17 | public ScenarioExec(String keyword, String name) {
18 | this.keyword = keyword;
19 | this.name = name;
20 | }
21 |
22 | public String name() {
23 | return name;
24 | }
25 |
26 | public boolean isSucess() {
27 | return steps().allMatch(statusPassed);
28 | }
29 |
30 | public ScenarioExec recursiveCopy() {
31 | ScenarioExec copy = new ScenarioExec(keyword, name);
32 | recursiveCopy(copy);
33 | return copy;
34 | }
35 |
36 | public Status status() {
37 | Optional opt = steps().firstMatch(Predicates.not(StepExec.statusPassed));
38 | if (opt.isPresent())
39 | return opt.get().result().status();
40 | else
41 | return Status.Passed;
42 | }
43 |
44 | public Optional recursiveCopy(Predicate matching) {
45 | if(matching.apply(Tags.from(tags().toList()))) {
46 | return Optional.of(recursiveCopy());
47 | }
48 | return Optional.absent();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/coffeemachine/06-background.feature:
--------------------------------------------------------------------------------
1 | Feature: Making Even more Money
2 |
3 | #
4 | # **In order to** have daily reports of what is sold and when
5 | #
6 | # **As a** shop keeper
7 | #
8 | # **I want to** track statistics of machine usage
9 | #
10 | #```formula
11 | # {\eta \leq C(\delta(\eta) +\Lambda_M(0,\delta))
12 | #```
13 | #
14 | #```formula
15 | # \Re{z} =\frac{n\pi \dfrac{\theta +\psi}{2}}{
16 | # \left(\dfrac{\theta +\psi}{2}\right)^2 + \left( \dfrac{1}{2}
17 | # \log \left\vert\dfrac{B}{A}\right\vert\right)^2}.
18 | #```
19 | #
20 |
21 | Background: Default daily activity
22 | Given the following orders:
23 | | time | drink |
24 | | 08:05:23 | Coffee |
25 | | 08:06:43 | Coffee |
26 | | 08:10:23 | Coffee |
27 | | 08:45:03 | Tea |
28 | | 10:05:47 | Coffee |
29 | | 10:05:47 | Chocolate |
30 |
31 | # Represents the default daily activity
32 | # Basic statistics usage
33 |
34 | @reporting @noDrink
35 | Scenario: Statistics collect basic usage
36 |
37 | # **Report is queried** and generated on-the-fly.
38 |
39 | When I query for a report
40 | Then the report output should be
41 | """
42 | chocolate: 1
43 | coffee: 4
44 | tea: 1
45 | ---
46 | Total: 3.00€
47 | """
48 |
49 | @reporting @noDrink
50 | Scenario: Statistics collect basic usage
51 |
52 | When I order a "Coffee" with 5 sugar
53 | And I query for a report
54 | Then the report output should be
55 | """
56 | chocolate: 1
57 | coffee: 4
58 | tea: 1
59 | ---
60 | Total: 3.00€
61 | """
62 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/frontend/cart_inclusive_tax.feature:
--------------------------------------------------------------------------------
1 | @checkout
2 | Feature: Tax included in price
3 | In order to handle product taxation
4 | As a store owner
5 | I want to apply taxes during checkout
6 |
7 | Background:
8 | Given there are following taxonomies defined:
9 | | name |
10 | | Category |
11 | And taxonomy "Category" has following taxons:
12 | | Clothing > PHP T-Shirts |
13 | And the following zones are defined:
14 | | name | type | members |
15 | | Germany | country | Germany |
16 | And there are following tax categories:
17 | | name |
18 | | Taxable Goods |
19 | And the following tax rates exist:
20 | | category | zone | name | amount | included in price? |
21 | | Taxable Goods | Germany | Germany VAT | 23% | yes |
22 | And the following products exist:
23 | | name | price | taxons | tax category |
24 | | PHP Top | 85 | PHP T-Shirts | Taxable Goods |
25 | And the default tax zone is "Germany"
26 |
27 | Scenario: Correct amounts are displayed for inclusive taxes
28 | Given I am on the store homepage
29 | And I follow "PHP T-Shirts"
30 | And I click "PHP Top"
31 | When I fill in "Quantity" with "3"
32 | And I press "Add to cart"
33 | Then I should be on the cart summary page
34 | And "Tax total: €47.68" should appear on the page
35 | And "Grand total: €255.00" should appear on the page
36 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/test/java/samples/coffeemachine/CoffeeMachineTagCheckTest.java:
--------------------------------------------------------------------------------
1 | package samples.coffeemachine;
2 |
3 | import org.junit.runner.RunWith;
4 | import samples.TestSettings;
5 | import tzatziki.analysis.step.Features;
6 | import tzatziki.analysis.tag.TagDictionary;
7 | import tzatziki.junit.SanityTagChecker;
8 |
9 | import java.io.File;
10 |
11 | import static tzatziki.junit.SanityTagChecker.loadFeaturesFromSourceDirectory;
12 |
13 | /**
14 | * @author @aloyer
15 | */
16 | @RunWith(SanityTagChecker.class)
17 | public class CoffeeMachineTagCheckTest {
18 |
19 | @SanityTagChecker.TagDictionaryProvider
20 | public static TagDictionary tagDictionary() {
21 | return new TagDictionary()
22 | .declareTag("@wip")
23 | .declareTag("@protocol")
24 | .declareTag("@notification")
25 | .declareTag("@message")
26 | .declareTag("@runningOut")
27 | .declareTag("@coffee")
28 | .declareTag("@tea")
29 | .declareTag("@chocolate")
30 | .declareTag("@sugar")
31 | .declareTag("@noSugar")
32 | .declareTag("@takeOrder")
33 | .declareTag("@payment")
34 | .declareTag("@reporting")
35 | .declareTag("@manual")
36 | ;
37 | }
38 |
39 | @SanityTagChecker.FeaturesProvider
40 | public static Features features() {
41 | String basedir = new TestSettings().getBaseDir();
42 | return loadFeaturesFromSourceDirectory(new File(basedir, "src/main/resources/samples/coffeemachine"));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/frontend/checkout_finalize.feature:
--------------------------------------------------------------------------------
1 | @checkout
2 | Feature: Checkout finalization
3 | In order to buy products
4 | As a visitor
5 | I want to be able to complete the checkout process
6 |
7 | Background:
8 | Given there are following taxonomies defined:
9 | | name |
10 | | Category |
11 | And taxonomy "Category" has following taxons:
12 | | Clothing > PHP T-Shirts |
13 | And the following products exist:
14 | | name | price | taxons |
15 | | PHP Top | 5.99 | PHP T-Shirts |
16 | And the following zones are defined:
17 | | name | type | members |
18 | | UK | country | United Kingdom |
19 | And the following shipping methods exist:
20 | | zone | name |
21 | | UK | DHL Express |
22 | And the following payment methods exist:
23 | | name | gateway | enabled |
24 | | Dummy | dummy | yes |
25 |
26 | Scenario: Placing the order
27 | Given I am logged in user
28 | And I added product "PHP Top" to cart
29 | And I go to the checkout start page
30 | And I fill in the shipping address to United Kingdom
31 | And I press "Continue"
32 | And I select the "DHL Express" radio button
33 | And I press "Continue"
34 | And I select the "Dummy" radio button
35 | And I press "Continue"
36 | When I click "Place order"
37 | Then I should be on the store homepage
38 | And I should see "Thank you for your order!"
39 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/java/UsedBy.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java;
2 |
3 | import static tzatziki.util.Equal.areEquals;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public class UsedBy {
9 | private final String featureUri;
10 | private final String scenarioOutlineName;
11 | private final String scenarioName;
12 |
13 | public UsedBy(String featureUri, String scenarioOutlineName, String scenarioName) {
14 | this.featureUri = featureUri;
15 | this.scenarioOutlineName = scenarioOutlineName;
16 | this.scenarioName = scenarioName;
17 | }
18 |
19 | @Override
20 | public boolean equals(Object o) {
21 | if (this == o) return true;
22 | if (o == null || getClass() != o.getClass()) return false;
23 |
24 | UsedBy usedBy = (UsedBy) o;
25 | return areEquals(featureUri, usedBy.featureUri)
26 | && areEquals(scenarioName, usedBy.scenarioName)
27 | && areEquals(scenarioOutlineName, usedBy.scenarioOutlineName);
28 | }
29 |
30 | @Override
31 | public int hashCode() {
32 | int result = featureUri.hashCode();
33 | result = 31 * result + (scenarioOutlineName != null ? scenarioOutlineName.hashCode() : 0);
34 | result = 31 * result + scenarioName.hashCode();
35 | return result;
36 | }
37 |
38 | @Override
39 | public String toString() {
40 | return "UsedBy{" +
41 | "featureUri='" + featureUri + '\'' +
42 | ", scenarioOutlineName='" + scenarioOutlineName + '\'' +
43 | ", scenarioName='" + scenarioName + '\'' +
44 | '}';
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/java/HumanReadableRegex.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java;
2 |
3 | import java.util.regex.Pattern;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public class HumanReadableRegex {
9 | private final String rawPattern;
10 | private final Pattern pattern;
11 |
12 | public HumanReadableRegex(String rawPattern) {
13 | this.rawPattern = rawPattern;
14 | this.pattern = Pattern.compile(rawPattern);
15 | }
16 |
17 | public String humanReadable() {
18 | String s = discardStartAndEndModifier(rawPattern);
19 | s = replaceCapturingDigits(s);
20 | s = replaceCapturingDecimal(s);
21 | s = replaceCapturingAnything(s);
22 | s = replaceOptionalCharacterWithParenthesis(s);
23 | return s;
24 | }
25 |
26 | private String replaceOptionalCharacterWithParenthesis(String s) {
27 | return s.replaceAll("([a-z])\\?", "($1)");
28 | }
29 |
30 | private String replaceCapturingAnything(String s) {
31 | return s.replaceAll("\\((\\[\\^\"\\][+*]|\\.[+*])\\)", "");
32 | }
33 |
34 | private String replaceCapturingDigits(String s) {
35 | return s.replaceAll("\\(\\\\d[+*]\\)", "");
36 | }
37 |
38 | private String replaceCapturingDecimal(String s) {
39 | return s.replaceAll("\\(\\\\d[+](\\\\.\\?)?\\|\\\\d[*]\\\\.\\\\d[+]\\)", "");
40 | }
41 |
42 |
43 | private String discardStartAndEndModifier(String value) {
44 | return value
45 | .replaceAll("^\\^(.*)\\$", "$1")
46 | .replaceAll("^\\^(.*)", "$1")
47 | .replaceAll("(.*)\\$", "$1");
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/game-of-life/game-of-life.feature:
--------------------------------------------------------------------------------
1 | Feature: The game of life should...
2 | 1. Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
3 | 2. Any live cell with more than three live neighbours dies, as if by overcrowding.
4 | 3. Any live cell with two or three live neighbours lives on to the next generation.
5 | 4. Any dead cell with exactly three live neighbours becomes a live cell.
6 |
7 | @underpopulation
8 | Scenario: Cell has no neighbors
9 |
10 | Given Cell is alive
11 | And Cell has "0" neighbors
12 | When I go to the next generation
13 | Then Cell should be dead
14 |
15 | @underpopulation
16 | Scenario: Cell has one neighbor
17 |
18 | Given Cell is alive
19 | And Cell has "1" neighbors
20 | When I go to the next generation
21 | Then Cell should be dead
22 |
23 | @survive
24 | Scenario: Cell survives to the next generation with two neighbors
25 |
26 | Given Cell is alive
27 | And Cell has "2" neighbors
28 | When I go to the next generation
29 | Then Cell should be alive
30 |
31 | @survive
32 | Scenario: Cell survives to the next generation
33 |
34 | Given Cell is alive
35 | And Cell has "3" neighbors
36 | When I go to the next generation
37 | Then Cell should be alive
38 |
39 | @overcrowding
40 | Scenario: Cell dies of overpopulation
41 |
42 | Given Cell is alive
43 | And Cell has "4" neighbors
44 | When I go to the next generation
45 | Then Cell should be dead
46 |
47 | @generation
48 | Scenario: Empty cell has a birth
49 |
50 | Given Cell is dead
51 | And Cell has "3" neighbors
52 | When I go to the next generation
53 | Then Cell should be alive
54 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/backend/dashboard.feature:
--------------------------------------------------------------------------------
1 | @dashboard
2 | Feature: Store dashboard
3 | In order to have an overview of my business
4 | As a store owner
5 | I need to be able to see sales info in backend dashboard
6 |
7 | Background:
8 | Given I am logged in as administrator
9 | And the following zones are defined:
10 | | name | type | members |
11 | | German lands | country | Germany, Austria, Switzerland |
12 | And there are products:
13 | | name | price |
14 | | Mug | 5.99 |
15 | | Sticker | 10.00 |
16 | And the following orders were placed:
17 | | user | address |
18 | | klaus@example.com | Klaus Schmitt, Heine-Straße 12, 99734, Berlin, Germany |
19 | | lars@example.com | Lars Meine, Fun-Straße 1, 90032, Vienna, Austria |
20 | And order #000000001 has following items:
21 | | product | quantity |
22 | | Mug | 2 |
23 | And order #000000002 has following items:
24 | | product | quantity |
25 | | Mug | 1 |
26 | | Sticker | 4 |
27 |
28 | Scenario: Viewing the dashboard at website root
29 | Given I am on the dashboard page
30 | Then I should see "Administration dashboard"
31 |
32 | Scenario: Viewing recent orders
33 | Given I am on the dashboard page
34 | Then I should see 2 orders in the list
35 |
36 | Scenario: Viewing recent users
37 | Given I am on the dashboard page
38 | Then I should see 3 users in the list
39 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/gson/JsonIO.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.gson;
2 |
3 | import com.google.common.collect.Lists;
4 | import com.google.gson.Gson;
5 | import com.google.gson.GsonBuilder;
6 | import tzatziki.analysis.exec.model.FeatureExec;
7 | import tzatziki.analysis.exec.model.ScenarioExec;
8 | import tzatziki.analysis.exec.model.ScenarioOutlineExec;
9 | import tzatziki.analysis.exec.model.StepContainer;
10 |
11 | import java.io.InputStream;
12 | import java.io.InputStreamReader;
13 | import java.io.UnsupportedEncodingException;
14 | import java.util.List;
15 |
16 | /**
17 | * @author @aloyer
18 | */
19 | public class JsonIO {
20 |
21 | public Gson createGson() {
22 | Gson delegate = new GsonBuilder().setPrettyPrinting().create();
23 | return new GsonBuilder()
24 | .setPrettyPrinting()
25 | .registerTypeAdapter(ScenarioExec.class, new ScenarioExecSerializer(delegate))
26 | .registerTypeAdapter(ScenarioOutlineExec.class, new ScenarioOutlineExecSerializer(delegate))
27 | .registerTypeAdapter(StepContainer.class, new StepContainerDeserializer(delegate))
28 | .create();
29 | }
30 |
31 | public List load(InputStream in) throws UnsupportedEncodingException {
32 | return load(in, "UTF8");
33 | }
34 |
35 | public List load(InputStream in, String charset) throws UnsupportedEncodingException {
36 | Features features = createGson().fromJson(new InputStreamReader(in, charset), Features.class);
37 | return features.features;
38 | }
39 |
40 | public static class Features {
41 | private List features = Lists.newArrayList();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/coffeemachine/01-making-drinks.feature:
--------------------------------------------------------------------------------
1 | Feature: Making Drinks
2 |
3 | #
4 | # **In order** to send commands to the drink maker
5 | # **As a** developer
6 | # **I want to** implement the logic that translates orders from customers of the coffee machine to the drink maker
7 | #
8 | # {width:66px, height:100px}
9 | # 
10 | #
11 | # The code will use the drink maker protocol (see below) to send commands to the drink maker.
12 | #
13 | # The coffee machine can serves 3 type of drinks:
14 | #
15 | # * tea,
16 | # * coffee,
17 | # * chocolate.
18 | #
19 | #
20 | #```ditaa
21 | #
22 | # /---------+ +------------+
23 | # | Order |---->| Protocol |
24 | # +---------/ +------------+
25 | #
26 | #```
27 |
28 | @takeOrder @wip @tea @sugar @protocol
29 | Scenario: A tea with 1 sugar and a stick
30 |
31 | When I order a "Tea" with 1 sugar
32 | Then the instruction generated should be "T:1:0"
33 |
34 | @takeOrder @chocolate @noSugar @protocol
35 | Scenario: A chocolate with no sugar - and therefore no stick
36 |
37 | When I order a "Chocolate" with 0 sugar
38 | Then the instruction generated should be "H::"
39 |
40 | @takeOrder @coffee @sugar @protocol
41 | Scenario: A tea with 1 sugar and a stick
42 |
43 | When I order a "Coffee" with 2 sugar
44 | Then the instruction generated should be "C:2:0"
45 |
46 | @message @protocol
47 | Scenario Outline: any message received is forwarded for the customer to see
48 | When the message "" is sent
49 | Then the instruction generated should be ""
50 |
51 | Examples:
52 | | message | expected |
53 | | Hello | M:Hello |
54 | | Not enough money | M:Not enough money |
--------------------------------------------------------------------------------
/tzatziki-web/src/main/java/tzatziki/web/App.java:
--------------------------------------------------------------------------------
1 | package tzatziki.web;
2 |
3 |
4 | import io.dropwizard.Application;
5 | import io.dropwizard.assets.AssetsBundle;
6 | import io.dropwizard.jdbi.DBIFactory;
7 | import io.dropwizard.setup.Bootstrap;
8 | import io.dropwizard.setup.Environment;
9 | import org.skife.jdbi.v2.DBI;
10 |
11 |
12 | /**
13 | * @author @aloyer
14 | */
15 | public class App extends Application {
16 | private GrammarDAO grammarDAO;
17 |
18 | public static void main(String[] args) throws Exception {
19 | new App().run(args);
20 | }
21 |
22 | @Override
23 | public void initialize(Bootstrap bootstrap) {
24 | bootstrap.addBundle(new AssetsBundle("/assets/", "/"));
25 | }
26 |
27 | @Override
28 | public void run(AppConfiguration config, Environment environment) throws Exception {
29 | DBIFactory factory = new DBIFactory();
30 | DBI jdbi = factory.build(environment, config.getDataSourceFactory(), "db");
31 | ScenarioDAO scenarioDAO = jdbi.onDemand(ScenarioDAO.class);
32 |
33 | GrammarResource grammarResource = new GrammarResource(grammarDAO);
34 | GrammarDAOHealthCheck grammarDAOHealthCheck = new GrammarDAOHealthCheck(grammarDAO);
35 |
36 | ScenarioResource scenarioResource = new ScenarioResource(scenarioDAO);
37 | ScenarioDAOHealthCheck scenarioDAOHealthCheck = new ScenarioDAOHealthCheck(scenarioDAO);
38 |
39 | environment.jersey().register(grammarResource);
40 | environment.jersey().register(scenarioResource);
41 | environment.healthChecks().register("grammar-dao", grammarDAOHealthCheck);
42 | environment.healthChecks().register("scenario-dao", scenarioDAOHealthCheck);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/gson/StepContainerDeserializer.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.gson;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.JsonDeserializationContext;
5 | import com.google.gson.JsonDeserializer;
6 | import com.google.gson.JsonElement;
7 | import com.google.gson.JsonParseException;
8 | import com.google.gson.JsonPrimitive;
9 | import tzatziki.analysis.exec.model.ScenarioExec;
10 | import tzatziki.analysis.exec.model.ScenarioOutlineExec;
11 | import tzatziki.analysis.exec.model.StepContainer;
12 |
13 | import java.lang.reflect.Type;
14 |
15 | /**
16 | * @author @aloyer
17 | */
18 | public class StepContainerDeserializer implements JsonDeserializer {
19 | public static final String TYPE = "type";
20 | public static final String SCENARIO = "scenario";
21 | public static final String SCENARIO_OUTLINE = "scenario-outline";
22 |
23 | private final Gson delegate;
24 |
25 | public StepContainerDeserializer(Gson delegate) {
26 | this.delegate = delegate;
27 | }
28 |
29 | @Override
30 | public StepContainer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
31 | JsonPrimitive typeAsJson = json.getAsJsonObject().getAsJsonPrimitive(TYPE);
32 | if (typeAsJson != null) {
33 | String type = typeAsJson.getAsString();
34 | if (type.equals(SCENARIO))
35 | return delegate.fromJson(json, ScenarioExec.class);
36 | else if (type.equals(SCENARIO_OUTLINE))
37 | return delegate.fromJson(json, ScenarioOutlineExec.class);
38 | }
39 |
40 | // fallback?
41 | return delegate.fromJson(json, ScenarioExec.class);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/java/ClassEntry.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java;
2 |
3 | import com.google.common.base.Predicate;
4 | import com.google.common.collect.FluentIterable;
5 | import com.google.common.collect.Lists;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * @author @aloyer
11 | */
12 | public class ClassEntry extends Describable {
13 | private final String packageName;
14 | private final String name;
15 | private final List methodEntries;
16 |
17 | public ClassEntry(String packageName, String name) {
18 | this.packageName = packageName;
19 | this.name = name;
20 | this.methodEntries = Lists.newArrayList();
21 | }
22 |
23 | public String name() {
24 | return name;
25 | }
26 |
27 | public String packageName() {
28 | return packageName;
29 | }
30 |
31 | public String qualifiedName() {
32 | return packageName() + '.' + name();
33 | }
34 |
35 |
36 | public void mergeClass(ClassEntry other) {
37 | this.describeWith(other.comment());
38 | }
39 |
40 | public void declareEntry(MethodEntry methodEntry) {
41 | methodEntries.add(methodEntry);
42 | }
43 |
44 | public boolean hasEntries() {
45 | return !methodEntries.isEmpty();
46 | }
47 |
48 | public FluentIterable methods() {
49 | return FluentIterable.from(methodEntries);
50 | }
51 |
52 | public FluentIterable matchingEntries(final String text) {
53 | return methods().filter(new Predicate() {
54 | @Override
55 | public boolean apply(MethodEntry methodEntry) {
56 | return methodEntry.matches(text);
57 | }
58 | });
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/frontend/user_login.feature:
--------------------------------------------------------------------------------
1 | @users
2 | Feature: Sign in to the store
3 | In order to view my orders list
4 | As a visitor
5 | I need to be able to log in to the store
6 |
7 | Background:
8 | Given there are following users:
9 | | email | password | enabled |
10 | | bar@foo.com | foo | yes |
11 |
12 | Scenario: Log in with username and password
13 | Given I am on the store homepage
14 | And I follow "Login"
15 | When I fill in the following:
16 | | Email | bar@foo.com |
17 | | Password | foo |
18 | And I press "Login"
19 | Then I should be on the store homepage
20 | And I should see "Logout"
21 |
22 | Scenario: Log in with bad credentials
23 | Given I am on the store homepage
24 | And I follow "Login"
25 | When I fill in the following:
26 | | Email | bar@foo.com |
27 | | Password | bar |
28 | And I press "Login"
29 | Then I should be on login page
30 | And I should see "Bad credentials"
31 |
32 | Scenario: Trying to login without credentials
33 | Given I am on the store homepage
34 | And I follow "Login"
35 | When I press "Login"
36 | Then I should be on login page
37 | And I should see "Bad credentials"
38 |
39 | Scenario: Trying to login as non existing user
40 | Given I am on the store homepage
41 | And I follow "Login"
42 | When I fill in the following:
43 | | Email | john |
44 | | Password | bar |
45 | And I press "Login"
46 | Then I should be on login page
47 | And I should see "Bad credentials"
48 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/test/java/samples/coffeemachine/CoffeeMachineDrinkTagCheckTest.java:
--------------------------------------------------------------------------------
1 | package samples.coffeemachine;
2 |
3 | import static tzatziki.junit.SanityTagChecker.loadFeaturesFromSourceDirectory;
4 |
5 | import java.io.File;
6 | import java.util.EnumSet;
7 | import java.util.Set;
8 |
9 | import org.junit.runner.RunWith;
10 |
11 | import samples.TestSettings;
12 | import tzatziki.analysis.check.CheckAtLeastOneTagsExist;
13 | import tzatziki.analysis.check.CucumberPart;
14 | import tzatziki.analysis.check.TagChecker;
15 | import tzatziki.analysis.step.Features;
16 | import tzatziki.analysis.tag.TagDictionary;
17 | import tzatziki.junit.SanityTagChecker;
18 |
19 | /**
20 | * @author @aloyer
21 | */
22 | @RunWith(SanityTagChecker.class)
23 | public class CoffeeMachineDrinkTagCheckTest {
24 |
25 | @SanityTagChecker.TagDictionaryProvider
26 | public static TagDictionary tagDictionary() {
27 | return new TagDictionary()
28 | .declareTag("@coffee")
29 | .declareTag("@tea")
30 | .declareTag("@chocolate")
31 | .declareTag("@orangeJuice")
32 | .declareTag("@noDrink")
33 | ;
34 | }
35 |
36 | @SanityTagChecker.FeaturesProvider
37 | public static Features features() {
38 | String basedir = new TestSettings().getBaseDir();
39 | return loadFeaturesFromSourceDirectory(new File(basedir, "src/main/resources/samples/coffeemachine"));
40 | }
41 |
42 | @SanityTagChecker.TagCheckerProvider
43 | public static TagChecker checker() {
44 | return new CheckAtLeastOneTagsExist();
45 | }
46 |
47 | @SanityTagChecker.CheckScopeProvider
48 | public static Set scope() {
49 | return EnumSet.of(CucumberPart.Scenario);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/java/ConsoleOutputListener.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java;
2 |
3 | import com.thoughtworks.qdox.model.JavaClass;
4 | import com.thoughtworks.qdox.model.JavaMethod;
5 | import com.thoughtworks.qdox.model.JavaPackage;
6 |
7 | import java.util.Collection;
8 |
9 | /**
10 | * @author @aloyer
11 | */
12 | public class ConsoleOutputListener implements GrammarParserListener {
13 | @Override
14 | public void aboutToParsePackages(Collection packages) {
15 | System.out.println("ConsoleOutputListener.aboutToParsePackages: " + packages);
16 | }
17 |
18 | @Override
19 | public void aboutToParseClasses(Collection classes) {
20 | System.out.println("ConsoleOutputListener.aboutToParseClasses: " + classes);
21 | }
22 |
23 | @Override
24 | public void enteringPackage(JavaPackage pkg) {
25 | System.out.println("ConsoleOutputListener.enteringPackage: " + pkg);
26 | }
27 |
28 | @Override
29 | public void exitingPackage(JavaPackage pkg) {
30 | System.out.println("ConsoleOutputListener.exitingPackage: " + pkg);
31 | }
32 |
33 | @Override
34 | public void enteringClass(JavaClass klazz) {
35 | System.out.println("ConsoleOutputListener.enteringClass: " + klazz);
36 | }
37 |
38 | @Override
39 | public void exitingClass(JavaClass klazz) {
40 | System.out.println("ConsoleOutputListener.exitingClass: " + klazz);
41 | }
42 |
43 | @Override
44 | public void enteringMethod(JavaMethod method) {
45 | System.out.println("ConsoleOutputListener.enteringMethod: " + method);
46 | }
47 |
48 | @Override
49 | public void exitingMethod(JavaMethod method) {
50 | System.out.println("ConsoleOutputListener.exitingMethod: " + method);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/resources/tzatziki/junit/coffeemachine/01-making-drinks.feature:
--------------------------------------------------------------------------------
1 | Feature: Making Drinks
2 |
3 | #
4 | # **In order** to send commands to the drink maker
5 | #
6 | # **As a** developer
7 | #
8 | # **I want to** implement the logic that translates orders
9 | # from customers of the coffee machine to the drink maker
10 | #
11 | #
12 | #
13 | #
14 | #
15 | #The code will use the drink maker protocol (see below) to send commands to the drink maker.
16 | #
17 | #The coffee machine can serves 3 type of drinks:
18 | #
19 | # * tea,
20 | # * coffee,
21 | # * chocolate.
22 | #
23 | #
24 | #[ asciidiag ]
25 | #----
26 | #
27 | # /---------+ +------------+
28 | # | Order |---->| Protocol |
29 | # +---------/ +------------+
30 | #
31 | #----
32 |
33 | @takeOrder @wip @tea @sugar @protocol
34 | Scenario: A tea with 1 sugar and a stick
35 |
36 | When I order a "Tea" with 1 sugar
37 | Then the instruction generated should be "T:1:0"
38 |
39 | @takeOrder @chocolate @noSugar @protocol
40 | Scenario: A chocolate with no sugar - and therefore no stick
41 |
42 | When I order a "Chocolate" with 0 sugar
43 | Then the instruction generated should be "H::"
44 |
45 | @takeOrder @coffee @sugar @protocol
46 | Scenario: A tea with 1 sugar and a stick
47 |
48 | When I order a "Coffee" with 2 sugar
49 | Then the instruction generated should be "C:2:0"
50 |
51 | @message @protocol
52 | Scenario Outline: any message received is forwarded for the customer to see
53 | When the message "" is sent
54 | Then the instruction generated should be ""
55 |
56 | Examples:
57 | | message | expected |
58 | | Hello | M:Hello |
59 | | Not enough money | M:Not enough money |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/java/tzatziki/analysis/exec/gson/JsonIOTest.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.gson;
2 |
3 | import com.google.gson.Gson;
4 | import junitparams.JUnitParamsRunner;
5 | import junitparams.Parameters;
6 | import org.apache.commons.io.IOUtils;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 | import org.skyscreamer.jsonassert.JSONAssert;
11 | import tzatziki.analysis.exec.model.FeatureExec;
12 |
13 | import java.io.InputStream;
14 | import java.util.List;
15 |
16 | @RunWith(JUnitParamsRunner.class)
17 | public class JsonIOTest {
18 |
19 | private JsonIO jsonIO;
20 | private Gson gson;
21 |
22 | @Before
23 | public void setUp() {
24 | jsonIO = new JsonIO();
25 | gson = jsonIO.createGson();
26 | }
27 |
28 | @Test
29 | @Parameters({
30 | "/tzatziki/analysis/exec/gson/coffeemachine-exec.json"})
31 | public void readAndWrite_should_lead_to_identity(String resource) throws Exception {
32 | InputStream in = getClass().getResourceAsStream(resource);
33 | String origin = IOUtils.toString(in, "UTF8");
34 |
35 | in = getClass().getResourceAsStream(resource);
36 | List featureExecs = jsonIO.load(in);
37 |
38 | String json = toJson(gson, featureExecs).toString();
39 | JSONAssert.assertEquals(origin, json, false);
40 | }
41 |
42 | private static StringBuilder toJson(Gson gson, List featureExecs) {
43 | StringBuilder json = new StringBuilder();
44 | json.append("{\"features\": [\n");
45 | for (int i = 0; i < featureExecs.size(); i++) {
46 | if (i > 0)
47 | json.append(",\n");
48 | FeatureExec f = featureExecs.get(i);
49 | json.append(gson.toJson(f));
50 | }
51 | json.append("]\n}");
52 | return json;
53 | }
54 | }
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/frontend/checkout_addressing.feature:
--------------------------------------------------------------------------------
1 | @checkout
2 | Feature: Checkout addressing
3 | In order to select billing and shipping addresses
4 | As a visitor
5 | I want to proceed through addressing checkout step
6 |
7 | Background:
8 | Given there are following taxonomies defined:
9 | | name |
10 | | Category |
11 | And taxonomy "Category" has following taxons:
12 | | Clothing > PHP T-Shirts |
13 | And the following products exist:
14 | | name | price | taxons |
15 | | PHP Top | 5.99 | PHP T-Shirts |
16 | And there are following users:
17 | | email | password | enabled |
18 | | john@example.com | foo | yes |
19 | | rick@example.com | bar | yes |
20 | And I am logged in user
21 | And there are following countries:
22 | | name |
23 | | USA |
24 | | United Kingdom |
25 | | Poland |
26 | | Germany |
27 |
28 | Scenario: Filling the shipping address
29 | Given I added product "PHP Top" to cart
30 | When I go to the checkout start page
31 | And I fill in the shipping address to United Kingdom
32 | And I press "Continue"
33 | Then I should be on the checkout shipping step
34 |
35 | Scenario: Using different billing address
36 | Given I added product "PHP Top" to cart
37 | When I go to the checkout start page
38 | And I fill in the shipping address to Germany
39 | But I check "Use different address for billing?"
40 | And I fill in the billing address to USA
41 | And I press "Continue"
42 | Then I should be on the checkout shipping step
43 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/java/tzatziki/util/MemoizableIteratorTest.java:
--------------------------------------------------------------------------------
1 | package tzatziki.util;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.Arrays;
6 | import java.util.List;
7 | import java.util.NoSuchElementException;
8 |
9 | import static org.assertj.core.api.Assertions.assertThat;
10 |
11 | public class MemoizableIteratorTest {
12 |
13 | @Test
14 | public void usecase () {
15 | List elements = Arrays.asList("one", "two", "three");
16 | MemoizableIterator it = new MemoizableIterator(elements.iterator());
17 | assertThat(it.hasNext()).isTrue();
18 | assertThat(it.next()).isEqualTo("one");
19 | assertThat(it.current()).isEqualTo("one");
20 |
21 | assertThat(it.hasNext()).isTrue();
22 | assertThat(it.next()).isEqualTo("two");
23 | assertThat(it.current()).isEqualTo("two");
24 | assertThat(it.current()).isEqualTo("two");
25 |
26 | assertThat(it.hasNext()).isTrue();
27 | assertThat(it.next()).isEqualTo("three");
28 | assertThat(it.current()).isEqualTo("three");
29 | assertThat(it.current()).isEqualTo("three");
30 |
31 | assertThat(it.hasNext()).isFalse();
32 | assertThat(it.current()).isEqualTo("three");
33 | }
34 |
35 | @Test(expected = NoSuchElementException.class)
36 | public void next_should_throw_when_no_more_element__initial_empty_case () {
37 | List elements = Arrays.asList();
38 | MemoizableIterator it = new MemoizableIterator(elements.iterator());
39 | it.next();
40 | }
41 |
42 | @Test(expected = IllegalStateException.class)
43 | public void current_should_throw__when_no_more_elements () {
44 | List elements = Arrays.asList();
45 | MemoizableIterator it = new MemoizableIterator(elements.iterator());
46 | it.current();
47 | }
48 |
49 | }
--------------------------------------------------------------------------------
/tzatziki-pdf/src/main/java/tzatziki/pdf/emitter/StatusMarker.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf.emitter;
2 |
3 | import com.itextpdf.text.BaseColor;
4 | import com.itextpdf.text.Chunk;
5 | import com.itextpdf.text.DocumentException;
6 | import gutenberg.itext.FontAwesomeAdapter;
7 | import tzatziki.analysis.exec.model.Status;
8 |
9 | import java.io.IOException;
10 |
11 | /**
12 | * @author @aloyer
13 | */
14 | public class StatusMarker {
15 | private FontAwesomeAdapter fontAwesomeAdapter;
16 | private float symbolSize = 12;
17 |
18 | public Chunk statusMarker(Status status) {
19 | switch (status) {
20 | case Passed:
21 | return fontAwesomeAdapter().symbol("check-circle", symbolSize, BaseColor.GREEN.darker());
22 | case Skipped:
23 | return fontAwesomeAdapter().symbol("exclamation-circle", symbolSize, BaseColor.ORANGE);
24 | case Undefined:
25 | return fontAwesomeAdapter().symbol("question-circle", symbolSize, BaseColor.RED.darker());
26 | case Failed:
27 | return fontAwesomeAdapter().symbol("ban", symbolSize, BaseColor.RED);
28 | case Pending:
29 | return fontAwesomeAdapter().symbol("gears", symbolSize, BaseColor.ORANGE);
30 | default:
31 | return fontAwesomeAdapter().symbol("minus-circle", symbolSize, BaseColor.BLUE);
32 | }
33 | }
34 |
35 | private FontAwesomeAdapter fontAwesomeAdapter() {
36 | if (fontAwesomeAdapter == null)
37 | try {
38 | fontAwesomeAdapter = new FontAwesomeAdapter();
39 | } catch (IOException e) {
40 | throw new RuntimeException(e);
41 | } catch (DocumentException e) {
42 | throw new RuntimeException(e);
43 | }
44 | return fontAwesomeAdapter;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/tag/TagFilter.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.tag;
2 |
3 | import com.google.common.base.Function;
4 | import com.google.common.base.Predicate;
5 | import com.google.common.base.Predicates;
6 | import com.google.common.collect.FluentIterable;
7 |
8 | import static java.util.Arrays.asList;
9 |
10 | /**
11 | * @author @aloyer
12 | */
13 | public class TagFilter implements Predicate {
14 |
15 | public static TagFilter AcceptAll = new TagFilter(Predicates.alwaysTrue());
16 |
17 | public static TagFilter from(String... tagExprs) {
18 | return new TagFilter(new TagExpressionPredicate(asList(tagExprs)));
19 | }
20 |
21 | private final Predicate delegate;
22 |
23 | private TagFilter(Predicate delegate) {
24 | this.delegate = delegate;
25 | }
26 |
27 | @Override
28 | public boolean apply(Tags tags) {
29 | return delegate.apply(tags);
30 | }
31 |
32 | public TagFilter and(TagFilter... tagFilters) {
33 | Iterable> ands = extractPredicates(tagFilters);
34 | return new TagFilter(Predicates.and(ands));
35 | }
36 |
37 | public TagFilter or(TagFilter... tagFilters) {
38 | Iterable> ors = extractPredicates(tagFilters);
39 | return new TagFilter(Predicates.or(ors));
40 | }
41 |
42 | private static FluentIterable> extractPredicates(TagFilter[] tagFilters) {
43 | return FluentIterable.from(asList(tagFilters)).transform(tagFilterPredicateLens);
44 | }
45 |
46 | private static final Function> tagFilterPredicateLens = new Function>() {
47 | @Override
48 | public Predicate apply(TagFilter filter) {
49 | return filter.delegate;
50 | }
51 | };
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/backend/products_filter.feature:
--------------------------------------------------------------------------------
1 | @products
2 | Feature: Products filter
3 | In order to easily find products
4 | As a store owner
5 | I want to be able to filter list by name
6 |
7 | Background:
8 | Given I am logged in as administrator
9 | And the following products exist:
10 | | name | price | sku |
11 | | Super T-Shirt | 19.99 | 123 |
12 | | Black T-Shirt | 19.99 | 321 |
13 | | Mug | 5.99 | 136 |
14 | | Sticker | 10.00 | 555 |
15 | | Banana | 10.00 | 999 |
16 | | Orange | 10.00 | 124 |
17 |
18 | Scenario: Filtering products by name
19 | Given I am on the product index page
20 | When I fill in "Name" with "T-Shirt"
21 | And I press "Filter"
22 | Then I should be on the product index page
23 | And I should see 2 products in the list
24 |
25 | Scenario: Not matching products are not in the list
26 | Given I am on the product index page
27 | When I fill in "Name" with "T-Shirt"
28 | And I press "Filter"
29 | Then I should be on the product index page
30 | And I should not see "Orange"
31 | But I should see "Black T-Shirt"
32 |
33 | Scenario: Filtering products by SKU
34 | Given I am on the product index page
35 | When I fill in "SKU" with "123"
36 | And I press "Filter"
37 | Then I should be on the product index page
38 | And I should see 1 product in the list
39 |
40 | Scenario: Products with not matching SKU are filtered
41 | Given I am on the product index page
42 | When I fill in "SKU" with "555"
43 | And I press "Filter"
44 | Then I should be on the product index page
45 | And I should see "Sticker"
46 | But I should not see "T-Shirt"
47 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/frontend/account_password.feature:
--------------------------------------------------------------------------------
1 | @account
2 | Feature: User account password change
3 | In order to enhance the security of my account
4 | As a logged user
5 | I want to be able to change password
6 |
7 | Background:
8 | Given I am logged in user
9 | And I am on my account homepage
10 |
11 | Scenario: Viewing my password change page
12 | Given I follow "My password"
13 | Then I should be on my account password page
14 |
15 | Scenario: Changing my password with a wrong current password
16 | Given I am on my account password page
17 | When I fill in "Current password" with "wrongpassword"
18 | And I fill in "New password" with "newpassword"
19 | And I fill in "Confirmation" with "newpassword"
20 | And I press "Save changes"
21 | Then I should still be on my account password page
22 | And I should see "This value should be the user current password"
23 |
24 | Scenario: Changing my password with a wrong confirmation password
25 | Given I am on my account password page
26 | When I fill in "Current password" with "sylius"
27 | And I fill in "New password" with "newpassword"
28 | And I fill in "Confirmation" with "wrongnewpassword"
29 | And I press "Save changes"
30 | Then I should still be on my account password page
31 | And I should see "The entered passwords don't match"
32 |
33 | Scenario: Successfully changing my password
34 | Given I am on my account password page
35 | When I fill in "Current password" with "sylius"
36 | And I fill in "New password" with "newpassword"
37 | And I fill in "Confirmation" with "newpassword"
38 | And I press "Save changes"
39 | Then I should be on my account profile page
40 | And I should see "The password has been changed"
41 |
42 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/frontend/checkout_taxation.feature:
--------------------------------------------------------------------------------
1 | @checkout
2 | Feature: Checkout taxation
3 | In order to handle product taxation
4 | As a store owner
5 | I want to apply taxes during checkout
6 |
7 | Background:
8 | Given there are following taxonomies defined:
9 | | name |
10 | | Category |
11 | And taxonomy "Category" has following taxons:
12 | | Clothing > PHP T-Shirts |
13 | And the following zones are defined:
14 | | name | type | members |
15 | | UK | country | United Kingdom |
16 | And there are following tax categories:
17 | | name |
18 | | Taxable Goods |
19 | And the following tax rates exist:
20 | | category | zone | name | amount |
21 | | Taxable Goods | UK | UK Tax | 15% |
22 | And the following products exist:
23 | | name | price | taxons | tax category |
24 | | PHP Top | 250 | PHP T-Shirts | Taxable Goods |
25 | And the following shipping methods exist:
26 | | zone | name |
27 | | UK | DHL Express |
28 | And the following payment methods exist:
29 | | name | gateway | enabled |
30 | | Dummy | dummy | yes |
31 | And I am logged in user
32 | And I added product "PHP Top" to cart
33 | And I go to the checkout start page
34 |
35 | Scenario: Placing the order
36 | Given I fill in the shipping address to United Kingdom
37 | And I press "Continue"
38 | And I select the "DHL Express" radio button
39 | And I press "Continue"
40 | And I select the "Dummy" radio button
41 | When I press "Continue"
42 | Then I should be on the checkout finalize step
43 | And "Tax total: €37.50" should appear on the page
44 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/coffeemachine/04-making-money.feature:
--------------------------------------------------------------------------------
1 | Feature: Making Money
2 |
3 | #
4 | # **In order to** have daily reports of what is sold and when
5 | #
6 | # **As a** shop keeper
7 | #
8 | # **I want to** track statistics of machine usage
9 | #
10 | #```formula
11 | # {\eta \leq C(\delta(\eta) +\Lambda_M(0,\delta))
12 | #```
13 | #
14 | #```formula
15 | # \Re{z} =\frac{n\pi \dfrac{\theta +\psi}{2}}{
16 | # \left(\dfrac{\theta +\psi}{2}\right)^2 + \left( \dfrac{1}{2}
17 | # \log \left\vert\dfrac{B}{A}\right\vert\right)^2}.
18 | #```
19 | #
20 |
21 | @reporting @tea @coffee @chocolate
22 | Scenario: Statistics collect basic usage
23 |
24 | Given the following orders:
25 | | time | drink |
26 | | 08:05:23 | Coffee |
27 | | 08:06:43 | Coffee |
28 | | 08:10:23 | Coffee |
29 | | 08:45:03 | Tea |
30 | | 10:05:47 | Coffee |
31 | | 10:05:47 | Chocolate |
32 | When I query for a report
33 | Then the report output should be
34 | """
35 | chocolate: 1
36 | coffee: 4
37 | tea: 1
38 | ---
39 | Total: 3.00€
40 | """
41 |
42 | @reporting @noDrink
43 | Scenario: Statistics collect no usage
44 |
45 | #```ditaa
46 | #
47 | # /---------------+-------------\
48 | # |cRED Chocolate |cBLU 1 |-----\
49 | # +---------------+-------------+ :
50 | # |cGRE Coffee |cPNK 4 | |
51 | # +---------------+-------------+ |
52 | # |cAAA Total |<----/
53 | # +-----------------------------+ /-------\
54 | # |cCCC 3.00€ |---+---->| DB |
55 | # | +-------------+ | {s}|
56 | # | |cYEL YEL | \-------/
57 | # \---------------+-------------/
58 | #
59 | #```
60 |
61 | When I query for a report
62 | Then the report output should be
63 | """
64 | ---
65 | Total: 0.00€
66 | """
--------------------------------------------------------------------------------
/tzatziki-core/src/test/resources/tzatziki/analysis/step/going-into-business.feature:
--------------------------------------------------------------------------------
1 | Feature: Going into business
2 |
3 | #
4 | # **In order to** goes into Business
5 | #
6 | # **As a** shopkeeper
7 | #
8 | # **I want to** ensure The coffee machine is not free anymore!
9 | #
10 | #
11 | #
12 | #
13 | #
14 |
15 | @Payment
16 | Scenario: A tea with just enough money
17 |
18 | The drink maker should make the drinks only if the correct amount of money is given
19 |
20 | Given I've inserted 0.40€ in the machine
21 | When I order a "Tea" with 1 sugar
22 | Then the instruction generated should be "T:1:0"
23 |
24 | @Payment
25 | Scenario: A tea with not enough money
26 |
27 | If not enough money is provided, we want to send a message to the drink maker.
28 | The message should contains at least the amount of money missing.
29 |
30 | Given I've inserted 0.30€ in the machine
31 | When I order a "Tea" with 1 sugar
32 | Then the instruction generated should be "M:Not enough money 0.10 missing"
33 |
34 | @Payment
35 | Scenario: A coffee with more than required money
36 |
37 | If too much money is given, the drink maker will still make the drink according
38 | to the instructions. The machine will handle the return of the correct change.
39 |
40 | Given I've inserted 2€ in the machine
41 | When I order a "Coffee" with 0 sugar
42 | Then the instruction generated should be "C::"
43 |
44 | @Payment
45 | Scenario Outline: Check missing money
46 |
47 | Given I've inserted € in the machine
48 | When I order a "" with sugar
49 | Then the instruction generated should be ""
50 |
51 | Examples:
52 | | money | drink | n | instruction |
53 | | 0.25 | Coffee | 0 | M:Not enough money 0.25 missing |
54 | | 0.55 | Chocolate | 0 | M:Not enough money 0.05 missing |
55 | | 0.05 | Tea | 1 | M:Not enough money 0.35 missing |
56 |
57 |
--------------------------------------------------------------------------------
/tzatziki-pdf/src/main/java/tzatziki/pdf/emitter/ScenarioEmitter.java:
--------------------------------------------------------------------------------
1 | package tzatziki.pdf.emitter;
2 |
3 | import gutenberg.itext.Emitter;
4 | import gutenberg.itext.ITextContext;
5 | import gutenberg.itext.Sections;
6 | import gutenberg.util.KeyValues;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import tzatziki.analysis.exec.model.ScenarioExec;
10 |
11 | /**
12 | * @author @aloyer
13 | */
14 | public class ScenarioEmitter implements Emitter {
15 |
16 | public static final String DISPLAY_TAGS = "scenario-display-tags";
17 | private final int hLevel;
18 | private StepContainerEmitter stepsEmitter;
19 |
20 | private Logger log = LoggerFactory.getLogger(ScenarioEmitter.class);
21 |
22 | public ScenarioEmitter() {
23 | this(2);
24 | }
25 |
26 | public ScenarioEmitter(int hLevel) {
27 | this(hLevel, new StepContainerEmitter());
28 | }
29 |
30 | public ScenarioEmitter(int hLevel, StepContainerEmitter stepsEmitter) {
31 | this.hLevel = hLevel;
32 | this.stepsEmitter = stepsEmitter;
33 | }
34 |
35 | @Override
36 | public void emit(ScenarioExec scenario, ITextContext emitterContext) {
37 | Sections sections = emitterContext.sections();
38 | KeyValues kvs = emitterContext.keyValues();
39 |
40 | Integer rawOffset = kvs.getInteger(FeatureEmitter.FEATURE_HEADER_LEVEL_OFFSET).or(0);
41 | int headerLevel = hLevel + rawOffset;
42 |
43 | sections.newSection(scenario.name(), headerLevel);
44 | try {
45 | if (kvs.getBoolean(DISPLAY_TAGS, true)) {
46 | stepsEmitter.emitTags(scenario, emitterContext);
47 | }
48 | stepsEmitter.emitDescription(scenario, emitterContext);
49 | stepsEmitter.emitEmbeddings(scenario, emitterContext);
50 | stepsEmitter.emitSteps(scenario, emitterContext);
51 | } finally {
52 | sections.leaveSection(headerLevel); // end-of-scenario
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/resources/tzatziki/junit/coffeemachine/04-making-money.feature:
--------------------------------------------------------------------------------
1 | Feature: Making Money
2 |
3 | #
4 | # **In order to** have daily reports of what is sold and when
5 | #
6 | # **As a** shop keeper
7 | #
8 | # **I want to** track statistics of machine usage
9 | #
10 | # [formula]
11 | # ----
12 | # {\eta \leq C(\delta(\eta) +\Lambda_M(0,\delta))
13 | # ----
14 | #
15 | # {% formula %}
16 | #
17 | # \Re{z} =\frac{n\pi \dfrac{\theta +\psi}{2}}{
18 | # \left(\dfrac{\theta +\psi}{2}\right)^2 + \left( \dfrac{1}{2}
19 | # \log \left\vert\dfrac{B}{A}\right\vert\right)^2}.
20 | #
21 | # {% formula %}
22 | #
23 | #
24 |
25 | @reporting @tea @coffee @chocolate
26 | Scenario: Statistics collect basic usage
27 |
28 | Given the following orders:
29 | | time | drink |
30 | | 08:05:23 | Coffee |
31 | | 08:06:43 | Coffee |
32 | | 08:10:23 | Coffee |
33 | | 08:45:03 | Tea |
34 | | 10:05:47 | Coffee |
35 | | 10:05:47 | Chocolate |
36 | When I query for a report
37 | Then the report output should be
38 | """
39 | chocolate: 1
40 | coffee: 4
41 | tea: 1
42 | ---
43 | Total: 3.00€
44 | """
45 |
46 | @reporting @noDrink
47 | Scenario: Statistics collect no usage
48 |
49 | # [asciidiag]
50 | # ----
51 | # /---------------+-------------\
52 | # |cRED Chocolate |cBLU 1 |-----\
53 | # +---------------+-------------+ :
54 | # |cGRE Coffee |cPNK 4 | |
55 | # +---------------+-------------+ |
56 | # |cAAA Total |<----/
57 | # +-----------------------------+ /-------\
58 | # |cCCC 3.00€ |---+---->| DB |
59 | # | +-------------+ | {s}|
60 | # | |cYEL YEL | \-------/
61 | # \---------------+-------------/
62 | # ----
63 | #
64 |
65 | When I query for a report
66 | Then the report output should be
67 | """
68 | ---
69 | Total: 0.00€
70 | """
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/step/TagCollector.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.step;
2 |
3 | import com.google.common.collect.Maps;
4 |
5 | import java.util.Map;
6 |
7 | /**
8 | * @author @aloyer
9 | */
10 | public class TagCollector extends FeatureVisitorAdapter {
11 |
12 | private Map allTags = Maps.newConcurrentMap();
13 | private Feature feature;
14 |
15 | @Override
16 | public void enterFeature(Feature feature) {
17 | this.feature = feature;
18 | for(String tag : feature.getTags()) {
19 | Stats stats = statsFor(tag);
20 | stats.featureTagged++;
21 | }
22 | }
23 |
24 | @Override
25 | public void enterScenario(Scenario scenario) {
26 | for(String tag : scenario.getTags()) {
27 | Stats stats = statsFor(tag);
28 | stats.scenarioTagged++;
29 | }
30 | for(String tag : feature.getTags()) {
31 | Stats stats = statsFor(tag);
32 | stats.scenarioTaggedByInheritance++;
33 | }
34 | }
35 |
36 | @Override
37 | public void enterScenarioOutline(ScenarioOutline scenario) {
38 | for(String tag : scenario.getTags()) {
39 | Stats stats = statsFor(tag);
40 | stats.scenarioOutlineTagged++;
41 | }
42 | for(String tag : feature.getTags()) {
43 | Stats stats = statsFor(tag);
44 | stats.scenarioOutlineTaggedByInheritance++;
45 | }
46 | }
47 |
48 | private Stats statsFor(String tag) {
49 | Stats stats = allTags.get(tag);
50 | if(stats == null) {
51 | stats = new Stats();
52 | allTags.put(tag, stats);
53 | }
54 | return stats;
55 | }
56 |
57 | public static class Stats {
58 | public int featureTagged = 0;
59 | public int scenarioTagged = 0;
60 | public int scenarioTaggedByInheritance = 0;
61 | public int scenarioOutlineTagged = 0;
62 | public int scenarioOutlineTaggedByInheritance = 0;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/frontend/checkout_payment.feature:
--------------------------------------------------------------------------------
1 | @checkout
2 | Feature: Checkout Payment
3 | In order to submit a payment
4 | As a logged in user
5 | I want to be able to use checkout payment step
6 |
7 | Background:
8 | Given there are following taxonomies defined:
9 | | name |
10 | | Category |
11 | And taxonomy "Category" has following taxons:
12 | | Clothing > PHP T-Shirts |
13 | And the following products exist:
14 | | name | price | taxons |
15 | | PHP Top | 5.99 | PHP T-Shirts |
16 | And the following zones are defined:
17 | | name | type | members |
18 | | UK | country | United Kingdom |
19 | And the following shipping methods exist:
20 | | zone | name |
21 | | UK | DHL Express |
22 | And the following payment methods exist:
23 | | name | gateway | enabled |
24 | | Credit Card | stripe | yes |
25 | | PayPal | paypal | yes |
26 | | PayPal PRO | paypal_pro | no |
27 | And I am logged in user
28 | And I added product "PHP Top" to cart
29 | And I go to the checkout start page
30 | And I fill in the shipping address to United Kingdom
31 | And I press "Continue"
32 | And I select the "DHL Express" radio button
33 |
34 | Scenario: Accessing payment checkout step
35 | Given I press "Continue"
36 | Then I should be on the checkout payment step
37 |
38 | Scenario: Only enabled payment methods are displayed
39 | Given I press "Continue"
40 | Then I should be on the checkout payment step
41 | And I should see "PayPal"
42 | But I should not see "PayPal PRO"
43 |
44 | Scenario: Selecting one of payment methods
45 | Given I press "Continue"
46 | When I select the "PayPal" radio button
47 | And I press "Continue"
48 | Then I should be on the checkout finalize step
49 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/step/Feature.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.step;
2 |
3 | import com.google.common.collect.FluentIterable;
4 | import com.google.common.collect.Lists;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * @author @aloyer
10 | */
11 | public class Feature {
12 |
13 | private final String uri;
14 | private final String name;
15 | private List tags = Lists.newArrayList();
16 | private Background background;
17 | private List scenarios = Lists.newArrayList();
18 | private List scenarioOutlines = Lists.newArrayList();
19 |
20 | public Feature(String uri, String name) {
21 | this.uri = uri;
22 | this.name = name;
23 | }
24 |
25 | public Background background() {
26 | return background;
27 | }
28 |
29 | public void background(Background background) {
30 | this.background = background;
31 | }
32 |
33 | public void add(Scenario scenario) {
34 | scenarios.add(scenario);
35 | }
36 |
37 | public void add(ScenarioOutline scenarioOutline) {
38 | scenarioOutlines.add(scenarioOutline);
39 | }
40 |
41 | public void traverse(FeatureVisitor visitor) {
42 | visitor.enterFeature(this);
43 | for (Scenario scenario : scenarios)
44 | scenario.traverse(visitor);
45 | for (ScenarioOutline scenarioOutline : scenarioOutlines)
46 | scenarioOutline.traverse(visitor);
47 | visitor.exitFeature(this);
48 | }
49 |
50 | public String uri() {
51 | return uri;
52 | }
53 |
54 | public String name() {
55 | return name;
56 | }
57 |
58 | public void addTags(List tags) {
59 | this.tags.addAll(tags);
60 | }
61 |
62 | public List getTags() {
63 | return tags;
64 | }
65 |
66 | public FluentIterable scenario() {
67 | return FluentIterable.from(scenarios);
68 | }
69 |
70 | public FluentIterable scenarioOutlines() {
71 | return FluentIterable.from(scenarioOutlines);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/frontend/products.feature:
--------------------------------------------------------------------------------
1 | @products
2 | Feature: Products
3 | In order to know and pick the products
4 | As a visitor
5 | I want to be able to browse products
6 |
7 | Background:
8 | Given there are following taxonomies defined:
9 | | name |
10 | | Category |
11 | And taxonomy "Category" has following taxons:
12 | | Clothing > T-Shirts |
13 | | Clothing > PHP T-Shirts |
14 | | Clothing > Gloves |
15 | And the following products exist:
16 | | name | price | taxons |
17 | | Super T-Shirt | 19.99 | T-Shirts |
18 | | Black T-Shirt | 18.99 | T-Shirts |
19 | | Sylius Tee | 12.99 | PHP T-Shirts |
20 | | Symfony T-Shirt | 15.00 | PHP T-Shirts |
21 | | Doctrine T-Shirt | 15.00 | PHP T-Shirts |
22 |
23 | Scenario: Browsing products by taxon
24 | Given I am on the store homepage
25 | When I follow "T-Shirts"
26 | Then I should see there 2 products
27 | And I should see "Black T-Shirt"
28 |
29 | Scenario: Browsing products by taxon
30 | Given I am on the store homepage
31 | When I follow "PHP T-Shirts"
32 | Then I should see there 3 products
33 | And I should see "Sylius Tee"
34 |
35 | Scenario: Empty index of products
36 | Given there are no products
37 | And I am on the store homepage
38 | When I follow "Gloves"
39 | Then I should see "There are no products to display"
40 |
41 | Scenario: Accessing product page via "View more" button
42 | Given I am on the store homepage
43 | And I follow "T-Shirts"
44 | When I click "View more"
45 | Then I should be on the product page for "Super T-Shirt"
46 |
47 | Scenario: Accessing product page via title
48 | Given I am on the store homepage
49 | And I follow "PHP T-Shirts"
50 | When I click "Symfony T-Shirt"
51 | Then I should be on the product page for "Symfony T-Shirt"
52 |
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/stylius/frontend/cart_promotions_dates.feature:
--------------------------------------------------------------------------------
1 | @promotions
2 | Feature: Checkout limited time promotions
3 | In order to handle product promotions
4 | As a store owner
5 | I want to apply promotion discounts during checkout
6 |
7 | Background:
8 | Given the following promotions exist:
9 | | name | description | starts | ends |
10 | | Decade | 20 EUR off for this decade | 2013-01-01 | 2023-01-01 |
11 | | Too late | too late to get this discount | | 2013-01-01 |
12 | | Too soon | too soon to get this discount | 2023-01-01 | |
13 | And promotion "Decade" has following actions defined:
14 | | type | configuration |
15 | | Fixed discount | Amount: 20 |
16 | And promotion "Too late" has following actions defined:
17 | | type | configuration |
18 | | Fixed discount | Amount: 30 |
19 | And promotion "Too soon" has following actions defined:
20 | | type | configuration |
21 | | Fixed discount | Amount: 40 |
22 | And there are following taxonomies defined:
23 | | name |
24 | | Category |
25 | And taxonomy "Category" has following taxons:
26 | | Clothing > Debian T-Shirts |
27 | And the following products exist:
28 | | name | price | taxons |
29 | | Buzz | 500 | Debian T-Shirts |
30 | | Potato | 200 | Debian T-Shirts |
31 | | Woody | 125 | Debian T-Shirts |
32 | | Sarge | 25 | Debian T-Shirts |
33 | | Etch | 20 | Debian T-Shirts |
34 | | Lenny | 15 | Debian T-Shirts |
35 |
36 | Scenario: Promotion is applied when the order date corresponds
37 | with promotion dates
38 | Given I am on the store homepage
39 | When I added product "Sarge" to cart, with quantity "8"
40 | Then I should be on the cart summary page
41 | And "Promotion total: (€20.00)" should appear on the page
42 | And "Grand total: €180.00" should appear on the page
--------------------------------------------------------------------------------
/tzatziki-samples/src/main/resources/samples/coffeemachine/02-going-into-business.feature:
--------------------------------------------------------------------------------
1 | Feature: Going into business
2 |
3 | #
4 | # **In order to** goes into Business
5 | #
6 | # **As a** shopkeeper
7 | #
8 | # **I want to** ensure The coffee machine is not free anymore!
9 | #
10 | #
11 | #
12 | #
13 | #
14 |
15 | @payment @takeOrder @tea @sugar @protocol
16 | Scenario: A tea with just enough money
17 |
18 | The drink maker should make the drinks only if the correct amount of money is given
19 |
20 | Given I've inserted 0.40€ in the machine
21 | When I order a "Tea" with 1 sugar
22 | Then the instruction generated should be "T:1:0"
23 |
24 | @payment @takeOrder @tea @sugar @message @notEnoughMoney
25 | Scenario: A tea with not enough money
26 |
27 | If not enough money is provided, we want to send a message to the drink maker.
28 | The message should contains at least the amount of money missing.
29 |
30 | Given I've inserted 0.30€ in the machine
31 | When I order a "Tea" with 1 sugar
32 | Then the instruction generated should be "M:Not enough money 0.10 missing"
33 |
34 | @payment @takeOrder @tea @sugar @message @tooMuchMoney
35 | Scenario: A coffee with more than required money
36 |
37 | If too much money is given, the drink maker will still make the drink according
38 | to the instructions. The machine will handle the return of the correct change.
39 |
40 | Given I've inserted 2€ in the machine
41 | When I order a "Coffee" with 0 sugar
42 | Then the instruction generated should be "C::"
43 |
44 | @payment @takeOrder @tea @coffee @chocolate @message @notEnoughMoney
45 | Scenario Outline: Check missing money
46 |
47 | Given I've inserted € in the machine
48 | When I order a "" with sugar
49 | Then the instruction generated should be ""
50 |
51 | Examples:
52 | | money | drink | n | instruction |
53 | | 0.25 | Coffee | 0 | M:Not enough money 0.25 missing |
54 | | 0.55 | Chocolate | 0 | M:Not enough money 0.05 missing |
55 | | 0.05 | Tea | 1 | M:Not enough money 0.35 missing |
56 |
57 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/resources/tzatziki/junit/coffeemachine/02-going-into-business.feature:
--------------------------------------------------------------------------------
1 | Feature: Going into business
2 |
3 | #
4 | # **In order to** goes into Business
5 | #
6 | # **As a** shopkeeper
7 | #
8 | # **I want to** ensure The coffee machine is not free anymore!
9 | #
10 | #
11 | #
12 | #
13 | #
14 |
15 | @payment @takeOrder @tea @sugar @protocol
16 | Scenario: A tea with just enough money
17 |
18 | The drink maker should make the drinks only if the correct amount of money is given
19 |
20 | Given I've inserted 0.40€ in the machine
21 | When I order a "Tea" with 1 sugar
22 | Then the instruction generated should be "T:1:0"
23 |
24 | @payment @takeOrder @tea @sugar @message @notEnoughMoney
25 | Scenario: A tea with not enough money
26 |
27 | If not enough money is provided, we want to send a message to the drink maker.
28 | The message should contains at least the amount of money missing.
29 |
30 | Given I've inserted 0.30€ in the machine
31 | When I order a "Tea" with 1 sugar
32 | Then the instruction generated should be "M:Not enough money 0.10 missing"
33 |
34 | @payment @takeOrder @tea @sugar @message @tooMuchMoney
35 | Scenario: A coffee with more than required money
36 |
37 | If too much money is given, the drink maker will still make the drink according
38 | to the instructions. The machine will handle the return of the correct change.
39 |
40 | Given I've inserted 2€ in the machine
41 | When I order a "Coffee" with 0 sugar
42 | Then the instruction generated should be "C::"
43 |
44 | @payment @takeOrder @tea @coffee @chocolate @message @notEnoughMoney
45 | Scenario Outline: Check missing money
46 |
47 | Given I've inserted € in the machine
48 | When I order a "" with sugar
49 | Then the instruction generated should be ""
50 |
51 | Examples:
52 | | money | drink | n | instruction |
53 | | 0.25 | Coffee | 0 | M:Not enough money 0.25 missing |
54 | | 0.55 | Chocolate | 0 | M:Not enough money 0.05 missing |
55 | | 0.05 | Tea | 1 | M:Not enough money 0.35 missing |
56 |
57 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/java/Grammar.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java;
2 |
3 | import com.google.common.collect.FluentIterable;
4 |
5 | /**
6 | * @author @aloyer
7 | */
8 | public class Grammar {
9 |
10 | private final PackageEntry root;
11 |
12 | public Grammar() {
13 | this.root = new PackageEntry("");
14 | }
15 |
16 | public void declarePackage(PackageEntry subPkgEntry) {
17 | root.declareSubPackage(subPkgEntry);
18 | }
19 |
20 | public void declareClass(ClassEntry classEntry) {
21 | root.declareClass(classEntry);
22 | }
23 |
24 | public boolean hasEntries() {
25 | return root.hasEntries();
26 | }
27 |
28 | public FluentIterable packages() {
29 | return root.subPackages();
30 | }
31 |
32 | public FluentIterable classes() {
33 | return root.classes();
34 | }
35 |
36 | public FluentIterable matchingEntries(String text) {
37 | return root.matchingEntries(text);
38 | }
39 |
40 | public void traverse(GrammarVisitor visitor) {
41 | for (ClassEntry classEntry : classes()) {
42 | traverse(null, classEntry, visitor);
43 | }
44 | for (PackageEntry packageEntry : packages()) {
45 | traverse(packageEntry, visitor);
46 | }
47 | }
48 |
49 | private void traverse(PackageEntry packageEntry, ClassEntry classEntry, GrammarVisitor visitor) {
50 | visitor.enter(packageEntry, classEntry);
51 | for (MethodEntry methodEntry : classEntry.methods()) {
52 | visitor.visit(packageEntry, classEntry, methodEntry);
53 | }
54 | visitor.leave(packageEntry, classEntry);
55 | }
56 |
57 | private void traverse(PackageEntry packageEntry, GrammarVisitor visitor) {
58 | visitor.enter(packageEntry);
59 | for (PackageEntry subPkgEntry : packageEntry.subPackages()) {
60 | traverse(subPkgEntry, visitor);
61 | }
62 | for (ClassEntry classEntry : packageEntry.classes()) {
63 | traverse(packageEntry, classEntry, visitor);
64 | }
65 | visitor.leave(packageEntry);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/ExamplesExec.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | import com.google.common.collect.FluentIterable;
4 | import com.google.common.collect.Lists;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * @author @aloyer
10 | */
11 | public class ExamplesExec {
12 | private final String keyword;
13 | private final String name;
14 | private List tags = Lists.newArrayList();
15 | private List comments = Lists.newArrayList();
16 | private String description;
17 | private List examplesRows;
18 |
19 | public ExamplesExec(String keyword, String name) {
20 | this.keyword = keyword;
21 | this.name = name;
22 | }
23 |
24 | public ExamplesExec declareTags(List tags) {
25 | this.tags.addAll(tags);
26 | return this;
27 | }
28 |
29 | public ExamplesExec declareComments(List comments) {
30 | this.comments.addAll(comments);
31 | return this;
32 | }
33 |
34 | public ExamplesExec declareDescription(String description) {
35 | this.description = description;
36 | return this;
37 | }
38 |
39 | public ExamplesExec declareRows(List examplesRows) {
40 | this.examplesRows = examplesRows;
41 | return this;
42 | }
43 |
44 | public String name() {
45 | return name;
46 | }
47 |
48 | public FluentIterable tags() {
49 | return FluentIterable.from(tags);
50 | }
51 |
52 | public FluentIterable rows() {
53 | return FluentIterable.from(examplesRows);
54 | }
55 |
56 | public ExamplesExec recursiveCopy() {
57 | return new ExamplesExec(keyword, name)
58 | .declareTags(tags)
59 | .declareComments(comments)
60 | .declareDescription(description)
61 | .declareRows(examplesRows);
62 | }
63 |
64 | public int rowCount() {
65 | return examplesRows.size();
66 | }
67 |
68 | public int columnCount() {
69 | return examplesRows.get(0).cells().size();
70 | }
71 |
72 | public String keyword() {
73 | return keyword;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/tag/TagDictionary.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.tag;
2 |
3 | import com.google.common.collect.FluentIterable;
4 | import com.google.common.collect.Maps;
5 |
6 | import java.util.Enumeration;
7 | import java.util.Map;
8 | import java.util.Properties;
9 |
10 | /**
11 | * @author @aloyer
12 | */
13 | public class TagDictionary {
14 |
15 | private static final String PREFIX = "@";
16 |
17 | private Map tags = Maps.newConcurrentMap();
18 |
19 | public TagDictionary() {
20 | }
21 |
22 | public TagDictionary clear() {
23 | tags.clear();
24 | return this;
25 | }
26 |
27 | public FluentIterable tags() {
28 | return FluentIterable.from(tags.values());
29 | }
30 |
31 | public TagDictionary declareTags(Properties properties) {
32 | Enumeration> propertyNames = properties.propertyNames();
33 | while (propertyNames.hasMoreElements()) {
34 | String tagKey = (String) propertyNames.nextElement();
35 | declareTag(tagKey, properties.getProperty(tagKey));
36 | }
37 | return this;
38 | }
39 |
40 | public TagDictionary declareTag(String tagKey, String description) {
41 | getOrInitTag(tagKey).declareDescription(description);
42 | return this;
43 | }
44 |
45 | private Tag getOrInitTag(String tagKey) {
46 | String formatted = format(tagKey);
47 | Tag tag = tags.get(formatted);
48 | if (tag == null) {
49 | tag = new Tag(formatted);
50 | tags.put(formatted, tag);
51 | }
52 | return tag;
53 | }
54 |
55 | private String format(String tagKey) {
56 | String formatted = tagKey.trim();
57 | if (!formatted.startsWith(PREFIX))
58 | return PREFIX + formatted;
59 | else
60 | return formatted;
61 | }
62 |
63 | public void declareTags(TagDictionary dictionary) {
64 | tags.putAll(dictionary.tags);
65 | }
66 |
67 | public boolean containsTag(String tag) {
68 | return tags.containsKey(format(tag));
69 | }
70 |
71 | public TagDictionary declareTag(String tag) {
72 | return declareTag(tag, "");
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/exec/model/StepContainer.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.exec.model;
2 |
3 | import com.google.common.collect.FluentIterable;
4 | import com.google.common.collect.Lists;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * @author @aloyer
10 | */
11 | public class StepContainer extends EmbeddingAndWriteContainer implements HasComments, HasTags {
12 |
13 | private List steps = Lists.newArrayList();
14 | private List tags = Lists.newArrayList();
15 | private List comments = Lists.newArrayList();
16 | private String description;
17 | private LineRange lineRange;
18 |
19 | protected void recursiveCopy(final StepContainer copy) {
20 | for (StepExec stepExec : steps()) {
21 | copy.declareStep(stepExec.recursiveCopy());
22 | }
23 | copy.declareTags(tags);
24 | copy.declareComments(comments);
25 | copy.declareDescription(description);
26 | copy.declareLineRange(lineRange);
27 | super.recursiveCopy(copy);
28 | }
29 |
30 |
31 | public void declareStep(StepExec stepExec) {
32 | if (stepExec == null)
33 | throw new IllegalArgumentException("Step cannot be null!");
34 | steps.add(stepExec);
35 | }
36 |
37 | public void declareTags(List tags) {
38 | this.tags.addAll(tags);
39 | }
40 |
41 | @Override
42 | public FluentIterable tags() {
43 | return FluentIterable.from(tags);
44 | }
45 |
46 | public void declareComments(List comments) {
47 | this.comments.addAll(comments);
48 | }
49 |
50 | public void declareDescription(String description) {
51 | this.description = description;
52 | }
53 |
54 | public FluentIterable steps() {
55 | return FluentIterable.from(steps);
56 | }
57 |
58 | public void declareLineRange(LineRange lineRange) {
59 | this.lineRange = lineRange;
60 | }
61 |
62 | public LineRange lineRange() {
63 | return lineRange;
64 | }
65 |
66 | public String description() {
67 | return description;
68 | }
69 |
70 | @Override
71 | public FluentIterable comments() {
72 | return FluentIterable.from(comments);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/java/tzatziki/exploratory/cucumber/TagExpressionTest.java:
--------------------------------------------------------------------------------
1 | package tzatziki.exploratory.cucumber;
2 |
3 | import com.google.common.base.Function;
4 | import com.google.common.collect.FluentIterable;
5 | import gherkin.TagExpression;
6 | import gherkin.formatter.model.Tag;
7 | import org.junit.Test;
8 |
9 | import java.util.List;
10 |
11 | import static java.util.Arrays.asList;
12 | import static org.assertj.core.api.Assertions.assertThat;
13 |
14 | /**
15 | * @author @aloyer
16 | */
17 | public class TagExpressionTest {
18 |
19 | @Test
20 | public void sampleCase_AND() {
21 | TagExpression e = new TagExpression(asList("~@wip", "~@defaults"));
22 | assertThat(e.evaluate(tags("@bar"))).isTrue();
23 | assertThat(e.evaluate(tags("@specs"))).isTrue();
24 | assertThat(e.evaluate(tags("@specs", "@defaults"))).isFalse();
25 | }
26 |
27 | @Test
28 | public void sampleCase_OR() {
29 | TagExpression e = new TagExpression(asList("~@wip", "@option", "@modify,@insert"));
30 | assertThat(e.evaluate(tags("@wip", "@option", "@modify", "@otc"))).isFalse();
31 | assertThat(e.evaluate(tags("@specs", "@option", "@modify", "@otc"))).isTrue();
32 | assertThat(e.evaluate(tags("@acceptance", "@option", "@insert", "@modify", "@otc"))).isTrue();
33 | }
34 |
35 | @Test
36 | public void sampleCase_OR_withNegation() {
37 | TagExpression e = new TagExpression(asList("~@wip, ~@defaults"));
38 | assertThat(e.evaluate(tags("@bar"))).isTrue();
39 | assertThat(e.evaluate(tags("@specs"))).isTrue();
40 | assertThat(e.evaluate(tags("@specs", "@defaults"))).isTrue();
41 | assertThat(e.evaluate(tags("@wip", "@defaults"))).isFalse();
42 | assertThat(e.evaluate(tags("@wip"))).isTrue();
43 | assertThat(e.evaluate(tags("@defaults"))).isTrue();
44 | }
45 |
46 | private static List tags(String... names) {
47 | return FluentIterable.from(asList(names)).transform(new Function() {
48 | @Override
49 | public Tag apply(String input) {
50 | return tag(input);
51 | }
52 | }).toList();
53 | }
54 |
55 | private static Tag tag(String name) {
56 | return new Tag(name, 1);
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/tzatziki-core/src/test/java/tzatziki/analysis/java/stepdefs/OptionStepdefs.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.java.stepdefs;
2 |
3 | import cucumber.api.PendingException;
4 | import cucumber.api.Scenario;
5 | import cucumber.api.java.Before;
6 | import cucumber.api.java.en.Given;
7 | import cucumber.api.java.en.Then;
8 | import cucumber.api.java.en.When;
9 |
10 | /**
11 | * @author @aloyer
12 | */
13 | public class OptionStepdefs {
14 | private Scenario scenario;
15 | private String message;
16 |
17 | @Before("@math")
18 | public void initWithMathSupport(Scenario scenario) {
19 | scenario.embed(
20 | ("Value = \\dfrac{ Why }{ How }").getBytes(), "text/formula");
21 | }
22 |
23 | @Before
24 | public void init(Scenario scenario) {
25 | this.scenario = scenario;
26 | }
27 |
28 | /**
29 | * Order a drink with a number of sugar.
30 | * If the drink does not support the addition of sugar it won't
31 | * be checked here ({@link String}).
32 | *
33 | * @param drinkType type of drink
34 | * @param nbSugar number of sugar (if applicable)
35 | * @throws Throwable
36 | */
37 | @When("^I order an? \"([^\"]*)\" with (\\d+) sugar$")
38 | public void I_order_a_with_sugar(String drinkType, int nbSugar) throws Throwable {
39 | scenario.embed(
40 | ("" +
41 | " /---------+ +------------+\n" +
42 | " | Order |---->| Protocol |\n" +
43 | " +---------/ +------------+").getBytes(), "text/asciidiag"
44 | );
45 | }
46 |
47 | @Then("^the instruction generated should be \"([^\"]*)\"$")
48 | public void the_instruction_generated_should_be(String expectedProtocol) throws Throwable {
49 | }
50 |
51 | @When("^the message \"([^\"]*)\" is sent$")
52 | public void the_message_is_sent(String message) throws Throwable {
53 | this.message = message;
54 | }
55 |
56 | @Given("^I've inserted (\\d+)€ in the machine$")
57 | public void I_ve_inserted_€_in_the_machine(int amountInEuro) throws Throwable {
58 | throw new PendingException();
59 | }
60 |
61 | @Then("^the report output should be$")
62 | public void the_report_output_should_be(String rawReport) throws Throwable {
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tzatziki-core/src/main/java/tzatziki/analysis/step/FeatureParser.java:
--------------------------------------------------------------------------------
1 | package tzatziki.analysis.step;
2 |
3 | import com.google.common.collect.Lists;
4 | import cucumber.runtime.FeatureBuilder;
5 | import cucumber.runtime.io.MultiLoader;
6 | import cucumber.runtime.io.ResourceLoader;
7 | import cucumber.runtime.model.CucumberFeature;
8 | import gherkin.formatter.Formatter;
9 | import tzatziki.util.ResourceLoaderWrapper;
10 | import tzatziki.util.Filter;
11 | import tzatziki.util.Filters;
12 |
13 | import java.io.File;
14 | import java.io.InputStream;
15 | import java.util.List;
16 |
17 | /**
18 | * @author @aloyer
19 | */
20 | public class FeatureParser {
21 |
22 | private List cucumberFeatures;
23 | private Formatter formatter;
24 | private String suffix = "";
25 | //
26 | private List featurePaths = Lists.newArrayList();
27 | private List