diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..34f9ada --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.classpath +.project +.settings/ +target/ \ No newline at end of file diff --git a/src/main/java/org/kinimod/asciidoctor/gherkin/GherkinBlockProcessor.java b/src/main/java/org/kinimod/asciidoctor/gherkin/GherkinBlockProcessor.java deleted file mode 100644 index ef76064..0000000 --- a/src/main/java/org/kinimod/asciidoctor/gherkin/GherkinBlockProcessor.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.kinimod.asciidoctor.gherkin; - -import gherkin.parser.Parser; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import org.asciidoctor.ast.AbstractBlock; -import org.asciidoctor.ast.Block; -import org.asciidoctor.extension.BlockProcessor; -import org.asciidoctor.extension.Reader; - -public class GherkinBlockProcessor extends BlockProcessor { - - private static Map configs = new HashMap() { - { - put("contexts", Arrays.asList(":paragraph", ":literal")); - put("content_model", ":simple"); - } - }; - - public GherkinBlockProcessor(String name, Map _config) { - super(name, configs); - } - - @Override - public Object process(AbstractBlock parent, Reader reader, - Map arguments) { - String blockContent = reader.read(); - String[] lines = convertGherkinToAsciidoc(blockContent); - - Block createBlock = createBlock(parent, "section", - Arrays.asList(lines), arguments, new HashMap()); - return createBlock; - } - - private String[] convertGherkinToAsciidoc(String blockContent) { - StringBuilder builder = new StringBuilder(); - AsciidocFormatter f = new AsciidocFormatter(builder); - Parser p = new Parser(f); - p.parse(blockContent, "feature", 0); - String out = builder.toString(); - String[] lines = out.split("\\r?\\n"); - return lines; - } -} diff --git a/src/main/java/org/kinimod/asciidoctor/gherkin/AsciidocFormatter.java b/src/main/java/org/kinimod/asciidoctor/gherkin/MapFormatter.java similarity index 71% rename from src/main/java/org/kinimod/asciidoctor/gherkin/AsciidocFormatter.java rename to src/main/java/org/kinimod/asciidoctor/gherkin/MapFormatter.java index 8869cff..e9d9557 100644 --- a/src/main/java/org/kinimod/asciidoctor/gherkin/AsciidocFormatter.java +++ b/src/main/java/org/kinimod/asciidoctor/gherkin/MapFormatter.java @@ -7,47 +7,48 @@ import gherkin.formatter.model.Scenario; import gherkin.formatter.model.ScenarioOutline; import gherkin.formatter.model.Step; +import gherkin.parser.Parser; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; -import org.kinimod.asciidoctor.gherkin.template.TemplateProcessor; -import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.apache.commons.io.IOUtils; -public class AsciidocFormatter implements Formatter { +public class MapFormatter implements Formatter { - private TemplateProcessor templateProcessor; + public static String getDefaultTemplate() { - public AsciidocFormatter(StringBuilder builder) { - this.builder = builder; - ClassPathXmlApplicationContext applicationContext = null; + String text = ""; try { - applicationContext = new ClassPathXmlApplicationContext(); - - applicationContext.setConfigLocation("classpath:/beans.xml"); - applicationContext.refresh(); - this.templateProcessor = applicationContext - .getBean(TemplateProcessor.class); - - } finally { - if (applicationContext != null) { - applicationContext.close(); - } + text = IOUtils + .toString( + MapFormatter.class + .getResourceAsStream("/com/github/domgold/asciidoctor/extension/gherkin/default_template.erb"), + "UTF-8"); + } catch (IOException e) { + throw new RuntimeException(e); } + return text; } - private StringBuilder builder; + public static Map parse(String fileContent) { + MapFormatter f = new MapFormatter(); + Parser p = new Parser(f); + p.parse(fileContent, "feature", 0); + return f.getFeature(); + } private Map currentFeature; - private Map currentScenario; - private Map currentStep; - private Map currentExamples; + public MapFormatter() { + super(); + } + @Override public void background(Background arg0) { currentScenario = arg0.toMap(); @@ -66,18 +67,6 @@ public void done() { public void endOfScenarioLifeCycle(Scenario arg0) { } - @Override - public void eof() { - try { - String content = templateProcessor.process("feature.ftl", - currentFeature); - builder.append(content); - } catch (IOException e) { - throw new RuntimeException(e); - } - - } - @Override public void examples(Examples arg0) { currentExamples = arg0.toMap(); @@ -120,6 +109,7 @@ public void syntaxError(String arg0, String arg1, List arg2, public void uri(String arg0) { } + @SuppressWarnings("unchecked") private void addNew(Map baseMap, String key, Map newMap) { if (!baseMap.containsKey(key)) { @@ -128,4 +118,13 @@ private void addNew(Map baseMap, String key, ((List>) baseMap.get(key)).add(newMap); } + @Override + public void eof() { + + } + + public Map getFeature() { + return currentFeature; + } + } \ No newline at end of file diff --git a/src/main/java/org/kinimod/asciidoctor/gherkin/template/TemplateProcessor.java b/src/main/java/org/kinimod/asciidoctor/gherkin/template/TemplateProcessor.java deleted file mode 100644 index eba6dad..0000000 --- a/src/main/java/org/kinimod/asciidoctor/gherkin/template/TemplateProcessor.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.kinimod.asciidoctor.gherkin.template; - -import java.io.IOException; -import java.util.Map; - -import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; - -import freemarker.template.Configuration; -import freemarker.template.Template; -import freemarker.template.TemplateException; - -public class TemplateProcessor { - - private Configuration freemarkerConfiguration; - - public TemplateProcessor() { - - } - - public String process(String templateName, Map args) throws IOException { - Template template = this.getFreemarkerConfiguration().getTemplate( - templateName); - try { - return FreeMarkerTemplateUtils - .processTemplateIntoString(template, args); - } catch(TemplateException ex) { - throw new RuntimeException(ex); - } catch(IOException ex) { - throw new RuntimeException(ex); - } - } - - public Configuration getFreemarkerConfiguration() { - return freemarkerConfiguration; - } - - public void setFreemarkerConfiguration(Configuration freemarkerConfiguration) { - this.freemarkerConfiguration = freemarkerConfiguration; - } -} diff --git a/src/main/java/org/kinimod/asciidoctor/gherkin/template/package-info.java b/src/main/java/org/kinimod/asciidoctor/gherkin/template/package-info.java deleted file mode 100644 index 1eb94bb..0000000 --- a/src/main/java/org/kinimod/asciidoctor/gherkin/template/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -/** - * - */ -/** - * @author Dominik - * - */ -package org.kinimod.asciidoctor.gherkin.template; \ No newline at end of file diff --git a/src/main/resources/beans.xml b/src/main/resources/beans.xml deleted file mode 100644 index c0d38a8..0000000 --- a/src/main/resources/beans.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/com/github/domgold/asciidoctor/extension/gherkin/default_template.erb b/src/main/resources/com/github/domgold/asciidoctor/extension/gherkin/default_template.erb new file mode 100644 index 0000000..544dde1 --- /dev/null +++ b/src/main/resources/com/github/domgold/asciidoctor/extension/gherkin/default_template.erb @@ -0,0 +1,73 @@ + + +=== <%= feature['name'] %> + +<%if feature.key?('description') %> +<%= feature['description'] %> +<% end %> + +<%if feature.key?('background') %>==== <%= feature['background']['name'] %> +<%if feature['background'].key?('description') %> +<%= feature['background']['description'] %> +<% end %><%if feature['background'].key?('steps') %> +[.step-list]<% feature['background']['steps'].each do |step| %> +* *<%= step['keyword'].strip %>* <%= step['name'] %><%if step.key?('doc_string') %> ++ +.... +<%= step['doc_string']['value'] %> +.... +<% end %><%if step.key?('rows') %> ++ +|==== +<% step['rows'].each_with_index do |row, index| %><% row['cells'].each do |cell| %>a| <%= cell %> <% end %> +<% end %>|==== +<% end %> + +<% end %> +<% end %> + +<%if feature['background'].key?('examples') %> + +===== <%= feature['background']['examples']['keyword'].strip %> <%= feature['background']['examples']['name'] %> + +|==== +<% feature['background']['examples']['rows'].each_with_index do |row, index| %><% row['cells'].each do |cell| %>a| <%= cell %> <% end %> +<% if index == 0 %> +<% end %><% end %>|==== +<% end %> +<% end %> + +<%if feature.key?('scenarios') %><% feature['scenarios'].each do |scenario| %>==== <%= scenario['name'] %> +<%if scenario.key?('description') %> +<%= scenario['description'] %> +<% end %><%if scenario.key?('steps') %> +[.step-list]<% scenario['steps'].each do |step| %> +* *<%= step['keyword'].strip %>* <%= step['name'] %><%if step.key?('doc_string') %> ++ +.... +<%= step['doc_string']['value'] %> +.... +<% end %><%if step.key?('rows') %> ++ +|==== +<% step['rows'].each_with_index do |row, index| %><% row['cells'].each do |cell| %>a| <%= cell %> <% end %> +<% end %>|==== +<% end %> + +<% end %> +<% end %> + +<%if scenario.key?('examples') %> + +===== <%= scenario['examples']['keyword'].strip %> <%= scenario['examples']['name'] %> + +|==== +<% scenario['examples']['rows'].each_with_index do |row, index| %><% row['cells'].each do |cell| %>a| <%= cell %> <% end %> +<% if index == 0 %> +<% end %><% end %>|==== + +<% end %> + +<% end %> +<% end %> + diff --git a/src/main/resources/com/github/domgold/asciidoctor/extension/gherkin/gherkinblock.rb b/src/main/resources/com/github/domgold/asciidoctor/extension/gherkin/gherkinblock.rb new file mode 100644 index 0000000..2bc4b05 --- /dev/null +++ b/src/main/resources/com/github/domgold/asciidoctor/extension/gherkin/gherkinblock.rb @@ -0,0 +1,43 @@ +require 'asciidoctor' +require 'asciidoctor/extensions' +require 'erb' +require 'java' + +class GherkinIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor + + def process doc, reader, target, attributes + #get the default template from java resource + template_content = org.kinimod.asciidoctor.gherkin.MapFormatter.getDefaultTemplate() + + if doc.attributes.key?('docdir') && attributes.key?('template') && File.exist?(File.expand_path(attributes['template'], doc.attributes['docdir'])) + template_file = File.open(File.expand_path(attributes['template'], doc.attributes['docdir']), "rb") + template_content = template_file.read + else + template_content = org.kinimod.asciidoctor.gherkin.MapFormatter.getDefaultTemplate() + end + + erb_template = ERB.new(template_content) + + feature_file_name = target[8..-1] + if doc.attributes.key?('docdir') + feature_file_name = File.expand_path(feature_file_name, doc.attributes['docdir']) + end + + file = File.open(feature_file_name, "rb") + feature_file_content = file.read + + #parse feature and make the result available to the template via binding as 'feature' hash. + feature = org.kinimod.asciidoctor.gherkin.MapFormatter.parse(feature_file_content) + + rendered_template_output = erb_template.result(binding()) + + reader.push_include rendered_template_output, target, target, 1, attributes + + reader + end + + def handles? target + target.start_with? 'gherkin:' + end + +end \ No newline at end of file diff --git a/src/main/resources/feature.ftl b/src/main/resources/feature.ftl deleted file mode 100644 index ea4d01e..0000000 --- a/src/main/resources/feature.ftl +++ /dev/null @@ -1,49 +0,0 @@ -= ${name?trim} -:numbered: - -<#if description??> -${description} - - -<#if background??> -== ${background.keyword?trim} ${background.name?trim} - -<#if background.steps??> -<#list background.steps as step> -* *${step.keyword?trim}* ${step.name?trim} - - - - -<#if scenarios??> -== Scénarii - -<#list scenarios as scenario> -=== ${scenario.keyword?trim} ${scenario.name?trim} - -<#if description??> -${description} - - -<#if scenario.steps??> -<#list scenario.steps as step> -* *${step.keyword?trim}* ${step.name?trim} - - - -<#if scenario.examples??> - -==== ${scenario.examples.keyword?trim} ${scenario.examples.name?trim} - -|==== -<#list scenario.examples.rows as row> -<#list row.cells as cell>| ${cell?trim}<#if row_index == 0> - - - -|==== - - - - - diff --git a/src/test/java/org/kinimod/asciidoctor/gherkin/AsciidocFormatterTest.java b/src/test/java/org/kinimod/asciidoctor/gherkin/AsciidocFormatterTest.java deleted file mode 100644 index 09c65da..0000000 --- a/src/test/java/org/kinimod/asciidoctor/gherkin/AsciidocFormatterTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.kinimod.asciidoctor.gherkin; - -import static org.junit.Assert.*; -import gherkin.formatter.Formatter; -import gherkin.parser.Parser; - -import java.io.File; -import java.io.IOException; - -import org.apache.commons.io.FileUtils; -import org.junit.Test; - -public class AsciidocFormatterTest { - - @Test - public void test() throws IOException { - String content = FileUtils.readFileToString(new File("src/test/resources/simple.feature")); - StringBuilder builder = new StringBuilder(); - AsciidocFormatter f = new AsciidocFormatter(builder); - Parser p = new Parser(f); - p.parse(content, "src/test/resources/simple.feature", 0); - String out = builder.toString(); - assertNotNull(out); - File testOutput = new File("target/testoutput"); - if(!testOutput.exists()) { - testOutput.mkdirs(); - } - FileUtils.writeStringToFile(new File(testOutput, "test.ad"), out); - } - -} diff --git a/src/test/java/org/kinimod/asciidoctor/gherkin/GherkinBlockProcessorTest.java b/src/test/java/org/kinimod/asciidoctor/gherkin/GherkinBlockProcessorTest.java deleted file mode 100644 index 14d66b9..0000000 --- a/src/test/java/org/kinimod/asciidoctor/gherkin/GherkinBlockProcessorTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.kinimod.asciidoctor.gherkin; - -import static org.junit.Assert.*; -import static org.asciidoctor.OptionsBuilder.*; - -import java.io.File; -import java.io.IOException; - -import org.apache.commons.io.FileUtils; -import org.asciidoctor.Asciidoctor; -import org.asciidoctor.Options; -import org.asciidoctor.SafeMode; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -public class GherkinBlockProcessorTest { - - @BeforeClass - public static void init() { - File tmpFolder = new File("target/tmptest"); - if(!tmpFolder.exists()) { - assertTrue(tmpFolder.mkdirs()); - } - } - - @Rule - public TemporaryFolder testFolder = new TemporaryFolder(new File("target/tmptest")); - - @Test - public void testProcessAbstractBlockReaderMapOfStringObject() throws IOException { - Asciidoctor asciidoctor = Asciidoctor.Factory.create(); - asciidoctor.javaExtensionRegistry().block("gherkin", - GherkinBlockProcessor.class); -// asciidoctor.javaExtensionRegistry().preprocessor(GherkinPreprocessor.class); - Options options = options().inPlace(false) - .toFile(new File(new File("target/tmptest"), "rendersample.html")) - .safe(SafeMode.UNSAFE).get(); - asciidoctor.renderFile(new File("src/test/resources/testfeatureblock.ad"), - options); - File renderedFile = new File(testFolder.getRoot(), "rendersample.html"); - assertTrue(renderedFile.exists()); - String html = FileUtils.readFileToString(renderedFile); - assertTrue(String.format("html must contain '

Test feature', content was :\n%s", html), html.contains("

Test feature")); - } - -} diff --git a/src/test/java/org/kinimod/asciidoctor/gherkin/GherkinRubyBlockTest.java b/src/test/java/org/kinimod/asciidoctor/gherkin/GherkinRubyBlockTest.java new file mode 100644 index 0000000..75ed59b --- /dev/null +++ b/src/test/java/org/kinimod/asciidoctor/gherkin/GherkinRubyBlockTest.java @@ -0,0 +1,40 @@ +package org.kinimod.asciidoctor.gherkin; + +import static org.junit.Assert.*; + +import java.io.File; + +import static org.asciidoctor.OptionsBuilder.*; + +import org.asciidoctor.Asciidoctor; +import org.asciidoctor.Options; +import org.asciidoctor.SafeMode; +import org.asciidoctor.extension.RubyExtensionRegistry; +import org.junit.AfterClass; +import org.junit.Test; + +public class GherkinRubyBlockTest { + + @AfterClass + public static void tearDownAfterClass() throws Exception { + } + + @Test + public void test() { + Asciidoctor asciidoctor = Asciidoctor.Factory.create(); + RubyExtensionRegistry rubyExtensionRegistry = asciidoctor.rubyExtensionRegistry(); + rubyExtensionRegistry.loadClass(Class.class.getResourceAsStream("/com/github/domgold/asciidoctor/extension/gherkin/gherkinblock.rb")).includeProcessor("GherkinIncludeProcessor"); + + File destinationDir = new File("target"); + Options options = options() + .toDir(destinationDir) + .safe(SafeMode.UNSAFE).get(); + asciidoctor.convertFile( + new File("src/test/resources/simple_specs_example.adoc"), + options); + asciidoctor.convertFile( + new File("src/test/resources/complex_specs_example.adoc"), + options); + } + +} diff --git a/src/test/resources/_feature1.ad b/src/test/resources/_feature1.ad deleted file mode 100644 index daebb29..0000000 --- a/src/test/resources/_feature1.ad +++ /dev/null @@ -1,35 +0,0 @@ -= Test feature -:numbered: - -Ceci est une simple fonctionnalité. La description n'est pas très détaillée. - -== Contexte - -* *Quand* I click *fat* and {backend} -* *Alors* I see - -== Scénarii - -=== Scénario Scenario1 - -Ceci est une simple fonctionnalité. La description n'est pas très détaillée. - -* *Quand* I click -* *Alors* I see - - -=== Plan du scénario Scenario2 - -Ceci est une simple fonctionnalité. La description n'est pas très détaillée. - -* *Quand* I click2 -* *Alors* I see2 - - -==== Exemples - -|==== -| h1| h2 - -| hello| hello2|==== - diff --git a/src/test/resources/complex.feature b/src/test/resources/complex.feature new file mode 100644 index 0000000..a21e13a --- /dev/null +++ b/src/test/resources/complex.feature @@ -0,0 +1,61 @@ +# language: en +Feature: A complex feature + This is the feature description. + + Use asciidoctor markup as it pleases you. + + [source,java] + ---- + String test = "Hello, Asciidoctor."; + ---- + + * list content + * list content2 + *** nested list content. + + WARNING: admonition. + + include::included_doc.adoc[] + + .A table with title + |==== + | A | B + + | 1 | 2 + |==== + + Background: Background + Given a simple step. + + Scenario Outline: Scenario title + + The Scenario description comes here. + + [NOTE] + ==== + Lists with \* get interpreted as steps. + So if you want a list, use `-` or escape the star. + + ----- + \* + ----- + ==== + + Given a simple outline step with a doc string : + """ + doc string + 2 *lines* + """ + When I have a step with a *table* + | a | b | + | c | d | + And I render the asciidoctor content to html + Then the parameters should NOT get processed. + | img | + | **.png | + And the file "**.png" everything is fine. + + Examples: + | parameter_name | parameter_value | + | _actorWidth_ | 25 | + | actorHeight | 30 | diff --git a/src/test/resources/complex_specs_example.adoc b/src/test/resources/complex_specs_example.adoc new file mode 100644 index 0000000..8d3d55e --- /dev/null +++ b/src/test/resources/complex_specs_example.adoc @@ -0,0 +1,40 @@ += Complex specs example +:toc: right +:numbered: +:toclevels: 6 +:icons: font +:docinfo1: + + +Introduction + +include::included_doc.adoc[] + +== Some features with default template + +include::gherkin:./complex.feature[] + +== The same feature with a custom template + +[NOTE] +==== +Place a file called `my_custom_feature_template.erb` in the same folder as the source document. + +PAste the following content: + +.Custom template +.... +include::my_custom_feature_template.erb[] +.... +==== + + +The following include line + +.... +\include::gherkin:./complex.feature[template=my_custom_feature_template.erb] +.... + +renders to: + +include::gherkin:./complex.feature[template=my_custom_feature_template.erb] diff --git a/src/test/resources/docinfo.html b/src/test/resources/docinfo.html new file mode 100644 index 0000000..49a8a67 --- /dev/null +++ b/src/test/resources/docinfo.html @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/src/test/resources/included_doc.adoc b/src/test/resources/included_doc.adoc new file mode 100644 index 0000000..e92d6fd --- /dev/null +++ b/src/test/resources/included_doc.adoc @@ -0,0 +1,7 @@ + +This is an included paragraph. + +[source,java] +---- +String test = "Hello"; +---- diff --git a/src/test/resources/my_custom_feature_template.erb b/src/test/resources/my_custom_feature_template.erb new file mode 100644 index 0000000..40ea7c6 --- /dev/null +++ b/src/test/resources/my_custom_feature_template.erb @@ -0,0 +1,9 @@ + +.<%= feature['name'] %> +|==== +<%if feature.key?('scenarios') %><% feature['scenarios'].each do |scenario| %>| <%= scenario['name'] %> +<% end %> +<% else %> +| _Nothing to show. +<% end %> +|==== diff --git a/src/test/resources/simple.feature b/src/test/resources/simple.feature index a2eec89..61100cc 100644 --- a/src/test/resources/simple.feature +++ b/src/test/resources/simple.feature @@ -1,25 +1,11 @@ -# language: fr -Fonctionnalité: Test feature - Ceci est une simple fonctionnalité. La description n'est pas très détaillée. +# language: en +Feature: A simple feature + This is the feature description. - Contexte: - La description du contexte n'est pas très détaillée non plus. + Background: Background + Given a simple background step. - Quand I click *fat* and {backend} - Alors I see - - Scénario: Scenario1 - La description du scénario n'est pas très détaillée non plus. - - Quand I click - Alors I see - - Plan du scénario: Scenario2 - La description du plan du scénario n'est pas très détaillée non plus. - - Quand I click2 - Alors I see2 - - Exemples: - | h1 | h2 | - | hello | hello2 | + Scenario: Scenario title + Given a simple scenario step + When I render the asciidoctor content to html + Then my feature gets nicely formatted in html. diff --git a/src/test/resources/simple2.feature b/src/test/resources/simple2.feature new file mode 100644 index 0000000..292c546 --- /dev/null +++ b/src/test/resources/simple2.feature @@ -0,0 +1,11 @@ +# language: en +Feature: A simple feature 2 + This is the second feature description. + + Background: Background2 + Given a simple background step. + + Scenario: Scenario title2 + Given two apples + When I eat one apple + Then there is one apple left. diff --git a/src/test/resources/simple_specs_example.adoc b/src/test/resources/simple_specs_example.adoc new file mode 100644 index 0000000..89ed443 --- /dev/null +++ b/src/test/resources/simple_specs_example.adoc @@ -0,0 +1,7 @@ += Simple specs example + +== My features + +include::gherkin:./simple.feature[] + +include::gherkin:./simple2.feature[] diff --git a/src/test/resources/testfeatureblock-old.ad b/src/test/resources/testfeatureblock-old.ad deleted file mode 100644 index ed5b6f8..0000000 --- a/src/test/resources/testfeatureblock-old.ad +++ /dev/null @@ -1,14 +0,0 @@ -= Sample Document -:toc: right -:numbered: -:toclevels: 4 - - -Introduction - -== Some features - -:leveloffset: +2 -include::_feature1.ad[] -:leveloffset: -2 - diff --git a/src/test/resources/testfeatureblock.ad b/src/test/resources/testfeatureblock.ad deleted file mode 100644 index 9595dd8..0000000 --- a/src/test/resources/testfeatureblock.ad +++ /dev/null @@ -1,17 +0,0 @@ -= Sample Document -:toc: right -:numbered: -:toclevels: 4 - - -Introduction - -== Some features - -:leveloffset: +2 -[gherkin] -.... -include::simple.feature[] -.... -:leveloffset: -2 -