diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index ebc91d6f..4c188cd4 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -73,7 +73,7 @@ + value="com.jayway.restassured.RestAssured.*, org.junit.Assert.*, org.junit.Assume.*, org.hamcrest.CoreMatchers.*, org.hamcrest.Matchers.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.Matchers.*, org.springframework.restdocs.curl.CurlDocumentation.*, org.springframework.restdocs.headers.HeaderDocumentation.*, org.springframework.restdocs.hypermedia.HypermediaDocumentation.*, org.springframework.restdocs.mockmvc.IterableEnumeration.*, org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*, org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*, org.springframework.restdocs.payload.PayloadDocumentation.*, org.springframework.restdocs.operation.preprocess.Preprocessors.*, org.springframework.restdocs.request.RequestDocumentation.*, org.springframework.restdocs.restassured.RestAssuredRestDocumentation.*, org.springframework.restdocs.restassured.operation.preprocess.RestAssuredPreprocessors.*, org.springframework.restdocs.snippet.Attributes.*, org.springframework.restdocs.snippet.SnippetFormats.*, org.springframework.restdocs.test.SnippetMatchers.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*" /> diff --git a/docs/src/docs/asciidoc/configuration.adoc b/docs/src/docs/asciidoc/configuration.adoc index 23958800..f068507e 100644 --- a/docs/src/docs/asciidoc/configuration.adoc +++ b/docs/src/docs/asciidoc/configuration.adoc @@ -42,8 +42,7 @@ TIP: To configure a request's context path, use the `contextPath` method on [[configuration-snippet-encoding]] === Snippet encoding -The default encoding used by Asciidoctor is `UTF-8`. Spring REST Docs adopts the same -default for the snippets that it generates. You can change the default snippet encoding +The default snippet encoding is `UTF-8`. You can change the default snippet encoding using the `RestDocumentationConfigurer` API. For example, to use `ISO-8859-1`: [source,java,indent=0,role="primary"] @@ -60,6 +59,26 @@ include::{examples-dir}/com/example/restassured/CustomEncoding.java[tags=custom- +[[configuration-snippet-format]] +=== Snippet format + +The default snippet format is Asciidoctor. Markdown is also supported out of the box. You +can change the default format using the `RestDocumentationConfigurer` API: + +[source,java,indent=0,role="primary"] +.MockMvc +---- +include::{examples-dir}/com/example/mockmvc/CustomFormat.java[tags=custom-format] +---- + +[source,java,indent=0,role="secondary"] +.REST Assured +---- +include::{examples-dir}/com/example/restassured/CustomFormat.java[tags=custom-format] +---- + + + [[configuration-default-snippets]] === Default snippets diff --git a/docs/src/docs/asciidoc/index.adoc b/docs/src/docs/asciidoc/index.adoc index 17cde86e..10777c6a 100644 --- a/docs/src/docs/asciidoc/index.adoc +++ b/docs/src/docs/asciidoc/index.adoc @@ -26,4 +26,5 @@ include::documenting-your-api.adoc[] include::customizing-requests-and-responses.adoc[] include::configuration.adoc[] include::working-with-asciidoctor.adoc[] +include::working-with-markdown.adoc[] include::contributing.adoc[] \ No newline at end of file diff --git a/docs/src/docs/asciidoc/introduction.adoc b/docs/src/docs/asciidoc/introduction.adoc index 3bfaef7d..7a6fde22 100644 --- a/docs/src/docs/asciidoc/introduction.adoc +++ b/docs/src/docs/asciidoc/introduction.adoc @@ -6,8 +6,9 @@ services that is accurate and readable. Writing high-quality documentation is difficult. One way to ease that difficulty is to use tools that are well-suited to the job. To this end, Spring REST Docs uses -http://asciidoctor.org[Asciidoctor]. Asciidoctor processes plain text and produces -HTML, styled and layed out to suit your needs. +http://asciidoctor.org[Asciidoctor] by default. Asciidoctor processes plain text and +produces HTML, styled and layed out to suit your needs. If you prefer, Spring REST Docs +can also be configured to use Markdown. Spring REST Docs makes use of snippets produced by tests written with {spring-framework-docs}/#spring-mvc-test-framework[Spring MVC Test] or diff --git a/docs/src/docs/asciidoc/working-with-markdown.adoc b/docs/src/docs/asciidoc/working-with-markdown.adoc new file mode 100644 index 00000000..6f6d8ed7 --- /dev/null +++ b/docs/src/docs/asciidoc/working-with-markdown.adoc @@ -0,0 +1,28 @@ +[[working-with-markdown]] +== Working with Markdown + +This section describes any aspects of working with Markdown that are particularly +relevant to Spring REST Docs. + + + +[[working-with-markdown-limitations]] +=== Limitations + +Markdown was originally designed for people writing for the web and, as such, isn't +as well-suited to writing documentation as Asciidoctor. Typically, these limitations +are overcome by using another tool that builds on top of Markdown. + +Markdown has no official support for tables. Spring REST Docs' default Markdown snippet +templates use https://michelf.ca/projects/php-markdown/extra/#table[Markdown Extra's table +format]. + + + +[[working-with-markdown-including-snippets]] +=== Including snippets + +Markdown has no built-in support for including one Markdown file in another. To include +the generated snippets of Markdown in your documentation, you should use an additional +tool that supports this functionality. One example that's particularly well-suited to +documenting APIs is https://github.com/tripit/slate[Slate]. \ No newline at end of file diff --git a/docs/src/test/java/com/example/mockmvc/CustomFormat.java b/docs/src/test/java/com/example/mockmvc/CustomFormat.java new file mode 100644 index 00000000..ab121224 --- /dev/null +++ b/docs/src/test/java/com/example/mockmvc/CustomFormat.java @@ -0,0 +1,50 @@ +/* + * Copyright 2014-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.mockmvc; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; + +import org.junit.Before; +import org.junit.Rule; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.restdocs.RestDocumentation; +import org.springframework.restdocs.snippet.SnippetFormats; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +public class CustomFormat { + + @Rule + public final RestDocumentation restDocumentation = new RestDocumentation("build"); + + @Autowired + private WebApplicationContext context; + + private MockMvc mockMvc; + + @Before + public void setUp() { + // tag::custom-format[] + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(documentationConfiguration(this.restDocumentation) + .snippets().withFormat(SnippetFormats.markdown())) + .build(); + // end::custom-format[] + } + +} diff --git a/docs/src/test/java/com/example/restassured/CustomFormat.java b/docs/src/test/java/com/example/restassured/CustomFormat.java new file mode 100644 index 00000000..649becb2 --- /dev/null +++ b/docs/src/test/java/com/example/restassured/CustomFormat.java @@ -0,0 +1,46 @@ +/* + * Copyright 2014-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.restassured; + +import org.junit.Before; +import org.junit.Rule; +import org.springframework.restdocs.RestDocumentation; +import org.springframework.restdocs.snippet.SnippetFormats; + +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.specification.RequestSpecification; + +import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; + +public class CustomFormat { + + @Rule + public final RestDocumentation restDocumentation = new RestDocumentation("build"); + + private RequestSpecification spec; + + @Before + public void setUp() { + // tag::custom-format[] + this.spec = new RequestSpecBuilder() + .addFilter(documentationConfiguration(this.restDocumentation) + .snippets().withFormat(SnippetFormats.markdown())) + .build(); + // end::custom-format[] + } + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java index bedec85a..0f0ff9b2 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java @@ -93,13 +93,20 @@ public abstract class RestDocumentationConfigurer { private static final class TemplateEngineConfigurer extends AbstractConfigurer { - private TemplateEngine templateEngine = new MustacheTemplateEngine( - new StandardTemplateResourceResolver()); + private TemplateEngine templateEngine; @Override public void apply(Map configuration, RestDocumentationContext context) { - configuration.put(TemplateEngine.class.getName(), this.templateEngine); + TemplateEngine engineToUse = this.templateEngine; + if (engineToUse == null) { + SnippetConfiguration snippetConfiguration = (SnippetConfiguration) configuration + .get(SnippetConfiguration.class.getName()); + engineToUse = new MustacheTemplateEngine( + new StandardTemplateResourceResolver( + snippetConfiguration.getFormat())); + } + configuration.put(TemplateEngine.class.getName(), engineToUse); } private void setTemplateEngine(TemplateEngine templateEngine) { @@ -117,8 +124,12 @@ public abstract class RestDocumentationConfigurer { RestDocumentationContext context) { WriterResolver resolverToUse = this.writerResolver; if (resolverToUse == null) { + SnippetConfiguration snippetConfiguration = (SnippetConfiguration) configuration + .get(SnippetConfiguration.class.getName()); resolverToUse = new StandardWriterResolver( - new RestDocumentationContextPlaceholderResolver(context)); + new RestDocumentationContextPlaceholderResolver(context), + snippetConfiguration.getEncoding(), + snippetConfiguration.getFormat()); } configuration.put(WriterResolver.class.getName(), resolverToUse); } diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfiguration.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfiguration.java new file mode 100644 index 00000000..ecfd94e2 --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfiguration.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.config; + +import org.springframework.restdocs.snippet.SnippetFormat; + +/** + * An encapsulation of the configuration for documentation snippets. + * + * @author Andy Wilkinson + */ +class SnippetConfiguration { + + private final String encoding; + + private final SnippetFormat format; + + SnippetConfiguration(String encoding, SnippetFormat format) { + this.encoding = encoding; + this.format = format; + } + + String getEncoding() { + return this.encoding; + } + + SnippetFormat getFormat() { + return this.format; + } + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java index 6f4be5a5..a028a7c6 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java @@ -24,7 +24,8 @@ import org.springframework.restdocs.RestDocumentationContext; import org.springframework.restdocs.curl.CurlDocumentation; import org.springframework.restdocs.http.HttpDocumentation; import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.WriterResolver; +import org.springframework.restdocs.snippet.SnippetFormat; +import org.springframework.restdocs.snippet.SnippetFormats; /** * A configurer that can be used to configure the generated documentation snippets. @@ -51,8 +52,18 @@ public abstract class SnippetConfigurer extends AbstractNestedConfigurer

extends AbstractNestedConfigurer

configuration, RestDocumentationContext context) { - ((WriterResolver) configuration.get(WriterResolver.class.getName())) - .setEncoding(this.snippetEncoding); + configuration.put(SnippetConfiguration.class.getName(), new SnippetConfiguration( + this.snippetEncoding, this.snippetFormat)); configuration.put(ATTRIBUTE_DEFAULT_SNIPPETS, this.defaultSnippets); } @@ -93,4 +104,17 @@ public abstract class SnippetConfigurer extends AbstractNestedConfigurer

- * Templates are resolved by first looking for a resource on the classpath named + * Templates are resolved by looking for a resource on the classpath named * {@code org/springframework/restdocs/templates/{name}.snippet}. If no such - * resource exists {@code default-} is prepended to the name and the classpath is checked - * again. The built-in snippet templates are all named {@code default- name}, thereby - * allowing them to be overridden. + * resource exists an attempt is made to return a default resource that is appropriate for + * the configured snippet format. * * @author Andy Wilkinson */ public class StandardTemplateResourceResolver implements TemplateResourceResolver { + private final SnippetFormat snippetFormat; + + /** + * Creates a new {@code StandardTemplateResourceResolver} that will produce default + * template resources formatted with Asciidoctor. + * + * @deprecated since 1.1.0 in favour of + * {@link #StandardTemplateResourceResolver(SnippetFormat)} + */ + @Deprecated + public StandardTemplateResourceResolver() { + this(SnippetFormats.asciidoctor()); + } + + /** + * Creates a new {@code StandardTemplateResourceResolver} that will produce default + * template resources formatted with the given {@code snippetFormat}. + * + * @param snippetFormat the format for the default snippet templates + */ + public StandardTemplateResourceResolver(SnippetFormat snippetFormat) { + this.snippetFormat = snippetFormat; + } + @Override public Resource resolveTemplateResource(String name) { ClassPathResource classPathResource = new ClassPathResource( "org/springframework/restdocs/templates/" + name + ".snippet"); if (!classPathResource.exists()) { classPathResource = new ClassPathResource( - "org/springframework/restdocs/templates/default-" + name + ".snippet"); + "org/springframework/restdocs/templates/" + + this.snippetFormat.getFileExtension() + "/" + name + + ".snippet"); if (!classPathResource.exists()) { throw new IllegalStateException("Template named '" + name + "' could not be resolved"); diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-curl-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/curl-request.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-curl-request.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/curl-request.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-http-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/http-request.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-http-request.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/http-request.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-http-response.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/http-response.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-http-response.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/http-response.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-links.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/links.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-links.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/links.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-path-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/path-parameters.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-path-parameters.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/path-parameters.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-request-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/request-fields.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-request-fields.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/request-fields.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-request-headers.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/request-headers.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-request-headers.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/request-headers.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-request-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/request-parameters.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-request-parameters.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/request-parameters.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-response-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/response-fields.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-response-fields.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/response-fields.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-response-headers.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/response-headers.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/default-response-headers.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/adoc/response-headers.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/curl-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/curl-request.snippet new file mode 100644 index 00000000..08589427 --- /dev/null +++ b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/curl-request.snippet @@ -0,0 +1,3 @@ +```bash +$ curl {{url}} {{options}} +``` \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/http-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/http-request.snippet new file mode 100644 index 00000000..9613f5fc --- /dev/null +++ b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/http-request.snippet @@ -0,0 +1,7 @@ +```http +{{method}} {{path}} HTTP/1.1 +{{#headers}} +{{name}}: {{value}} +{{/headers}} +{{requestBody}} +``` \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/http-response.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/http-response.snippet new file mode 100644 index 00000000..1b01aa5b --- /dev/null +++ b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/http-response.snippet @@ -0,0 +1,7 @@ +```http +HTTP/1.1 {{statusCode}} {{statusReason}} +{{#headers}} +{{name}}: {{value}} +{{/headers}} +{{responseBody}} +``` \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/links.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/links.snippet new file mode 100644 index 00000000..116d8eab --- /dev/null +++ b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/links.snippet @@ -0,0 +1,5 @@ +Relation | Description +-------- | ----------- +{{#links}} +{{rel}} | {{description}} +{{/links}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/path-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/path-parameters.snippet new file mode 100644 index 00000000..f5f1daae --- /dev/null +++ b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/path-parameters.snippet @@ -0,0 +1,6 @@ +{{path}} +Parameter | Description +--------- | ----------- +{{#parameters}} +{{name}} | {{description}} +{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/request-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/request-fields.snippet new file mode 100644 index 00000000..e8d2957f --- /dev/null +++ b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/request-fields.snippet @@ -0,0 +1,5 @@ +Path | Type | Description +---- | ---- | ----------- +{{#fields}} +{{path}} | {{type}} | {{description}} +{{/fields}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/request-headers.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/request-headers.snippet new file mode 100644 index 00000000..f8c5ed27 --- /dev/null +++ b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/request-headers.snippet @@ -0,0 +1,5 @@ +Name | Description +---- | ----------- +{{#headers}} +{{name}} | {{description}} +{{/headers}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/request-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/request-parameters.snippet new file mode 100644 index 00000000..09a8a199 --- /dev/null +++ b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/request-parameters.snippet @@ -0,0 +1,5 @@ +Parameter | Description +--------- | ----------- +{{#parameters}} +{{name}} | {{description}} +{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/response-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/response-fields.snippet new file mode 100644 index 00000000..e8d2957f --- /dev/null +++ b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/response-fields.snippet @@ -0,0 +1,5 @@ +Path | Type | Description +---- | ---- | ----------- +{{#fields}} +{{path}} | {{type}} | {{description}} +{{/fields}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/response-headers.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/response-headers.snippet new file mode 100644 index 00000000..f8c5ed27 --- /dev/null +++ b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/md/response-headers.snippet @@ -0,0 +1,5 @@ +Name | Description +---- | ----------- +{{#headers}} +{{name}} | {{description}} +{{/headers}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/AbstractSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/AbstractSnippetTests.java new file mode 100644 index 00000000..83017f29 --- /dev/null +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/AbstractSnippetTests.java @@ -0,0 +1,96 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.HttpStatus; +import org.springframework.restdocs.snippet.SnippetFormat; +import org.springframework.restdocs.test.ExpectedSnippet; +import org.springframework.restdocs.test.OperationBuilder; +import org.springframework.restdocs.test.SnippetMatchers; +import org.springframework.restdocs.test.SnippetMatchers.CodeBlockMatcher; +import org.springframework.restdocs.test.SnippetMatchers.HttpRequestMatcher; +import org.springframework.restdocs.test.SnippetMatchers.HttpResponseMatcher; +import org.springframework.restdocs.test.SnippetMatchers.TableMatcher; +import org.springframework.web.bind.annotation.RequestMethod; + +import static org.springframework.restdocs.snippet.SnippetFormats.asciidoctor; +import static org.springframework.restdocs.snippet.SnippetFormats.markdown; + +/** + * Abstract base class for testing snippet generation. + * + * @author Andy Wilkinson + */ +@RunWith(Parameterized.class) +public abstract class AbstractSnippetTests { + + protected final SnippetFormat snippetFormat; + + @Rule + public ExpectedSnippet snippet; + + @Parameters(name = "{0}") + public static List parameters() { + return Arrays.asList(new Object[] { "Asciidoctor", asciidoctor() }, new Object[] { + "Markdown", markdown() }); + } + + public AbstractSnippetTests(String name, SnippetFormat snippetFormat) { + this.snippet = new ExpectedSnippet(snippetFormat); + this.snippetFormat = snippetFormat; + } + + public CodeBlockMatcher codeBlock(String language) { + return SnippetMatchers.codeBlock(this.snippetFormat, language); + } + + public TableMatcher tableWithHeader(String... headers) { + return SnippetMatchers.tableWithHeader(this.snippetFormat, headers); + } + + public TableMatcher tableWithTitleAndHeader(String title, String... headers) { + return SnippetMatchers + .tableWithTitleAndHeader(this.snippetFormat, title, headers); + } + + public HttpRequestMatcher httpRequest(RequestMethod method, String uri) { + return SnippetMatchers.httpRequest(this.snippetFormat, method, uri); + } + + public HttpResponseMatcher httpResponse(HttpStatus responseStatus) { + return SnippetMatchers.httpResponse(this.snippetFormat, responseStatus); + } + + public OperationBuilder operationBuilder(String name) { + return new OperationBuilder(name, this.snippet.getOutputDirectory(), + this.snippetFormat); + } + + protected FileSystemResource snippetResource(String name) { + return new FileSystemResource("src/test/resources/custom-snippet-templates/" + + this.snippetFormat.getFileExtension() + "/" + name + ".snippet"); + } + +} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/curl/CurlRequestSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/curl/CurlRequestSnippetTests.java index 21c1973d..f93b0467 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/curl/CurlRequestSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/curl/CurlRequestSnippetTests.java @@ -18,17 +18,16 @@ package org.springframework.restdocs.curl; import java.io.IOException; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.core.io.FileSystemResource; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.restdocs.AbstractSnippetTests; +import org.springframework.restdocs.snippet.SnippetFormat; import org.springframework.restdocs.templates.TemplateEngine; import org.springframework.restdocs.templates.TemplateResourceResolver; import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.restdocs.test.ExpectedSnippet; -import org.springframework.restdocs.test.OperationBuilder; import org.springframework.util.Base64Utils; import static org.hamcrest.CoreMatchers.containsString; @@ -36,7 +35,6 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.restdocs.test.SnippetMatchers.codeBlock; /** * Tests for {@link CurlRequestSnippet}. @@ -47,30 +45,27 @@ import static org.springframework.restdocs.test.SnippetMatchers.codeBlock; * @author Jonathan Pearlin * @author Paul-Christian Volkmer */ -public class CurlRequestSnippetTests { +@RunWith(Parameterized.class) +public class CurlRequestSnippetTests extends AbstractSnippetTests { - @Rule - public ExpectedSnippet snippet = new ExpectedSnippet(); - - @Rule - public ExpectedException thrown = ExpectedException.none(); + public CurlRequestSnippetTests(String name, SnippetFormat snippetFormat) { + super(name, snippetFormat); + } @Test public void getRequest() throws IOException { this.snippet.expectCurlRequest("get-request").withContents( codeBlock("bash").content("$ curl 'http://localhost/foo' -i")); - new CurlRequestSnippet().document(new OperationBuilder("get-request", - this.snippet.getOutputDirectory()).request("http://localhost/foo") - .build()); + new CurlRequestSnippet().document(operationBuilder("get-request").request( + "http://localhost/foo").build()); } @Test public void nonGetRequest() throws IOException { this.snippet.expectCurlRequest("non-get-request").withContents( codeBlock("bash").content("$ curl 'http://localhost/foo' -i -X POST")); - new CurlRequestSnippet().document(new OperationBuilder("non-get-request", - this.snippet.getOutputDirectory()).request("http://localhost/foo") - .method("POST").build()); + new CurlRequestSnippet().document(operationBuilder("non-get-request") + .request("http://localhost/foo").method("POST").build()); } @Test @@ -78,9 +73,8 @@ public class CurlRequestSnippetTests { this.snippet.expectCurlRequest("request-with-content").withContents( codeBlock("bash") .content("$ curl 'http://localhost/foo' -i -d 'content'")); - new CurlRequestSnippet().document(new OperationBuilder("request-with-content", - this.snippet.getOutputDirectory()).request("http://localhost/foo") - .content("content").build()); + new CurlRequestSnippet().document(operationBuilder("request-with-content") + .request("http://localhost/foo").content("content").build()); } @Test @@ -89,8 +83,7 @@ public class CurlRequestSnippetTests { .withContents( codeBlock("bash").content( "$ curl 'http://localhost/foo?param=value' -i")); - new CurlRequestSnippet().document(new OperationBuilder( - "request-with-query-string", this.snippet.getOutputDirectory()).request( + new CurlRequestSnippet().document(operationBuilder("request-with-query-string").request( "http://localhost/foo?param=value").build()); } @@ -99,8 +92,7 @@ public class CurlRequestSnippetTests { this.snippet.expectCurlRequest("post-request-with-query-string").withContents( codeBlock("bash").content( "$ curl 'http://localhost/foo?param=value' -i -X POST")); - new CurlRequestSnippet().document(new OperationBuilder( - "post-request-with-query-string", this.snippet.getOutputDirectory()) + new CurlRequestSnippet().document(operationBuilder("post-request-with-query-string") .request("http://localhost/foo?param=value").method("POST").build()); } @@ -110,8 +102,7 @@ public class CurlRequestSnippetTests { codeBlock("bash").content( "$ curl 'http://localhost/foo' -i -X POST -d 'k1=v1'")); new CurlRequestSnippet() - .document(new OperationBuilder("post-request-with-one-parameter", - this.snippet.getOutputDirectory()) + .document(operationBuilder("post-request-with-one-parameter") .request("http://localhost/foo").method("POST").param("k1", "v1") .build()); } @@ -123,10 +114,10 @@ public class CurlRequestSnippetTests { codeBlock("bash").content( "$ curl 'http://localhost/foo' -i -X POST" + " -d 'k1=v1&k1=v1-bis&k2=v2'")); - new CurlRequestSnippet().document(new OperationBuilder( - "post-request-with-multiple-parameters", this.snippet - .getOutputDirectory()).request("http://localhost/foo") - .method("POST").param("k1", "v1", "v1-bis").param("k2", "v2").build()); + new CurlRequestSnippet() + .document(operationBuilder("post-request-with-multiple-parameters") + .request("http://localhost/foo").method("POST") + .param("k1", "v1", "v1-bis").param("k2", "v2").build()); } @Test @@ -136,10 +127,10 @@ public class CurlRequestSnippetTests { .withContents( codeBlock("bash").content( "$ curl 'http://localhost/foo' -i -X POST -d 'k1=a%26b'")); - new CurlRequestSnippet().document(new OperationBuilder( - "post-request-with-url-encoded-parameter", this.snippet - .getOutputDirectory()).request("http://localhost/foo") - .method("POST").param("k1", "a&b").build()); + new CurlRequestSnippet().document(operationBuilder( + "post-request-with-url-encoded-parameter") + .request("http://localhost/foo").method("POST").param("k1", "a&b") + .build()); } @Test @@ -150,10 +141,10 @@ public class CurlRequestSnippetTests { codeBlock("bash") .content( "$ curl 'http://localhost/foo?a=alpha' -i -X POST -d 'b=bravo'")); - new CurlRequestSnippet().document(new OperationBuilder( - "post-request-with-query-string-and-parameter", this.snippet - .getOutputDirectory()).request("http://localhost/foo?a=alpha") - .method("POST").param("b", "bravo").build()); + new CurlRequestSnippet().document(operationBuilder( + "post-request-with-query-string-and-parameter") + .request("http://localhost/foo?a=alpha").method("POST") + .param("b", "bravo").build()); } @Test @@ -165,10 +156,10 @@ public class CurlRequestSnippetTests { codeBlock("bash") .content( "$ curl 'http://localhost/foo?a=alpha' -i -X POST -d 'b=bravo'")); - new CurlRequestSnippet().document(new OperationBuilder( - "post-request-with-overlapping-query-string-and-parameters", this.snippet - .getOutputDirectory()).request("http://localhost/foo?a=alpha") - .method("POST").param("a", "alpha").param("b", "bravo").build()); + new CurlRequestSnippet().document(operationBuilder( + "post-request-with-overlapping-query-string-and-parameters") + .request("http://localhost/foo?a=alpha").method("POST") + .param("a", "alpha").param("b", "bravo").build()); } @Test @@ -176,8 +167,7 @@ public class CurlRequestSnippetTests { this.snippet.expectCurlRequest("put-request-with-one-parameter").withContents( codeBlock("bash").content( "$ curl 'http://localhost/foo' -i -X PUT -d 'k1=v1'")); - new CurlRequestSnippet().document(new OperationBuilder( - "put-request-with-one-parameter", this.snippet.getOutputDirectory()) + new CurlRequestSnippet().document(operationBuilder("put-request-with-one-parameter") .request("http://localhost/foo").method("PUT").param("k1", "v1").build()); } @@ -188,11 +178,9 @@ public class CurlRequestSnippetTests { codeBlock("bash").content( "$ curl 'http://localhost/foo' -i -X PUT" + " -d 'k1=v1&k1=v1-bis&k2=v2'")); - new CurlRequestSnippet() - .document(new OperationBuilder("put-request-with-multiple-parameters", - this.snippet.getOutputDirectory()) - .request("http://localhost/foo").method("PUT").param("k1", "v1") - .param("k1", "v1-bis").param("k2", "v2").build()); + new CurlRequestSnippet().document(operationBuilder("put-request-with-multiple-parameters") + .request("http://localhost/foo").method("PUT").param("k1", "v1") + .param("k1", "v1-bis").param("k2", "v2").build()); } @Test @@ -201,9 +189,8 @@ public class CurlRequestSnippetTests { .withContents( codeBlock("bash").content( "$ curl 'http://localhost/foo' -i -X PUT -d 'k1=a%26b'")); - new CurlRequestSnippet().document(new OperationBuilder( - "put-request-with-url-encoded-parameter", this.snippet - .getOutputDirectory()).request("http://localhost/foo") + new CurlRequestSnippet().document(operationBuilder( + "put-request-with-url-encoded-parameter").request("http://localhost/foo") .method("PUT").param("k1", "a&b").build()); } @@ -213,8 +200,8 @@ public class CurlRequestSnippetTests { codeBlock("bash").content( "$ curl 'http://localhost/foo' -i" + " -H 'Content-Type: application/json' -H 'a: alpha'")); - new CurlRequestSnippet().document(new OperationBuilder("request-with-headers", - this.snippet.getOutputDirectory()).request("http://localhost/foo") + new CurlRequestSnippet().document(operationBuilder("request-with-headers") + .request("http://localhost/foo") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header("a", "alpha").build()); } @@ -226,8 +213,7 @@ public class CurlRequestSnippetTests { + "'metadata={\"description\": \"foo\"}'"; this.snippet.expectCurlRequest("multipart-post-no-original-filename") .withContents(codeBlock("bash").content(expectedContent)); - new CurlRequestSnippet().document(new OperationBuilder( - "multipart-post-no-original-filename", this.snippet.getOutputDirectory()) + new CurlRequestSnippet().document(operationBuilder("multipart-post-no-original-filename") .request("http://localhost/upload").method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("metadata", "{\"description\": \"foo\"}".getBytes()).build()); @@ -240,8 +226,7 @@ public class CurlRequestSnippetTests { + "'image=@documents/images/example.png;type=image/png'"; this.snippet.expectCurlRequest("multipart-post-with-content-type").withContents( codeBlock("bash").content(expectedContent)); - new CurlRequestSnippet().document(new OperationBuilder( - "multipart-post-with-content-type", this.snippet.getOutputDirectory()) + new CurlRequestSnippet().document(operationBuilder("multipart-post-with-content-type") .request("http://localhost/upload").method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("image", new byte[0]) @@ -256,9 +241,8 @@ public class CurlRequestSnippetTests { + "'image=@documents/images/example.png'"; this.snippet.expectCurlRequest("multipart-post").withContents( codeBlock("bash").content(expectedContent)); - new CurlRequestSnippet().document(new OperationBuilder("multipart-post", - this.snippet.getOutputDirectory()).request("http://localhost/upload") - .method("POST") + new CurlRequestSnippet().document(operationBuilder("multipart-post") + .request("http://localhost/upload").method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("image", new byte[0]) .submittedFileName("documents/images/example.png").build()); @@ -272,8 +256,7 @@ public class CurlRequestSnippetTests { + "-F 'b=banana'"; this.snippet.expectCurlRequest("multipart-post-with-parameters").withContents( codeBlock("bash").content(expectedContent)); - new CurlRequestSnippet().document(new OperationBuilder( - "multipart-post-with-parameters", this.snippet.getOutputDirectory()) + new CurlRequestSnippet().document(operationBuilder("multipart-post-with-parameters") .request("http://localhost/upload").method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("image", new byte[0]) @@ -281,34 +264,30 @@ public class CurlRequestSnippetTests { .param("a", "apple", "avocado").param("b", "banana").build()); } - @Test - public void customAttributes() throws IOException { - this.snippet.expectCurlRequest("custom-attributes").withContents( - containsString("curl request title")); - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("curl-request")) - .willReturn( - new FileSystemResource( - "src/test/resources/custom-snippet-templates/curl-request-with-title.snippet")); - new CurlRequestSnippet(attributes(key("title").value("curl request title"))) - .document(new OperationBuilder("custom-attributes", this.snippet - .getOutputDirectory()) - .attribute(TemplateEngine.class.getName(), - new MustacheTemplateEngine(resolver)) - .request("http://localhost/foo").build()); - } - @Test public void basicAuthCredentialsAreSuppliedUsingUserOption() throws IOException { this.snippet.expectCurlRequest("basic-auth").withContents( codeBlock("bash").content( "$ curl 'http://localhost/foo' -i -u 'user:secret'")); - new CurlRequestSnippet().document(new OperationBuilder("basic-auth", this.snippet - .getOutputDirectory()) + new CurlRequestSnippet().document(operationBuilder("basic-auth") .request("http://localhost/foo") .header(HttpHeaders.AUTHORIZATION, "Basic " + Base64Utils.encodeToString("user:secret".getBytes())) .build()); } + @Test + public void customAttributes() throws IOException { + this.snippet.expectCurlRequest("custom-attributes").withContents( + containsString("curl request title")); + TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); + given(resolver.resolveTemplateResource("curl-request")).willReturn( + snippetResource("curl-request-with-title")); + new CurlRequestSnippet(attributes(key("title").value("curl request title"))) + .document(operationBuilder("custom-attributes") + .attribute(TemplateEngine.class.getName(), + new MustacheTemplateEngine(resolver)) + .request("http://localhost/foo").build()); + } + } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetFailureTests.java new file mode 100644 index 00000000..ca011d2b --- /dev/null +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetFailureTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.headers; + +import java.io.IOException; +import java.util.Arrays; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.snippet.SnippetFormats; +import org.springframework.restdocs.test.ExpectedSnippet; +import org.springframework.restdocs.test.OperationBuilder; + +import static org.hamcrest.CoreMatchers.endsWith; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; + +/** + * Tests for failures when rendering {@link RequestHeadersSnippet} due to missing or + * undocumented headers. + * + * @author Andy Wilkinson + */ +public class RequestHeadersSnippetFailureTests { + + @Rule + public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor()); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void missingRequestHeader() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(equalTo("Headers with the following names were not found" + + " in the request: [Accept]")); + new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description( + "one"))).document(new OperationBuilder("missing-request-headers", + this.snippet.getOutputDirectory()).request("http://localhost").build()); + } + + @Test + public void undocumentedRequestHeaderAndMissingRequestHeader() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(endsWith("Headers with the following names were not found" + + " in the request: [Accept]")); + new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description( + "one"))).document(new OperationBuilder( + "undocumented-request-header-and-missing-request-header", this.snippet + .getOutputDirectory()).request("http://localhost") + .header("X-Test", "test").build()); + } + +} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetTests.java index b5b2d1e4..ed076d26 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,26 +19,20 @@ package org.springframework.restdocs.headers; import java.io.IOException; import java.util.Arrays; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.core.io.FileSystemResource; -import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.AbstractSnippetTests; +import org.springframework.restdocs.snippet.SnippetFormat; import org.springframework.restdocs.templates.TemplateEngine; import org.springframework.restdocs.templates.TemplateResourceResolver; import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.restdocs.test.ExpectedSnippet; import org.springframework.restdocs.test.OperationBuilder; -import static org.hamcrest.CoreMatchers.endsWith; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.CoreMatchers.containsString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader; /** * Tests for {@link RequestHeadersSnippet}. @@ -46,13 +40,11 @@ import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader; * @author Andreas Evers * @author Andy Wilkinson */ -public class RequestHeadersSnippetTests { +public class RequestHeadersSnippetTests extends AbstractSnippetTests { - @Rule - public final ExpectedException thrown = ExpectedException.none(); - - @Rule - public final ExpectedSnippet snippet = new ExpectedSnippet(); + public RequestHeadersSnippetTests(String name, SnippetFormat snippetFormat) { + super(name, snippetFormat); + } @Test public void requestWithHeaders() throws IOException { @@ -67,14 +59,13 @@ public class RequestHeadersSnippetTests { .description("three"), headerWithName("Accept-Language") .description("four"), headerWithName("Cache-Control") .description("five"), - headerWithName("Connection").description("six"))) - .document(new OperationBuilder("request-with-headers", this.snippet - .getOutputDirectory()).request("http://localhost") - .header("X-Test", "test").header("Accept", "*/*") - .header("Accept-Encoding", "gzip, deflate") - .header("Accept-Language", "en-US,en;q=0.5") - .header("Cache-Control", "max-age=0") - .header("Connection", "keep-alive").build()); + headerWithName("Connection").description("six"))).document(operationBuilder( + "request-with-headers").request("http://localhost") + .header("X-Test", "test").header("Accept", "*/*") + .header("Accept-Encoding", "gzip, deflate") + .header("Accept-Language", "en-US,en;q=0.5") + .header("Cache-Control", "max-age=0").header("Connection", "keep-alive") + .build()); } @Test @@ -83,8 +74,7 @@ public class RequestHeadersSnippetTests { .expectRequestHeaders("case-insensitive-request-headers") .withContents(tableWithHeader("Name", "Description").row("X-Test", "one")); new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description( - "one"))).document(new OperationBuilder( - "case-insensitive-request-headers", this.snippet.getOutputDirectory()) + "one"))).document(operationBuilder("case-insensitive-request-headers") .request("/").header("X-test", "test").build()); } @@ -97,37 +87,28 @@ public class RequestHeadersSnippetTests { } @Test - public void missingRequestHeader() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(equalTo("Headers with the following names were not found" - + " in the request: [Accept]")); - new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description( - "one"))).document(new OperationBuilder("missing-request-headers", - this.snippet.getOutputDirectory()).request("http://localhost").build()); - } - - @Test - public void undocumentedRequestHeaderAndMissingRequestHeader() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(endsWith("Headers with the following names were not found" - + " in the request: [Accept]")); - new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description( - "one"))).document(new OperationBuilder( - "undocumented-request-header-and-missing-request-header", this.snippet - .getOutputDirectory()).request("http://localhost") - .header("X-Test", "test").build()); + public void requestHeadersWithCustomAttributes() throws IOException { + this.snippet.expectRequestHeaders("request-headers-with-custom-attributes") + .withContents(containsString("Custom title")); + TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); + given(resolver.resolveTemplateResource("request-headers")).willReturn( + snippetResource("request-headers-with-title")); + new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description( + "one")), attributes(key("title").value("Custom title"))) + .document(operationBuilder("request-headers-with-custom-attributes") + .attribute(TemplateEngine.class.getName(), + new MustacheTemplateEngine(resolver)) + .request("http://localhost").header("X-Test", "test").build()); } @Test public void requestHeadersWithCustomDescriptorAttributes() throws IOException { - this.snippet.expectRequestHeaders("request-headers-with-custom-attributes") - .withContents(// - tableWithHeader("Name", "Description", "Foo") - .row("X-Test", "one", "alpha") - .row("Accept-Encoding", "two", "bravo") - .row("Accept", "three", "charlie")); + this.snippet.expectRequestHeaders( + "request-headers-with-custom-descriptor-attributes").withContents(// + tableWithHeader("Name", "Description", "Foo") + .row("X-Test", "one", "alpha") + .row("Accept-Encoding", "two", "bravo") + .row("Accept", "three", "charlie")); TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); given(resolver.resolveTemplateResource("request-headers")).willReturn( snippetResource("request-headers-with-extra-column")); @@ -137,34 +118,12 @@ public class RequestHeadersSnippetTests { headerWithName("Accept-Encoding").description("two").attributes( key("foo").value("bravo")), headerWithName("Accept").description("three").attributes( - key("foo").value("charlie")))).document(new OperationBuilder( - "request-headers-with-custom-attributes", this.snippet - .getOutputDirectory()) + key("foo").value("charlie")))).document(operationBuilder( + "request-headers-with-custom-descriptor-attributes") .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)).request("http://localhost") .header("X-Test", "test").header("Accept-Encoding", "gzip, deflate") .header("Accept", "*/*").build()); } - @Test - public void requestHeadersWithCustomAttributes() throws IOException { - this.snippet.expectRequestHeaders("request-headers-with-custom-attributes") - .withContents(startsWith(".Custom title")); - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-headers")).willReturn( - snippetResource("request-headers-with-title")); - new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description( - "one")), attributes(key("title").value("Custom title"))) - .document(new OperationBuilder("request-headers-with-custom-attributes", - this.snippet.getOutputDirectory()) - .attribute(TemplateEngine.class.getName(), - new MustacheTemplateEngine(resolver)) - .request("http://localhost").header("X-Test", "test").build()); - } - - private FileSystemResource snippetResource(String name) { - return new FileSystemResource("src/test/resources/custom-snippet-templates/" - + name + ".snippet"); - } - } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetFailureTests.java new file mode 100644 index 00000000..382e9125 --- /dev/null +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetFailureTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.headers; + +import java.io.IOException; +import java.util.Arrays; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.snippet.SnippetFormats; +import org.springframework.restdocs.test.ExpectedSnippet; +import org.springframework.restdocs.test.OperationBuilder; + +import static org.hamcrest.CoreMatchers.endsWith; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; + +/** + * Tests for failures when rendering {@link ResponseHeadersSnippet} due to missing or + * undocumented headers. + * + * @author Andy Wilkinson + */ +public class ResponseHeadersSnippetFailureTests { + + @Rule + public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor()); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void missingResponseHeader() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(equalTo("Headers with the following names were not found" + + " in the response: [Content-Type]")); + new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type") + .description("one"))).document(new OperationBuilder( + "missing-response-headers", this.snippet.getOutputDirectory()).response() + .build()); + } + + @Test + public void undocumentedResponseHeaderAndMissingResponseHeader() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(endsWith("Headers with the following names were not found" + + " in the response: [Content-Type]")); + new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type") + .description("one"))).document(new OperationBuilder( + "undocumented-response-header-and-missing-response-header", this.snippet + .getOutputDirectory()).response().header("X-Test", "test") + .build()); + } + +} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetTests.java index 24487094..f58805e0 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,26 +19,20 @@ package org.springframework.restdocs.headers; import java.io.IOException; import java.util.Arrays; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.core.io.FileSystemResource; -import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.AbstractSnippetTests; +import org.springframework.restdocs.snippet.SnippetFormat; import org.springframework.restdocs.templates.TemplateEngine; import org.springframework.restdocs.templates.TemplateResourceResolver; import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.restdocs.test.ExpectedSnippet; import org.springframework.restdocs.test.OperationBuilder; -import static org.hamcrest.CoreMatchers.endsWith; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.CoreMatchers.containsString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader; /** * Tests for {@link ResponseHeadersSnippet}. @@ -46,13 +40,11 @@ import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader; * @author Andreas Evers * @author Andy Wilkinson */ -public class ResponseHeadersSnippetTests { +public class ResponseHeadersSnippetTests extends AbstractSnippetTests { - @Rule - public final ExpectedException thrown = ExpectedException.none(); - - @Rule - public final ExpectedSnippet snippet = new ExpectedSnippet(); + public ResponseHeadersSnippetTests(String name, SnippetFormat snippetFormat) { + super(name, snippetFormat); + } @Test public void responseWithHeaders() throws IOException { @@ -65,8 +57,7 @@ public class ResponseHeadersSnippetTests { headerWithName("Content-Type").description("two"), headerWithName("Etag") .description("three"), headerWithName("Cache-Control") .description("five"), headerWithName("Vary").description("six"))) - .document(new OperationBuilder("response-headers", this.snippet - .getOutputDirectory()).response().header("X-Test", "test") + .document(operationBuilder("response-headers").response().header("X-Test", "test") .header("Content-Type", "application/json") .header("Etag", "lskjadldj3ii32l2ij23") .header("Cache-Control", "max-age=0") @@ -79,9 +70,31 @@ public class ResponseHeadersSnippetTests { .expectResponseHeaders("case-insensitive-response-headers") .withContents(tableWithHeader("Name", "Description").row("X-Test", "one")); new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description( - "one"))).document(new OperationBuilder( - "case-insensitive-response-headers", this.snippet.getOutputDirectory()) - .response().header("X-test", "test").build()); + "one"))).document(operationBuilder("case-insensitive-response-headers").response() + .header("X-test", "test").build()); + } + + @Test + public void undocumentedResponseHeader() throws IOException { + new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description( + "one"))).document(new OperationBuilder("undocumented-response-header", + this.snippet.getOutputDirectory()).response().header("X-Test", "test") + .header("Content-Type", "*/*").build()); + } + + @Test + public void responseHeadersWithCustomAttributes() throws IOException { + this.snippet.expectResponseHeaders("response-headers-with-custom-attributes") + .withContents(containsString("Custom title")); + TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); + given(resolver.resolveTemplateResource("response-headers")).willReturn( + snippetResource("response-headers-with-title")); + new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description( + "one")), attributes(key("title").value("Custom title"))) + .document(operationBuilder("response-headers-with-custom-attributes") + .attribute(TemplateEngine.class.getName(), + new MustacheTemplateEngine(resolver)).response() + .header("X-Test", "test").build()); } @Test @@ -101,67 +114,12 @@ public class ResponseHeadersSnippetTests { headerWithName("Content-Type").description("two").attributes( key("foo").value("bravo")), headerWithName("Etag").description("three").attributes( - key("foo").value("charlie")))).document(new OperationBuilder( - "response-headers-with-custom-attributes", this.snippet - .getOutputDirectory()) + key("foo").value("charlie")))).document(operationBuilder( + "response-headers-with-custom-attributes") .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)).response() .header("X-Test", "test").header("Content-Type", "application/json") .header("Etag", "lskjadldj3ii32l2ij23").build()); } - @Test - public void responseHeadersWithCustomAttributes() throws IOException { - this.snippet.expectResponseHeaders("response-headers-with-custom-attributes") - .withContents(startsWith(".Custom title")); - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("response-headers")).willReturn( - snippetResource("response-headers-with-title")); - new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description( - "one")), attributes(key("title").value("Custom title"))) - .document(new OperationBuilder("response-headers-with-custom-attributes", - this.snippet.getOutputDirectory()) - .attribute(TemplateEngine.class.getName(), - new MustacheTemplateEngine(resolver)).response() - .header("X-Test", "test").build()); - } - - @Test - public void undocumentedResponseHeader() throws IOException { - new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description( - "one"))).document(new OperationBuilder("undocumented-response-header", - this.snippet.getOutputDirectory()).response().header("X-Test", "test") - .header("Content-Type", "*/*").build()); - } - - @Test - public void missingResponseHeader() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(equalTo("Headers with the following names were not found" - + " in the response: [Content-Type]")); - new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type") - .description("one"))).document(new OperationBuilder( - "missing-response-headers", this.snippet.getOutputDirectory()).response() - .build()); - } - - @Test - public void undocumentedResponseHeaderAndMissingResponseHeader() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(endsWith("Headers with the following names were not found" - + " in the response: [Content-Type]")); - new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type") - .description("one"))).document(new OperationBuilder( - "undocumented-response-header-and-missing-response-header", this.snippet - .getOutputDirectory()).response().header("X-Test", "test") - .build()); - } - - private FileSystemResource snippetResource(String name) { - return new FileSystemResource("src/test/resources/custom-snippet-templates/" - + name + ".snippet"); - } - } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java index f948fb10..41e90bfb 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,16 +18,14 @@ package org.springframework.restdocs.http; import java.io.IOException; -import org.junit.Rule; import org.junit.Test; -import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.restdocs.AbstractSnippetTests; +import org.springframework.restdocs.snippet.SnippetFormat; import org.springframework.restdocs.templates.TemplateEngine; import org.springframework.restdocs.templates.TemplateResourceResolver; import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.restdocs.test.ExpectedSnippet; -import org.springframework.restdocs.test.OperationBuilder; import org.springframework.web.bind.annotation.RequestMethod; import static org.hamcrest.CoreMatchers.containsString; @@ -35,21 +33,20 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.restdocs.test.SnippetMatchers.httpRequest; /** * Tests for {@link HttpRequestSnippet}. * * @author Andy Wilkinson * @author Jonathan Pearlin - * */ -public class HttpRequestSnippetTests { +public class HttpRequestSnippetTests extends AbstractSnippetTests { private static final String BOUNDARY = "6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm"; - @Rule - public final ExpectedSnippet snippet = new ExpectedSnippet(); + public HttpRequestSnippetTests(String name, SnippetFormat snippetFormat) { + super(name, snippetFormat); + } @Test public void getRequest() throws IOException { @@ -57,9 +54,8 @@ public class HttpRequestSnippetTests { httpRequest(RequestMethod.GET, "/foo").header("Alpha", "a").header( HttpHeaders.HOST, "localhost")); - new HttpRequestSnippet().document(new OperationBuilder("get-request", - this.snippet.getOutputDirectory()).request("http://localhost/foo") - .header("Alpha", "a").build()); + new HttpRequestSnippet().document(operationBuilder("get-request") + .request("http://localhost/foo").header("Alpha", "a").build()); } @Test @@ -68,8 +64,7 @@ public class HttpRequestSnippetTests { httpRequest(RequestMethod.GET, "/foo?bar=baz").header(HttpHeaders.HOST, "localhost")); - new HttpRequestSnippet().document(new OperationBuilder( - "get-request-with-query-string", this.snippet.getOutputDirectory()) + new HttpRequestSnippet().document(operationBuilder("get-request-with-query-string") .request("http://localhost/foo?bar=baz").build()); } @@ -81,8 +76,7 @@ public class HttpRequestSnippetTests { .header(HttpHeaders.HOST, "localhost").content(content) .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); - new HttpRequestSnippet().document(new OperationBuilder( - "post-request-with-content", this.snippet.getOutputDirectory()) + new HttpRequestSnippet().document(operationBuilder("post-request-with-content") .request("http://localhost/foo").method("POST").content(content).build()); } @@ -97,8 +91,7 @@ public class HttpRequestSnippetTests { .header(HttpHeaders.CONTENT_LENGTH, contentBytes.length) .content(japaneseContent)); - new HttpRequestSnippet().document(new OperationBuilder( - "post-request-with-charset", this.snippet.getOutputDirectory()) + new HttpRequestSnippet().document(operationBuilder("post-request-with-charset") .request("http://localhost/foo").method("POST") .header("Content-Type", "text/plain;charset=UTF-8").content(contentBytes) .build()); @@ -112,8 +105,7 @@ public class HttpRequestSnippetTests { .header("Content-Type", "application/x-www-form-urlencoded") .content("b%26r=baz&a=alpha")); - new HttpRequestSnippet().document(new OperationBuilder( - "post-request-with-parameter", this.snippet.getOutputDirectory()) + new HttpRequestSnippet().document(operationBuilder("post-request-with-parameter") .request("http://localhost/foo").method("POST").param("b&r", "baz") .param("a", "alpha").build()); } @@ -126,8 +118,7 @@ public class HttpRequestSnippetTests { .header(HttpHeaders.HOST, "localhost").content(content) .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); - new HttpRequestSnippet().document(new OperationBuilder( - "put-request-with-content", this.snippet.getOutputDirectory()) + new HttpRequestSnippet().document(operationBuilder("put-request-with-content") .request("http://localhost/foo").method("PUT").content(content).build()); } @@ -139,8 +130,7 @@ public class HttpRequestSnippetTests { .header("Content-Type", "application/x-www-form-urlencoded") .content("b%26r=baz&a=alpha")); - new HttpRequestSnippet().document(new OperationBuilder( - "put-request-with-parameter", this.snippet.getOutputDirectory()) + new HttpRequestSnippet().document(operationBuilder("put-request-with-parameter") .request("http://localhost/foo").method("PUT").param("b&r", "baz") .param("a", "alpha").build()); } @@ -154,9 +144,8 @@ public class HttpRequestSnippetTests { .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) .header(HttpHeaders.HOST, "localhost").content(expectedContent)); - new HttpRequestSnippet().document(new OperationBuilder("multipart-post", - this.snippet.getOutputDirectory()).request("http://localhost/upload") - .method("POST") + new HttpRequestSnippet().document(operationBuilder("multipart-post") + .request("http://localhost/upload").method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("image", "<< data >>".getBytes()).build()); } @@ -177,8 +166,7 @@ public class HttpRequestSnippetTests { .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) .header(HttpHeaders.HOST, "localhost").content(expectedContent)); - new HttpRequestSnippet().document(new OperationBuilder( - "multipart-post-with-parameters", this.snippet.getOutputDirectory()) + new HttpRequestSnippet().document(operationBuilder("multipart-post-with-parameters") .request("http://localhost/upload").method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .param("a", "apple", "avocado").param("b", "banana") @@ -195,8 +183,7 @@ public class HttpRequestSnippetTests { .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) .header(HttpHeaders.HOST, "localhost").content(expectedContent)); - new HttpRequestSnippet().document(new OperationBuilder( - "multipart-post-with-content-type", this.snippet.getOutputDirectory()) + new HttpRequestSnippet().document(operationBuilder("multipart-post-with-content-type") .request("http://localhost/upload").method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("image", "<< data >>".getBytes()) @@ -208,8 +195,8 @@ public class HttpRequestSnippetTests { this.snippet.expectHttpRequest("get-request-custom-host").withContents( httpRequest(RequestMethod.GET, "/foo").header(HttpHeaders.HOST, "api.example.com")); - new HttpRequestSnippet().document(new OperationBuilder("get-request-custom-host", - this.snippet.getOutputDirectory()).request("http://localhost/foo") + new HttpRequestSnippet().document(operationBuilder("get-request-custom-host") + .request("http://localhost/foo") .header(HttpHeaders.HOST, "api.example.com").build()); } @@ -218,13 +205,10 @@ public class HttpRequestSnippetTests { this.snippet.expectHttpRequest("request-with-snippet-attributes").withContents( containsString("Title for the request")); TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("http-request")) - .willReturn( - new FileSystemResource( - "src/test/resources/custom-snippet-templates/http-request-with-title.snippet")); + given(resolver.resolveTemplateResource("http-request")).willReturn( + snippetResource("http-request-with-title")); new HttpRequestSnippet(attributes(key("title").value("Title for the request"))) - .document(new OperationBuilder("request-with-snippet-attributes", - this.snippet.getOutputDirectory()) + .document(operationBuilder("request-with-snippet-attributes") .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) .request("http://localhost/foo").build()); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpResponseSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpResponseSnippetTests.java index 4959e777..96bc7be4 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpResponseSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpResponseSnippetTests.java @@ -18,24 +18,21 @@ package org.springframework.restdocs.http; import java.io.IOException; -import org.junit.Rule; import org.junit.Test; -import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.restdocs.AbstractSnippetTests; +import org.springframework.restdocs.snippet.SnippetFormat; import org.springframework.restdocs.templates.TemplateEngine; import org.springframework.restdocs.templates.TemplateResourceResolver; import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.restdocs.test.ExpectedSnippet; -import org.springframework.restdocs.test.OperationBuilder; import static org.hamcrest.CoreMatchers.containsString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.restdocs.test.SnippetMatchers.httpResponse; /** * Tests for {@link HttpResponseSnippet}. @@ -43,25 +40,24 @@ import static org.springframework.restdocs.test.SnippetMatchers.httpResponse; * @author Andy Wilkinson * @author Jonathan Pearlin */ -public class HttpResponseSnippetTests { +public class HttpResponseSnippetTests extends AbstractSnippetTests { - @Rule - public final ExpectedSnippet snippet = new ExpectedSnippet(); + public HttpResponseSnippetTests(String name, SnippetFormat snippetFormat) { + super(name, snippetFormat); + } @Test public void basicResponse() throws IOException { this.snippet.expectHttpResponse("basic-response").withContents( httpResponse(HttpStatus.OK)); - new HttpResponseSnippet().document(new OperationBuilder("basic-response", - this.snippet.getOutputDirectory()).build()); + new HttpResponseSnippet().document(operationBuilder("basic-response").build()); } @Test public void nonOkResponse() throws IOException { this.snippet.expectHttpResponse("non-ok-response").withContents( httpResponse(HttpStatus.BAD_REQUEST)); - new HttpResponseSnippet().document(new OperationBuilder("non-ok-response", - this.snippet.getOutputDirectory()).response() + new HttpResponseSnippet().document(operationBuilder("non-ok-response").response() .status(HttpStatus.BAD_REQUEST.value()).build()); } @@ -70,8 +66,7 @@ public class HttpResponseSnippetTests { this.snippet.expectHttpResponse("response-with-headers").withContents( httpResponse(HttpStatus.OK).header("Content-Type", "application/json") .header("a", "alpha")); - new HttpResponseSnippet().document(new OperationBuilder("response-with-headers", - this.snippet.getOutputDirectory()).response() + new HttpResponseSnippet().document(operationBuilder("response-with-headers").response() .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header("a", "alpha").build()); } @@ -82,8 +77,8 @@ public class HttpResponseSnippetTests { this.snippet.expectHttpResponse("response-with-content").withContents( httpResponse(HttpStatus.OK).content(content).header( HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); - new HttpResponseSnippet().document(new OperationBuilder("response-with-content", - this.snippet.getOutputDirectory()).response().content(content).build()); + new HttpResponseSnippet().document(operationBuilder("response-with-content").response() + .content(content).build()); } @Test @@ -95,8 +90,7 @@ public class HttpResponseSnippetTests { .header("Content-Type", "text/plain;charset=UTF-8") .content(japaneseContent) .header(HttpHeaders.CONTENT_LENGTH, contentBytes.length)); - new HttpResponseSnippet().document(new OperationBuilder("response-with-charset", - this.snippet.getOutputDirectory()).response() + new HttpResponseSnippet().document(operationBuilder("response-with-charset").response() .header("Content-Type", "text/plain;charset=UTF-8").content(contentBytes) .build()); } @@ -106,13 +100,10 @@ public class HttpResponseSnippetTests { this.snippet.expectHttpResponse("response-with-snippet-attributes").withContents( containsString("Title for the response")); TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("http-response")) - .willReturn( - new FileSystemResource( - "src/test/resources/custom-snippet-templates/http-response-with-title.snippet")); + given(resolver.resolveTemplateResource("http-response")).willReturn( + snippetResource("http-response-with-title")); new HttpResponseSnippet(attributes(key("title").value("Title for the response"))) - .document(new OperationBuilder("response-with-snippet-attributes", - this.snippet.getOutputDirectory()).attribute( + .document(operationBuilder("response-with-snippet-attributes").attribute( TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)).build()); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetFailureTests.java new file mode 100644 index 00000000..335127cf --- /dev/null +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetFailureTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.hypermedia; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.snippet.SnippetFormats; +import org.springframework.restdocs.test.ExpectedSnippet; +import org.springframework.restdocs.test.OperationBuilder; + +import static org.hamcrest.CoreMatchers.equalTo; + +/** + * Tests for failures when rendering {@link LinksSnippet} due to missing or undocumented + * links. + * + * @author Andy Wilkinson + */ +public class LinksSnippetFailureTests { + + @Rule + public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor()); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void undocumentedLink() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown.expectMessage(equalTo("Links with the following relations were not" + + " documented: [foo]")); + new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "bar")), + Collections.emptyList()).document(new OperationBuilder( + "undocumented-link", this.snippet.getOutputDirectory()).build()); + } + + @Test + public void missingLink() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown.expectMessage(equalTo("Links with the following relations were not" + + " found in the response: [foo]")); + new LinksSnippet(new StubLinkExtractor(), Arrays.asList(new LinkDescriptor("foo") + .description("bar"))).document(new OperationBuilder("missing-link", + this.snippet.getOutputDirectory()).build()); + } + + @Test + public void undocumentedLinkAndMissingLink() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown.expectMessage(equalTo("Links with the following relations were not" + + " documented: [a]. Links with the following relations were not" + + " found in the response: [foo]")); + new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha")), + Arrays.asList(new LinkDescriptor("foo").description("bar"))) + .document(new OperationBuilder("undocumented-link-and-missing-link", + this.snippet.getOutputDirectory()).build()); + } + +} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java index 07adce26..c3bd55a7 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java @@ -18,51 +18,29 @@ package org.springframework.restdocs.hypermedia; import java.io.IOException; import java.util.Arrays; -import java.util.Collections; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.core.io.FileSystemResource; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.AbstractSnippetTests; +import org.springframework.restdocs.snippet.SnippetFormat; import org.springframework.restdocs.templates.TemplateEngine; import org.springframework.restdocs.templates.TemplateResourceResolver; import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.restdocs.test.ExpectedSnippet; -import org.springframework.restdocs.test.OperationBuilder; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.CoreMatchers.containsString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader; /** * Tests for {@link LinksSnippet}. * * @author Andy Wilkinson */ -public class LinksSnippetTests { +public class LinksSnippetTests extends AbstractSnippetTests { - @Rule - public ExpectedSnippet snippet = new ExpectedSnippet(); - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Test - public void undocumentedLink() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown.expectMessage(equalTo("Links with the following relations were not" - + " documented: [foo]")); - new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "bar")), - Collections.emptyList()).document(new OperationBuilder( - "undocumented-link", this.snippet.getOutputDirectory()).build()); + public LinksSnippetTests(String name, SnippetFormat snippetFormat) { + super(name, snippetFormat); } @Test @@ -71,19 +49,8 @@ public class LinksSnippetTests { tableWithHeader("Relation", "Description").row("b", "Link b")); new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), Arrays.asList(new LinkDescriptor("a").ignored(), - new LinkDescriptor("b").description("Link b"))) - .document(new OperationBuilder("ignored-link", this.snippet - .getOutputDirectory()).build()); - } - - @Test - public void missingLink() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown.expectMessage(equalTo("Links with the following relations were not" - + " found in the response: [foo]")); - new LinksSnippet(new StubLinkExtractor(), Arrays.asList(new LinkDescriptor("foo") - .description("bar"))).document(new OperationBuilder("missing-link", - this.snippet.getOutputDirectory()).build()); + new LinkDescriptor("b").description("Link b"))).document(operationBuilder( + "ignored-link").build()); } @Test @@ -92,8 +59,7 @@ public class LinksSnippetTests { tableWithHeader("Relation", "Description").row("foo", "bar")); new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "blah")), Arrays.asList(new LinkDescriptor("foo").description("bar").optional())) - .document(new OperationBuilder("documented-optional-link", this.snippet - .getOutputDirectory()).build()); + .document(operationBuilder("documented-optional-link").build()); } @Test @@ -101,20 +67,8 @@ public class LinksSnippetTests { this.snippet.expectLinks("missing-optional-link").withContents( tableWithHeader("Relation", "Description").row("foo", "bar")); new LinksSnippet(new StubLinkExtractor(), Arrays.asList(new LinkDescriptor("foo") - .description("bar").optional())).document(new OperationBuilder( - "missing-optional-link", this.snippet.getOutputDirectory()).build()); - } - - @Test - public void undocumentedLinkAndMissingLink() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown.expectMessage(equalTo("Links with the following relations were not" - + " documented: [a]. Links with the following relations were not" - + " found in the response: [foo]")); - new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha")), - Arrays.asList(new LinkDescriptor("foo").description("bar"))) - .document(new OperationBuilder("undocumented-link-and-missing-link", - this.snippet.getOutputDirectory()).build()); + .description("bar").optional())) + .document(operationBuilder("missing-optional-link").build()); } @Test @@ -125,18 +79,32 @@ public class LinksSnippetTests { new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), Arrays.asList( new LinkDescriptor("a").description("one"), - new LinkDescriptor("b").description("two"))) - .document(new OperationBuilder("documented-links", this.snippet - .getOutputDirectory()).build()); + new LinkDescriptor("b").description("two"))).document(operationBuilder( + "documented-links").build()); + } + + @Test + public void linksWithCustomAttributes() throws IOException { + TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); + given(resolver.resolveTemplateResource("links")).willReturn( + snippetResource("links-with-title")); + this.snippet.expectLinks("links-with-custom-attributes").withContents( + containsString("Title for the links")); + + new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), + new Link("b", "bravo")), Arrays.asList( + new LinkDescriptor("a").description("one"), + new LinkDescriptor("b").description("two")), attributes(key("title") + .value("Title for the links"))).document(operationBuilder( + "links-with-custom-attributes").attribute(TemplateEngine.class.getName(), + new MustacheTemplateEngine(resolver)).build()); } @Test public void linksWithCustomDescriptorAttributes() throws IOException { TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("links")) - .willReturn( - new FileSystemResource( - "src/test/resources/custom-snippet-templates/links-with-extra-column.snippet")); + given(resolver.resolveTemplateResource("links")).willReturn( + snippetResource("links-with-extra-column")); this.snippet.expectLinks("links-with-custom-descriptor-attributes").withContents( tableWithHeader("Relation", "Description", "Foo") .row("a", "one", "alpha").row("b", "two", "bravo")); @@ -146,49 +114,10 @@ public class LinksSnippetTests { new LinkDescriptor("a").description("one").attributes( key("foo").value("alpha")), new LinkDescriptor("b").description("two").attributes( - key("foo").value("bravo")))).document(new OperationBuilder( - "links-with-custom-descriptor-attributes", this.snippet - .getOutputDirectory()).attribute(TemplateEngine.class.getName(), - new MustacheTemplateEngine(resolver)).build()); - } - - @Test - public void linksWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("links")) - .willReturn( - new FileSystemResource( - "src/test/resources/custom-snippet-templates/links-with-title.snippet")); - this.snippet.expectLinks("links-with-custom-attributes").withContents( - startsWith(".Title for the links")); - - new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), - new Link("b", "bravo")), Arrays.asList( - new LinkDescriptor("a").description("one"), - new LinkDescriptor("b").description("two")), attributes(key("title") - .value("Title for the links"))).document(new OperationBuilder( - "links-with-custom-attributes", this.snippet.getOutputDirectory()) - .attribute(TemplateEngine.class.getName(), - new MustacheTemplateEngine(resolver)).build()); - } - - private static class StubLinkExtractor implements LinkExtractor { - - private MultiValueMap linksByRel = new LinkedMultiValueMap<>(); - - @Override - public MultiValueMap extractLinks(OperationResponse response) - throws IOException { - return this.linksByRel; - } - - private StubLinkExtractor withLinks(Link... links) { - for (Link link : links) { - this.linksByRel.add(link.getRel(), link); - } - return this; - } - + key("foo").value("bravo")))).document(operationBuilder( + "links-with-custom-descriptor-attributes").attribute( + TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) + .build()); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/StubLinkExtractor.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/StubLinkExtractor.java new file mode 100644 index 00000000..237cc5b5 --- /dev/null +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/StubLinkExtractor.java @@ -0,0 +1,47 @@ +/* + * Copyright 2014-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.hypermedia; + +import java.io.IOException; + +import org.springframework.restdocs.operation.OperationResponse; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +/** + * Stub implementation of {@code LinkExtractor} for testing. + * + * @author Andy Wilkinson + */ +class StubLinkExtractor implements LinkExtractor { + + private MultiValueMap linksByRel = new LinkedMultiValueMap<>(); + + @Override + public MultiValueMap extractLinks(OperationResponse response) + throws IOException { + return this.linksByRel; + } + + StubLinkExtractor withLinks(Link... links) { + for (Link link : links) { + this.linksByRel.add(link.getRel(), link); + } + return this; + } + +} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/AsciidoctorRequestFieldsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/AsciidoctorRequestFieldsSnippetTests.java new file mode 100644 index 00000000..a56e8ed5 --- /dev/null +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/AsciidoctorRequestFieldsSnippetTests.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.payload; + +import java.io.IOException; +import java.util.Arrays; + +import org.junit.Rule; +import org.junit.Test; +import org.springframework.core.io.FileSystemResource; +import org.springframework.restdocs.snippet.SnippetFormats; +import org.springframework.restdocs.templates.TemplateEngine; +import org.springframework.restdocs.templates.TemplateResourceResolver; +import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.test.ExpectedSnippet; +import org.springframework.restdocs.test.OperationBuilder; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.snippet.SnippetFormats.asciidoctor; +import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader; + +/** + * Tests for {@link RequestFieldsSnippet} that are specific to Asciidoctor. + * + * @author Andy Wilkinson + */ +public class AsciidoctorRequestFieldsSnippetTests { + + @Rule + public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor()); + + @Test + public void requestFieldsWithListDescription() throws IOException { + TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); + given(resolver.resolveTemplateResource("request-fields")).willReturn( + snippetResource("request-fields-with-list-description")); + this.snippet.expectRequestFields("request-fields-with-list-description") + .withContents( + tableWithHeader(asciidoctor(), "Path", "Type", "Description") + // + .row("a", "String", String.format(" - one%n - two")) + .configuration("[cols=\"1,1,1a\"]")); + + new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description( + Arrays.asList("one", "two")))) + .document(new OperationBuilder("request-fields-with-list-description", + this.snippet.getOutputDirectory()) + .attribute(TemplateEngine.class.getName(), + new MustacheTemplateEngine(resolver)) + .request("http://localhost").content("{\"a\": \"foo\"}").build()); + } + + private FileSystemResource snippetResource(String name) { + return new FileSystemResource("src/test/resources/custom-snippet-templates/adoc/" + + name + ".snippet"); + } + +} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetFailureTests.java new file mode 100644 index 00000000..442616b6 --- /dev/null +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetFailureTests.java @@ -0,0 +1,162 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.payload; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.snippet.SnippetFormats; +import org.springframework.restdocs.test.ExpectedSnippet; +import org.springframework.restdocs.test.OperationBuilder; + +import static org.hamcrest.CoreMatchers.endsWith; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +/** + * Tests for failures when rendering {@link RequestFieldsSnippet} due to missing or + * undocumented fields. + * + * @author Andy Wilkinson + */ +public class RequestFieldsSnippetFailureTests { + + @Rule + public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor()); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void undocumentedRequestField() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(startsWith("The following parts of the payload were not" + + " documented:")); + new RequestFieldsSnippet(Collections.emptyList()) + .document(new OperationBuilder("undocumented-request-field", this.snippet + .getOutputDirectory()).request("http://localhost") + .content("{\"a\": 5}").build()); + } + + @Test + public void missingRequestField() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(equalTo("Fields with the following paths were not found" + + " in the payload: [a.b]")); + new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) + .document(new OperationBuilder("missing-request-fields", this.snippet + .getOutputDirectory()).request("http://localhost").content("{}") + .build()); + } + + @Test + public void missingOptionalRequestFieldWithNoTypeProvided() throws IOException { + this.thrown.expect(FieldTypeRequiredException.class); + new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one") + .optional())).document(new OperationBuilder( + "missing-optional-request-field-with-no-type", this.snippet + .getOutputDirectory()).request("http://localhost").content("{ }") + .build()); + } + + @Test + public void undocumentedRequestFieldAndMissingRequestField() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(startsWith("The following parts of the payload were not" + + " documented:")); + this.thrown + .expectMessage(endsWith("Fields with the following paths were not found" + + " in the payload: [a.b]")); + new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) + .document(new OperationBuilder( + "undocumented-request-field-and-missing-request-field", + this.snippet.getOutputDirectory()).request("http://localhost") + .content("{ \"a\": { \"c\": 5 }}").build()); + } + + @Test + public void undocumentedXmlRequestField() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(startsWith("The following parts of the payload were not" + + " documented:")); + new RequestFieldsSnippet(Collections.emptyList()) + .document(new OperationBuilder("undocumented-xml-request-field", + this.snippet.getOutputDirectory()) + .request("http://localhost") + .content("5") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build()); + } + + @Test + public void xmlRequestFieldWithNoType() throws IOException { + this.thrown.expect(FieldTypeRequiredException.class); + new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) + .document(new OperationBuilder("missing-xml-request", this.snippet + .getOutputDirectory()) + .request("http://localhost") + .content("5") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build()); + } + + @Test + public void missingXmlRequestField() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(equalTo("Fields with the following paths were not found" + + " in the payload: [a/b]")); + new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"), + fieldWithPath("a").description("one"))).document(new OperationBuilder( + "missing-xml-request-fields", this.snippet.getOutputDirectory()) + .request("http://localhost").content("") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build()); + } + + @Test + public void undocumentedXmlRequestFieldAndMissingXmlRequestField() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(startsWith("The following parts of the payload were not" + + " documented:")); + this.thrown + .expectMessage(endsWith("Fields with the following paths were not found" + + " in the payload: [a/b]")); + new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"))) + .document(new OperationBuilder( + "undocumented-xml-request-field-and-missing-xml-request-field", + this.snippet.getOutputDirectory()) + .request("http://localhost") + .content("5") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build()); + } + +} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java index 0fedbdee..7f9a665a 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,43 +18,33 @@ package org.springframework.restdocs.payload; import java.io.IOException; import java.util.Arrays; -import java.util.Collections; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.AbstractSnippetTests; +import org.springframework.restdocs.snippet.SnippetFormat; import org.springframework.restdocs.templates.TemplateEngine; import org.springframework.restdocs.templates.TemplateResourceResolver; import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.restdocs.test.ExpectedSnippet; -import org.springframework.restdocs.test.OperationBuilder; -import static org.hamcrest.CoreMatchers.endsWith; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.CoreMatchers.containsString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader; /** * Tests for {@link RequestFieldsSnippet}. * * @author Andy Wilkinson */ -public class RequestFieldsSnippetTests { +public class RequestFieldsSnippetTests extends AbstractSnippetTests { - @Rule - public final ExpectedException thrown = ExpectedException.none(); - - @Rule - public final ExpectedSnippet snippet = new ExpectedSnippet(); + public RequestFieldsSnippetTests(String name, SnippetFormat snippetFormat) { + super(name, snippetFormat); + } @Test public void mapRequestWithFields() throws IOException { @@ -65,9 +55,8 @@ public class RequestFieldsSnippetTests { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"), fieldWithPath("a.c").description("two"), - fieldWithPath("a").description("three"))).document(new OperationBuilder( - "map-request-with-fields", this.snippet.getOutputDirectory()) - .request("http://localhost") + fieldWithPath("a").description("three"))).document(operationBuilder( + "map-request-with-fields").request("http://localhost") .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build()); } @@ -80,24 +69,11 @@ public class RequestFieldsSnippetTests { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("[]a.b").description("one"), fieldWithPath("[]a.c").description("two"), fieldWithPath("[]a") - .description("three"))).document(new OperationBuilder( - "array-request-with-fields", this.snippet.getOutputDirectory()) - .request("http://localhost") + .description("three"))).document(operationBuilder( + "array-request-with-fields").request("http://localhost") .content("[{\"a\": {\"b\": 5}},{\"a\": {\"c\": \"charlie\"}}]").build()); } - @Test - public void undocumentedRequestField() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(startsWith("The following parts of the payload were not" - + " documented:")); - new RequestFieldsSnippet(Collections.emptyList()) - .document(new OperationBuilder("undocumented-request-field", this.snippet - .getOutputDirectory()).request("http://localhost") - .content("{\"a\": 5}").build()); - } - @Test public void ignoredRequestField() throws IOException { this.snippet.expectRequestFields("ignored-request-field").withContents( @@ -105,48 +81,25 @@ public class RequestFieldsSnippetTests { "Field b")); new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").ignored(), - fieldWithPath("b").description("Field b"))) - .document(new OperationBuilder("ignored-request-field", this.snippet - .getOutputDirectory()).request("http://localhost") - .content("{\"a\": 5, \"b\": 4}").build()); + fieldWithPath("b").description("Field b"))).document(operationBuilder( + "ignored-request-field").request("http://localhost") + .content("{\"a\": 5, \"b\": 4}").build()); } @Test - public void missingRequestField() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(equalTo("Fields with the following paths were not found" - + " in the payload: [a.b]")); - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) - .document(new OperationBuilder("missing-request-fields", this.snippet - .getOutputDirectory()).request("http://localhost").content("{}") - .build()); - } + public void requestFieldsWithCustomAttributes() throws IOException { + TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); + given(resolver.resolveTemplateResource("request-fields")).willReturn( + snippetResource("request-fields-with-title")); + this.snippet.expectRequestFields("request-fields-with-custom-attributes") + .withContents(containsString("Custom title")); - @Test - public void missingOptionalRequestFieldWithNoTypeProvided() throws IOException { - this.thrown.expect(FieldTypeRequiredException.class); - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one") - .optional())).document(new OperationBuilder( - "missing-optional-request-field-with-no-type", this.snippet - .getOutputDirectory()).request("http://localhost").content("{ }") - .build()); - } - - @Test - public void undocumentedRequestFieldAndMissingRequestField() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(startsWith("The following parts of the payload were not" - + " documented:")); - this.thrown - .expectMessage(endsWith("Fields with the following paths were not found" - + " in the payload: [a.b]")); - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) - .document(new OperationBuilder( - "undocumented-request-field-and-missing-request-field", - this.snippet.getOutputDirectory()).request("http://localhost") - .content("{ \"a\": { \"c\": 5 }}").build()); + new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")), + attributes(key("title").value("Custom title"))).document(operationBuilder( + "request-fields-with-custom-attributes") + .attribute(TemplateEngine.class.getName(), + new MustacheTemplateEngine(resolver)).request("http://localhost") + .content("{\"a\": \"foo\"}").build()); } @Test @@ -167,52 +120,13 @@ public class RequestFieldsSnippetTests { fieldWithPath("a.c").description("two").attributes( key("foo").value("bravo")), fieldWithPath("a").description("three").attributes( - key("foo").value("charlie")))).document(new OperationBuilder( - "request-fields-with-custom-descriptor-attributes", this.snippet - .getOutputDirectory()) + key("foo").value("charlie")))).document(operationBuilder( + "request-fields-with-custom-descriptor-attributes") .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)).request("http://localhost") .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build()); } - @Test - public void requestFieldsWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-fields")).willReturn( - snippetResource("request-fields-with-title")); - this.snippet.expectRequestFields("request-fields-with-custom-attributes") - .withContents(startsWith(".Custom title")); - - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")), - attributes(key("title").value("Custom title"))) - .document(new OperationBuilder("request-fields-with-custom-attributes", - this.snippet.getOutputDirectory()) - .attribute(TemplateEngine.class.getName(), - new MustacheTemplateEngine(resolver)) - .request("http://localhost").content("{\"a\": \"foo\"}").build()); - } - - @Test - public void requestFieldsWithListDescription() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-fields")).willReturn( - snippetResource("request-fields-with-list-description")); - this.snippet.expectRequestFields("request-fields-with-list-description") - .withContents( - tableWithHeader("Path", "Type", "Description") - // - .row("a", "String", String.format(" - one%n - two")) - .configuration("[cols=\"1,1,1a\"]")); - - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description( - Arrays.asList("one", "two")))) - .document(new OperationBuilder("request-fields-with-list-description", - this.snippet.getOutputDirectory()) - .attribute(TemplateEngine.class.getName(), - new MustacheTemplateEngine(resolver)) - .request("http://localhost").content("{\"a\": \"foo\"}").build()); - } - @Test public void xmlRequestFields() throws IOException { this.snippet.expectRequestFields("xml-request").withContents( @@ -221,78 +135,11 @@ public class RequestFieldsSnippetTests { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one") .type("b"), fieldWithPath("a/c").description("two").type("c"), - fieldWithPath("a").description("three").type("a"))) - .document(new OperationBuilder("xml-request", this.snippet - .getOutputDirectory()) - .request("http://localhost") - .content("5charlie") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - } - - @Test - public void undocumentedXmlRequestField() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(startsWith("The following parts of the payload were not" - + " documented:")); - new RequestFieldsSnippet(Collections.emptyList()) - .document(new OperationBuilder("undocumented-xml-request-field", - this.snippet.getOutputDirectory()) - .request("http://localhost") - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - } - - @Test - public void xmlRequestFieldWithNoType() throws IOException { - this.thrown.expect(FieldTypeRequiredException.class); - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) - .document(new OperationBuilder("missing-xml-request", this.snippet - .getOutputDirectory()) - .request("http://localhost") - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - } - - @Test - public void missingXmlRequestField() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(equalTo("Fields with the following paths were not found" - + " in the payload: [a/b]")); - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"), - fieldWithPath("a").description("one"))).document(new OperationBuilder( - "missing-xml-request-fields", this.snippet.getOutputDirectory()) - .request("http://localhost").content("") + fieldWithPath("a").description("three").type("a"))).document(operationBuilder( + "xml-request").request("http://localhost") + .content("5charlie") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) .build()); } - @Test - public void undocumentedXmlRequestFieldAndMissingXmlRequestField() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(startsWith("The following parts of the payload were not" - + " documented:")); - this.thrown - .expectMessage(endsWith("Fields with the following paths were not found" - + " in the payload: [a/b]")); - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"))) - .document(new OperationBuilder( - "undocumented-xml-request-field-and-missing-xml-request-field", - this.snippet.getOutputDirectory()) - .request("http://localhost") - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - } - - private FileSystemResource snippetResource(String name) { - return new FileSystemResource("src/test/resources/custom-snippet-templates/" - + name + ".snippet"); - } - } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetFailureTests.java new file mode 100644 index 00000000..d664b807 --- /dev/null +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetFailureTests.java @@ -0,0 +1,143 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.payload; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.snippet.SnippetFormats; +import org.springframework.restdocs.test.ExpectedSnippet; +import org.springframework.restdocs.test.OperationBuilder; + +import static org.hamcrest.CoreMatchers.endsWith; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + +/** + * Tests for failures when rendering {@link ResponseFieldsSnippet} due to missing or + * undocumented fields. + * + * @author Andy Wilkinson + */ +public class ResponseFieldsSnippetFailureTests { + + @Rule + public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor()); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void undocumentedXmlResponseField() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(startsWith("The following parts of the payload were not" + + " documented:")); + new ResponseFieldsSnippet(Collections.emptyList()) + .document(new OperationBuilder("undocumented-xml-response-field", + this.snippet.getOutputDirectory()) + .response() + .content("5") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build()); + } + + @Test + public void missingXmlAttribute() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(equalTo("Fields with the following paths were not found" + + " in the payload: [a/@id]")); + new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one") + .type("b"), fieldWithPath("a/@id").description("two").type("c"))) + .document(new OperationBuilder("missing-xml-attribute", this.snippet + .getOutputDirectory()) + .response() + .content("foo") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build()); + } + + @Test + public void documentedXmlAttributesAreRemoved() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown.expectMessage(equalTo(String + .format("The following parts of the payload were not documented:" + + "%nbar%n"))); + new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/@id").description("one") + .type("a"))).document(new OperationBuilder( + "documented-attribute-is-removed", this.snippet.getOutputDirectory()) + .response().content("bar") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build()); + } + + @Test + public void xmlResponseFieldWithNoType() throws IOException { + this.thrown.expect(FieldTypeRequiredException.class); + new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) + .document(new OperationBuilder("xml-response-no-field-type", this.snippet + .getOutputDirectory()) + .response() + .content("5") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build()); + } + + @Test + public void missingXmlResponseField() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(equalTo("Fields with the following paths were not found" + + " in the payload: [a/b]")); + new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"), + fieldWithPath("a").description("one"))).document(new OperationBuilder( + "missing-xml-response-field", this.snippet.getOutputDirectory()) + .response().content("") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build()); + } + + @Test + public void undocumentedXmlResponseFieldAndMissingXmlResponseField() + throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(startsWith("The following parts of the payload were not" + + " documented:")); + this.thrown + .expectMessage(endsWith("Fields with the following paths were not found" + + " in the payload: [a/b]")); + new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"))) + .document(new OperationBuilder( + "undocumented-xml-request-field-and-missing-xml-request-field", + this.snippet.getOutputDirectory()) + .response() + .content("5") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build()); + } + +} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java index ad6653d3..f4e00111 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,43 +18,33 @@ package org.springframework.restdocs.payload; import java.io.IOException; import java.util.Arrays; -import java.util.Collections; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.AbstractSnippetTests; +import org.springframework.restdocs.snippet.SnippetFormat; import org.springframework.restdocs.templates.TemplateEngine; import org.springframework.restdocs.templates.TemplateResourceResolver; import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.restdocs.test.ExpectedSnippet; -import org.springframework.restdocs.test.OperationBuilder; -import static org.hamcrest.CoreMatchers.endsWith; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.CoreMatchers.containsString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader; /** * Tests for {@link ResponseFieldsSnippet}. * * @author Andy Wilkinson */ -public class ResponseFieldsSnippetTests { +public class ResponseFieldsSnippetTests extends AbstractSnippetTests { - @Rule - public final ExpectedException thrown = ExpectedException.none(); - - @Rule - public final ExpectedSnippet snippet = new ExpectedSnippet(); + public ResponseFieldsSnippetTests(String name, SnippetFormat snippetFormat) { + super(name, snippetFormat); + } @Test public void mapResponseWithFields() throws IOException { @@ -69,14 +59,12 @@ public class ResponseFieldsSnippetTests { .description("three"), fieldWithPath("assets[]").description("four"), fieldWithPath("assets[].id").description("five"), - fieldWithPath("assets[].name").description("six"))) - .document(new OperationBuilder("map-response-with-fields", this.snippet - .getOutputDirectory()) - .response() - .content( - "{\"id\": 67,\"date\": \"2015-01-20\",\"assets\":" - + " [{\"id\":356,\"name\": \"sample\"}]}") - .build()); + fieldWithPath("assets[].name").description("six"))).document(operationBuilder( + "map-response-with-fields") + .response() + .content( + "{\"id\": 67,\"date\": \"2015-01-20\",\"assets\":" + + " [{\"id\":356,\"name\": \"sample\"}]}").build()); } @Test @@ -88,8 +76,7 @@ public class ResponseFieldsSnippetTests { new ResponseFieldsSnippet(Arrays.asList( fieldWithPath("[]a.b").description("one"), fieldWithPath("[]a.c") .description("two"), fieldWithPath("[]a").description("three"))) - .document(new OperationBuilder("array-response-with-fields", this.snippet - .getOutputDirectory()).response() + .document(operationBuilder("array-response-with-fields").response() .content("[{\"a\": {\"b\": 5}},{\"a\": {\"c\": \"charlie\"}}]") .build()); } @@ -101,8 +88,7 @@ public class ResponseFieldsSnippetTests { tableWithHeader("Path", "Type", "Description").row("[]", "String", "one")); new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("[]").description("one"))) - .document(new OperationBuilder("array-response", this.snippet - .getOutputDirectory()).response() + .document(operationBuilder("array-response").response() .content("[\"a\", \"b\", \"c\"]").build()); } @@ -113,10 +99,25 @@ public class ResponseFieldsSnippetTests { "Field b")); new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").ignored(), - fieldWithPath("b").description("Field b"))) - .document(new OperationBuilder("ignored-response-field", this.snippet - .getOutputDirectory()).response().content("{\"a\": 5, \"b\": 4}") - .build()); + fieldWithPath("b").description("Field b"))).document(operationBuilder( + "ignored-response-field").response().content("{\"a\": 5, \"b\": 4}") + .build()); + } + + @Test + public void responseFieldsWithCustomAttributes() throws IOException { + TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); + given(resolver.resolveTemplateResource("response-fields")).willReturn( + snippetResource("response-fields-with-title")); + this.snippet.expectResponseFields("response-fields-with-custom-attributes") + .withContents(containsString("Custom title")); + + new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")), + attributes(key("title").value("Custom title"))).document(operationBuilder( + "response-fields-with-custom-attributes") + .attribute(TemplateEngine.class.getName(), + new MustacheTemplateEngine(resolver)).response() + .content("{\"a\": \"foo\"}").build()); } @Test @@ -137,31 +138,13 @@ public class ResponseFieldsSnippetTests { fieldWithPath("a.c").description("two").attributes( key("foo").value("bravo")), fieldWithPath("a").description("three").attributes( - key("foo").value("charlie")))).document(new OperationBuilder( - "response-fields-with-custom-attributes", this.snippet - .getOutputDirectory()) + key("foo").value("charlie")))).document(operationBuilder( + "response-fields-with-custom-attributes") .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)).response() .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build()); } - @Test - public void responseFieldsWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("response-fields")).willReturn( - snippetResource("response-fields-with-title")); - this.snippet.expectResponseFields("response-fields-with-custom-attributes") - .withContents(startsWith(".Custom title")); - - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")), - attributes(key("title").value("Custom title"))) - .document(new OperationBuilder("response-fields-with-custom-attributes", - this.snippet.getOutputDirectory()) - .attribute(TemplateEngine.class.getName(), - new MustacheTemplateEngine(resolver)).response() - .content("{\"a\": \"foo\"}").build()); - } - @Test public void xmlResponseFields() throws IOException { this.snippet.expectResponseFields("xml-response").withContents( @@ -169,28 +152,10 @@ public class ResponseFieldsSnippetTests { .row("a/c", "c", "two").row("a", "a", "three")); new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one") .type("b"), fieldWithPath("a/c").description("two").type("c"), - fieldWithPath("a").description("three").type("a"))) - .document(new OperationBuilder("xml-response", this.snippet - .getOutputDirectory()) - .response() - .content("5charlie") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - } - - @Test - public void undocumentedXmlResponseField() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(startsWith("The following parts of the payload were not" - + " documented:")); - new ResponseFieldsSnippet(Collections.emptyList()) - .document(new OperationBuilder("undocumented-xml-response-field", - this.snippet.getOutputDirectory()) - .response() - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); + fieldWithPath("a").description("three").type("a"))).document(operationBuilder( + "xml-response").response().content("5charlie") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build()); } @Test @@ -200,30 +165,13 @@ public class ResponseFieldsSnippetTests { "a/@id", "c", "two")); new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one") .type("b"), fieldWithPath("a/@id").description("two").type("c"))) - .document(new OperationBuilder("xml-attribute", this.snippet - .getOutputDirectory()) + .document(operationBuilder("xml-attribute") .response() .content("foo") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) .build()); } - @Test - public void missingXmlAttribute() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(equalTo("Fields with the following paths were not found" - + " in the payload: [a/@id]")); - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one") - .type("b"), fieldWithPath("a/@id").description("two").type("c"))) - .document(new OperationBuilder("missing-xml-attribute", this.snippet - .getOutputDirectory()) - .response() - .content("foo") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - } - @Test public void missingOptionalXmlAttribute() throws IOException { this.snippet.expectResponseFields("missing-optional-xml-attribute").withContents( @@ -231,8 +179,7 @@ public class ResponseFieldsSnippetTests { "a/@id", "c", "two")); new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one") .type("b"), fieldWithPath("a/@id").description("two").type("c") - .optional())).document(new OperationBuilder( - "missing-optional-xml-attribute", this.snippet.getOutputDirectory()) + .optional())).document(operationBuilder("missing-optional-xml-attribute") .response().content("foo") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) .build()); @@ -243,76 +190,10 @@ public class ResponseFieldsSnippetTests { this.snippet.expectResponseFields("undocumented-attribute").withContents( tableWithHeader("Path", "Type", "Description").row("a", "a", "one")); new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one") - .type("a"))).document(new OperationBuilder("undocumented-attribute", - this.snippet.getOutputDirectory()).response() + .type("a"))).document(operationBuilder("undocumented-attribute").response() .content("bar") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) .build()); } - @Test - public void documentedXmlAttributesAreRemoved() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown.expectMessage(equalTo(String - .format("The following parts of the payload were not documented:" - + "%nbar%n"))); - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/@id").description("one") - .type("a"))).document(new OperationBuilder( - "documented-attribute-is-removed", this.snippet.getOutputDirectory()) - .response().content("bar") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - } - - @Test - public void xmlResponseFieldWithNoType() throws IOException { - this.thrown.expect(FieldTypeRequiredException.class); - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) - .document(new OperationBuilder("xml-response-no-field-type", this.snippet - .getOutputDirectory()) - .response() - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - } - - @Test - public void missingXmlResponseField() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(equalTo("Fields with the following paths were not found" - + " in the payload: [a/b]")); - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"), - fieldWithPath("a").description("one"))).document(new OperationBuilder( - "missing-xml-response-field", this.snippet.getOutputDirectory()) - .response().content("") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - } - - @Test - public void undocumentedXmlResponseFieldAndMissingXmlResponseField() - throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(startsWith("The following parts of the payload were not" - + " documented:")); - this.thrown - .expectMessage(endsWith("Fields with the following paths were not found" - + " in the payload: [a/b]")); - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"))) - .document(new OperationBuilder( - "undocumented-xml-request-field-and-missing-xml-request-field", - this.snippet.getOutputDirectory()) - .response() - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - } - - private FileSystemResource snippetResource(String name) { - return new FileSystemResource("src/test/resources/custom-snippet-templates/" - + name + ".snippet"); - } - } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetFailureTests.java new file mode 100644 index 00000000..7f9a07b8 --- /dev/null +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetFailureTests.java @@ -0,0 +1,85 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.request; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.snippet.SnippetFormats; +import org.springframework.restdocs.test.ExpectedSnippet; +import org.springframework.restdocs.test.OperationBuilder; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; + +/** + * Tests for failures when rendering {@link PathParametersSnippet} due to missing or + * undocumented path parameters. + * + * @author Andy Wilkinson + */ +public class PathParametersSnippetFailureTests { + + @Rule + public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor()); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void undocumentedPathParameter() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown.expectMessage(equalTo("Path parameters with the following names were" + + " not documented: [a]")); + new PathParametersSnippet(Collections.emptyList()) + .document(new OperationBuilder("undocumented-path-parameter", + this.snippet.getOutputDirectory()).attribute( + "org.springframework.restdocs.urlTemplate", "/{a}/").build()); + } + + @Test + public void missingPathParameter() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown.expectMessage(equalTo("Path parameters with the following names were" + + " not found in the request: [a]")); + new PathParametersSnippet( + Arrays.asList(parameterWithName("a").description("one"))) + .document(new OperationBuilder("missing-path-parameter", this.snippet + .getOutputDirectory()).attribute( + "org.springframework.restdocs.urlTemplate", "/").build()); + } + + @Test + public void undocumentedAndMissingPathParameters() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown.expectMessage(equalTo("Path parameters with the following names were" + + " not documented: [b]. Path parameters with the following" + + " names were not found in the request: [a]")); + new PathParametersSnippet( + Arrays.asList(parameterWithName("a").description("one"))) + .document(new OperationBuilder( + "undocumented-and-missing-path-parameters", this.snippet + .getOutputDirectory()).attribute( + "org.springframework.restdocs.urlTemplate", "/{b}").build()); + } + +} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java index 4f2ee0b2..9d148769 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java @@ -18,78 +18,30 @@ package org.springframework.restdocs.request; import java.io.IOException; import java.util.Arrays; -import java.util.Collections; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.core.io.FileSystemResource; -import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.AbstractSnippetTests; +import org.springframework.restdocs.snippet.SnippetFormat; import org.springframework.restdocs.templates.TemplateEngine; import org.springframework.restdocs.templates.TemplateResourceResolver; import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.restdocs.test.ExpectedSnippet; -import org.springframework.restdocs.test.OperationBuilder; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.CoreMatchers.containsString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader; -import static org.springframework.restdocs.test.SnippetMatchers.tableWithTitleAndHeader; /** * Tests for {@link PathParametersSnippet}. * * @author Andy Wilkinson - * */ -public class PathParametersSnippetTests { +public class PathParametersSnippetTests extends AbstractSnippetTests { - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Rule - public ExpectedSnippet snippet = new ExpectedSnippet(); - - @Test - public void undocumentedPathParameter() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown.expectMessage(equalTo("Path parameters with the following names were" - + " not documented: [a]")); - new PathParametersSnippet(Collections.emptyList()) - .document(new OperationBuilder("undocumented-path-parameter", - this.snippet.getOutputDirectory()).attribute( - "org.springframework.restdocs.urlTemplate", "/{a}/").build()); - } - - @Test - public void missingPathParameter() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown.expectMessage(equalTo("Path parameters with the following names were" - + " not found in the request: [a]")); - new PathParametersSnippet( - Arrays.asList(parameterWithName("a").description("one"))) - .document(new OperationBuilder("missing-path-parameter", this.snippet - .getOutputDirectory()).attribute( - "org.springframework.restdocs.urlTemplate", "/").build()); - } - - @Test - public void undocumentedAndMissingPathParameters() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown.expectMessage(equalTo("Path parameters with the following names were" - + " not documented: [b]. Path parameters with the following" - + " names were not found in the request: [a]")); - new PathParametersSnippet( - Arrays.asList(parameterWithName("a").description("one"))) - .document(new OperationBuilder( - "undocumented-and-missing-path-parameters", this.snippet - .getOutputDirectory()).attribute( - "org.springframework.restdocs.urlTemplate", "/{b}").build()); + public PathParametersSnippetTests(String name, SnippetFormat snippetFormat) { + super(name, snippetFormat); } @Test @@ -99,9 +51,9 @@ public class PathParametersSnippetTests { "one").row("b", "two")); new PathParametersSnippet(Arrays.asList( parameterWithName("a").description("one"), parameterWithName("b") - .description("two"))).document(new OperationBuilder( - "path-parameters", this.snippet.getOutputDirectory()).attribute( - "org.springframework.restdocs.urlTemplate", "/{a}/{b}").build()); + .description("two"))).document(operationBuilder("path-parameters") + .attribute("org.springframework.restdocs.urlTemplate", "/{a}/{b}") + .build()); } @Test @@ -110,10 +62,9 @@ public class PathParametersSnippetTests { tableWithTitleAndHeader("/{a}/{b}", "Parameter", "Description").row("b", "two")); new PathParametersSnippet(Arrays.asList(parameterWithName("a").ignored(), - parameterWithName("b").description("two"))) - .document(new OperationBuilder("ignored-path-parameter", this.snippet - .getOutputDirectory()).attribute( - "org.springframework.restdocs.urlTemplate", "/{a}/{b}").build()); + parameterWithName("b").description("two"))).document(operationBuilder( + "ignored-path-parameter").attribute( + "org.springframework.restdocs.urlTemplate", "/{a}/{b}").build()); } @Test @@ -124,11 +75,28 @@ public class PathParametersSnippetTests { .row("a", "one").row("b", "two")); new PathParametersSnippet(Arrays.asList( parameterWithName("a").description("one"), parameterWithName("b") - .description("two"))) - .document(new OperationBuilder("path-parameters-with-query-string", - this.snippet.getOutputDirectory()).attribute( - "org.springframework.restdocs.urlTemplate", "/{a}/{b}?foo=bar") - .build()); + .description("two"))).document(operationBuilder( + "path-parameters-with-query-string").attribute( + "org.springframework.restdocs.urlTemplate", "/{a}/{b}?foo=bar").build()); + } + + @Test + public void pathParametersWithCustomAttributes() throws IOException { + TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); + given(resolver.resolveTemplateResource("path-parameters")).willReturn( + snippetResource("path-parameters-with-title")); + this.snippet.expectPathParameters("path-parameters-with-custom-attributes") + .withContents(containsString("The title")); + + new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one") + .attributes(key("foo").value("alpha")), parameterWithName("b") + .description("two").attributes(key("foo").value("bravo"))), + attributes(key("title").value("The title"))).document(operationBuilder( + "path-parameters-with-custom-attributes") + .attribute("org.springframework.restdocs.urlTemplate", "/{a}/{b}") + .attribute(TemplateEngine.class.getName(), + new MustacheTemplateEngine(resolver)).build()); + } @Test @@ -144,37 +112,10 @@ public class PathParametersSnippetTests { new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one") .attributes(key("foo").value("alpha")), parameterWithName("b") .description("two").attributes(key("foo").value("bravo")))) - .document(new OperationBuilder( - "path-parameters-with-custom-descriptor-attributes", this.snippet - .getOutputDirectory()) + .document(operationBuilder("path-parameters-with-custom-descriptor-attributes") .attribute("org.springframework.restdocs.urlTemplate", "/{a}/{b}") .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)).build()); } - @Test - public void pathParametersWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("path-parameters")).willReturn( - snippetResource("path-parameters-with-title")); - this.snippet.expectPathParameters("path-parameters-with-custom-attributes") - .withContents(startsWith(".The title")); - - new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one") - .attributes(key("foo").value("alpha")), parameterWithName("b") - .description("two").attributes(key("foo").value("bravo"))), - attributes(key("title").value("The title"))) - .document(new OperationBuilder("path-parameters-with-custom-attributes", - this.snippet.getOutputDirectory()) - .attribute("org.springframework.restdocs.urlTemplate", "/{a}/{b}") - .attribute(TemplateEngine.class.getName(), - new MustacheTemplateEngine(resolver)).build()); - - } - - private FileSystemResource snippetResource(String name) { - return new FileSystemResource("src/test/resources/custom-snippet-templates/" - + name + ".snippet"); - } - } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetFailureTests.java new file mode 100644 index 00000000..b795dd57 --- /dev/null +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetFailureTests.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.request; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.snippet.SnippetFormats; +import org.springframework.restdocs.test.ExpectedSnippet; +import org.springframework.restdocs.test.OperationBuilder; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; + +/** + * Tests for failures when rendering {@link RequestParametersSnippet} due to missing or + * undocumented request parameters. + * + * @author Andy Wilkinson + */ +public class RequestParametersSnippetFailureTests { + + @Rule + public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor()); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void undocumentedParameter() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(equalTo("Request parameters with the following names were" + + " not documented: [a]")); + new RequestParametersSnippet(Collections.emptyList()) + .document(new OperationBuilder("undocumented-parameter", this.snippet + .getOutputDirectory()).request("http://localhost") + .param("a", "alpha").build()); + } + + @Test + public void missingParameter() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(equalTo("Request parameters with the following names were" + + " not found in the request: [a]")); + new RequestParametersSnippet(Arrays.asList(parameterWithName("a").description( + "one"))).document(new OperationBuilder("missing-parameter", this.snippet + .getOutputDirectory()).request("http://localhost").build()); + } + + @Test + public void undocumentedAndMissingParameters() throws IOException { + this.thrown.expect(SnippetException.class); + this.thrown + .expectMessage(equalTo("Request parameters with the following names were" + + " not documented: [b]. Request parameters with the following" + + " names were not found in the request: [a]")); + new RequestParametersSnippet(Arrays.asList(parameterWithName("a").description( + "one"))).document(new OperationBuilder( + "undocumented-and-missing-parameters", this.snippet.getOutputDirectory()) + .request("http://localhost").param("b", "bravo").build()); + } + +} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetTests.java index da61deec..2facc40c 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetTests.java @@ -18,75 +18,30 @@ package org.springframework.restdocs.request; import java.io.IOException; import java.util.Arrays; -import java.util.Collections; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.core.io.FileSystemResource; -import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.AbstractSnippetTests; +import org.springframework.restdocs.snippet.SnippetFormat; import org.springframework.restdocs.templates.TemplateEngine; import org.springframework.restdocs.templates.TemplateResourceResolver; import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.restdocs.test.ExpectedSnippet; -import org.springframework.restdocs.test.OperationBuilder; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.CoreMatchers.containsString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader; /** * Tests for {@link RequestParametersSnippet}. * * @author Andy Wilkinson */ -public class RequestParametersSnippetTests { +public class RequestParametersSnippetTests extends AbstractSnippetTests { - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Rule - public ExpectedSnippet snippet = new ExpectedSnippet(); - - @Test - public void undocumentedParameter() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(equalTo("Request parameters with the following names were" - + " not documented: [a]")); - new RequestParametersSnippet(Collections.emptyList()) - .document(new OperationBuilder("undocumented-parameter", this.snippet - .getOutputDirectory()).request("http://localhost") - .param("a", "alpha").build()); - } - - @Test - public void missingParameter() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(equalTo("Request parameters with the following names were" - + " not found in the request: [a]")); - new RequestParametersSnippet(Arrays.asList(parameterWithName("a").description( - "one"))).document(new OperationBuilder("missing-parameter", this.snippet - .getOutputDirectory()).request("http://localhost").build()); - } - - @Test - public void undocumentedAndMissingParameters() throws IOException { - this.thrown.expect(SnippetException.class); - this.thrown - .expectMessage(equalTo("Request parameters with the following names were" - + " not documented: [b]. Request parameters with the following" - + " names were not found in the request: [a]")); - new RequestParametersSnippet(Arrays.asList(parameterWithName("a").description( - "one"))).document(new OperationBuilder( - "undocumented-and-missing-parameters", this.snippet.getOutputDirectory()) - .request("http://localhost").param("b", "bravo").build()); + public RequestParametersSnippetTests(String name, SnippetFormat snippetFormat) { + super(name, snippetFormat); } @Test @@ -96,8 +51,7 @@ public class RequestParametersSnippetTests { "two")); new RequestParametersSnippet(Arrays.asList( parameterWithName("a").description("one"), parameterWithName("b") - .description("two"))).document(new OperationBuilder( - "request-parameters", this.snippet.getOutputDirectory()) + .description("two"))).document(operationBuilder("request-parameters") .request("http://localhost").param("a", "bravo").param("b", "bravo") .build()); } @@ -107,10 +61,29 @@ public class RequestParametersSnippetTests { this.snippet.expectRequestParameters("ignored-request-parameter").withContents( tableWithHeader("Parameter", "Description").row("b", "two")); new RequestParametersSnippet(Arrays.asList(parameterWithName("a").ignored(), - parameterWithName("b").description("two"))) - .document(new OperationBuilder("ignored-request-parameter", this.snippet - .getOutputDirectory()).request("http://localhost") - .param("a", "bravo").param("b", "bravo").build()); + parameterWithName("b").description("two"))).document(operationBuilder( + "ignored-request-parameter").request("http://localhost") + .param("a", "bravo").param("b", "bravo").build()); + } + + @Test + public void requestParametersWithCustomAttributes() throws IOException { + TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); + given(resolver.resolveTemplateResource("request-parameters")).willReturn( + snippetResource("request-parameters-with-title")); + this.snippet.expectRequestParameters("request-parameters-with-custom-attributes") + .withContents(containsString("The title")); + + new RequestParametersSnippet(Arrays.asList( + parameterWithName("a").description("one").attributes( + key("foo").value("alpha")), + parameterWithName("b").description("two").attributes( + key("foo").value("bravo"))), attributes(key("title").value( + "The title"))).document(operationBuilder( + "request-parameters-with-custom-attributes") + .attribute(TemplateEngine.class.getName(), + new MustacheTemplateEngine(resolver)).request("http://localhost") + .param("a", "alpha").param("b", "bravo").build()); } @Test @@ -127,38 +100,11 @@ public class RequestParametersSnippetTests { parameterWithName("a").description("one").attributes( key("foo").value("alpha")), parameterWithName("b").description("two").attributes( - key("foo").value("bravo")))).document(new OperationBuilder( - "request-parameters-with-custom-descriptor-attributes", this.snippet - .getOutputDirectory()) + key("foo").value("bravo")))).document(operationBuilder( + "request-parameters-with-custom-descriptor-attributes") .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)).request("http://localhost") .param("a", "alpha").param("b", "bravo").build()); } - @Test - public void requestParametersWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-parameters")).willReturn( - snippetResource("request-parameters-with-title")); - this.snippet.expectRequestParameters("request-parameters-with-custom-attributes") - .withContents(startsWith(".The title")); - - new RequestParametersSnippet(Arrays.asList( - parameterWithName("a").description("one").attributes( - key("foo").value("alpha")), - parameterWithName("b").description("two").attributes( - key("foo").value("bravo"))), attributes(key("title").value( - "The title"))).document(new OperationBuilder( - "request-parameters-with-custom-attributes", this.snippet - .getOutputDirectory()) - .attribute(TemplateEngine.class.getName(), - new MustacheTemplateEngine(resolver)).request("http://localhost") - .param("a", "alpha").param("b", "bravo").build()); - } - - private FileSystemResource snippetResource(String name) { - return new FileSystemResource("src/test/resources/custom-snippet-templates/" - + name + ".snippet"); - } - } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java index 55ae2218..31bdb75d 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; +import static org.springframework.restdocs.snippet.SnippetFormats.asciidoctor; /** * Tests for {@link StandardWriterResolver}. @@ -37,7 +38,7 @@ public class StandardWriterResolverTests { private final PlaceholderResolver placeholderResolver = mock(PlaceholderResolver.class); private final StandardWriterResolver resolver = new StandardWriterResolver( - this.placeholderResolver); + this.placeholderResolver, "UTF-8", asciidoctor()); @Test public void noConfiguredOutputDirectoryAndRelativeInput() { diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java index ef807ee2..00a3c8d4 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import org.springframework.core.io.Resource; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; +import static org.springframework.restdocs.snippet.SnippetFormats.asciidoctor; /** * Tests for {@link TemplateResourceResolver}. @@ -40,7 +41,8 @@ public class StandardTemplateResourceResolverTests { @Rule public final ExpectedException thrown = ExpectedException.none(); - private final TemplateResourceResolver resolver = new StandardTemplateResourceResolver(); + private final TemplateResourceResolver resolver = new StandardTemplateResourceResolver( + asciidoctor()); private final TestClassLoader classLoader = new TestClassLoader(); @@ -66,7 +68,7 @@ public class StandardTemplateResourceResolverTests { @Test public void fallsBackToDefaultSnippet() throws Exception { this.classLoader.addResource( - "org/springframework/restdocs/templates/default-test.snippet", getClass() + "org/springframework/restdocs/templates/adoc/test.snippet", getClass() .getResource("test.snippet")); Resource snippet = doWithThreadContextClassLoader(this.classLoader, new Callable() { diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/ExpectedSnippet.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/ExpectedSnippet.java index 63364f6a..6a571f35 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/ExpectedSnippet.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/ExpectedSnippet.java @@ -23,6 +23,7 @@ import org.hamcrest.Matcher; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; +import org.springframework.restdocs.snippet.SnippetFormat; import org.springframework.restdocs.snippet.TemplatedSnippet; import org.springframework.restdocs.test.SnippetMatchers.SnippetMatcher; @@ -38,14 +39,21 @@ import static org.junit.Assert.assertThat; */ public class ExpectedSnippet implements TestRule { + private final SnippetFormat snippetFormat; + + private final SnippetMatcher snippet; + private String expectedName; private String expectedType; - private SnippetMatcher snippet = SnippetMatchers.snippet(); - private File outputDirectory; + public ExpectedSnippet(SnippetFormat snippetFormat) { + this.snippetFormat = snippetFormat; + this.snippet = SnippetMatchers.snippet(snippetFormat); + } + @Override public Statement apply(final Statement base, Description description) { this.outputDirectory = new File("build/" @@ -56,7 +64,8 @@ public class ExpectedSnippet implements TestRule { private void verifySnippet() throws IOException { if (this.outputDirectory != null && this.expectedName != null) { File snippetDir = new File(this.outputDirectory, this.expectedName); - File snippetFile = new File(snippetDir, this.expectedType + ".adoc"); + File snippetFile = new File(snippetDir, this.expectedType + "." + + this.snippetFormat.getFileExtension()); assertThat(snippetFile, is(this.snippet)); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/OperationBuilder.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/OperationBuilder.java index e5be8166..e2a54b99 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/OperationBuilder.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/OperationBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,8 @@ import org.springframework.restdocs.operation.OperationResponseFactory; import org.springframework.restdocs.operation.Parameters; import org.springframework.restdocs.operation.StandardOperation; import org.springframework.restdocs.snippet.RestDocumentationContextPlaceholderResolver; +import org.springframework.restdocs.snippet.SnippetFormat; +import org.springframework.restdocs.snippet.SnippetFormats; import org.springframework.restdocs.snippet.StandardWriterResolver; import org.springframework.restdocs.snippet.WriterResolver; import org.springframework.restdocs.templates.StandardTemplateResourceResolver; @@ -58,11 +60,18 @@ public class OperationBuilder { private final File outputDirectory; + private final SnippetFormat snippetFormat; + private OperationRequestBuilder requestBuilder; public OperationBuilder(String name, File outputDirectory) { + this(name, outputDirectory, SnippetFormats.asciidoctor()); + } + + public OperationBuilder(String name, File outputDirectory, SnippetFormat snippetFormat) { this.name = name; this.outputDirectory = outputDirectory; + this.snippetFormat = snippetFormat; } public OperationRequestBuilder request(String uri) { @@ -82,13 +91,15 @@ public class OperationBuilder { public Operation build() { if (this.attributes.get(TemplateEngine.class.getName()) == null) { this.attributes.put(TemplateEngine.class.getName(), - new MustacheTemplateEngine(new StandardTemplateResourceResolver())); + new MustacheTemplateEngine(new StandardTemplateResourceResolver( + this.snippetFormat))); } RestDocumentationContext context = new RestDocumentationContext(null, null, this.outputDirectory); this.attributes.put(RestDocumentationContext.class.getName(), context); this.attributes.put(WriterResolver.class.getName(), new StandardWriterResolver( - new RestDocumentationContextPlaceholderResolver(context))); + new RestDocumentationContextPlaceholderResolver(context), "UTF-8", + this.snippetFormat)); return new StandardOperation(this.name, (this.requestBuilder == null ? new OperationRequestBuilder( "http://localhost/").buildRequest() : this.requestBuilder diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/SnippetMatchers.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/SnippetMatchers.java index 97818a52..c076d0ca 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/SnippetMatchers.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/SnippetMatchers.java @@ -30,6 +30,8 @@ import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.springframework.http.HttpStatus; +import org.springframework.restdocs.snippet.SnippetFormat; +import org.springframework.restdocs.snippet.SnippetFormats; import org.springframework.util.FileCopyUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMethod; @@ -45,37 +47,62 @@ public final class SnippetMatchers { } - public static SnippetMatcher snippet() { - return new SnippetMatcher(); + public static SnippetMatcher snippet(SnippetFormat snippetFormat) { + return new SnippetMatcher(snippetFormat); } - public static AsciidoctorTableMatcher tableWithTitleAndHeader(String title, - String... headers) { - return new AsciidoctorTableMatcher(title, headers); + public static TableMatcher tableWithHeader(SnippetFormat format, String... headers) { + if ("adoc".equals(format.getFileExtension())) { + return new AsciidoctorTableMatcher(null, headers); + } + return new MarkdownTableMatcher(null, headers); } - public static AsciidoctorTableMatcher tableWithHeader(String... headers) { - return new AsciidoctorTableMatcher(null, headers); + public static TableMatcher tableWithTitleAndHeader(SnippetFormat format, + String title, String... headers) { + if ("adoc".equals(format.getFileExtension())) { + return new AsciidoctorTableMatcher(title, headers); + } + return new MarkdownTableMatcher(title, headers); } - public static HttpRequestMatcher httpRequest(RequestMethod method, String uri) { - return new HttpRequestMatcher(method, uri); + public static HttpRequestMatcher httpRequest(SnippetFormat format, + RequestMethod requestMethod, String uri) { + if ("adoc".equals(format.getFileExtension())) { + return new HttpRequestMatcher(requestMethod, uri, + new AsciidoctorCodeBlockMatcher<>("http"), 3); + } + return new HttpRequestMatcher(requestMethod, uri, new MarkdownCodeBlockMatcher<>( + "http"), 2); } - public static HttpResponseMatcher httpResponse(HttpStatus status) { - return new HttpResponseMatcher(status); + public static HttpResponseMatcher httpResponse(SnippetFormat format, HttpStatus status) { + if ("adoc".equals(format.getFileExtension())) { + return new HttpResponseMatcher(status, new AsciidoctorCodeBlockMatcher<>( + "http"), 3); + } + return new HttpResponseMatcher(status, new MarkdownCodeBlockMatcher<>("http"), 2); } @SuppressWarnings({ "rawtypes" }) - public static AsciidoctorCodeBlockMatcher codeBlock(String language) { - return new AsciidoctorCodeBlockMatcher(language); + public static CodeBlockMatcher codeBlock(SnippetFormat format, String language) { + if ("adoc".equals(format.getFileExtension())) { + return new AsciidoctorCodeBlockMatcher(language); + } + return new MarkdownCodeBlockMatcher(language); } private static abstract class AbstractSnippetContentMatcher extends BaseMatcher { + private final SnippetFormat snippetFormat; + private List lines = new ArrayList<>(); + protected AbstractSnippetContentMatcher(SnippetFormat snippetFormat) { + this.snippetFormat = snippetFormat; + } + protected void addLine(String line) { this.lines.add(line); } @@ -94,7 +121,7 @@ public final class SnippetMatchers { @Override public void describeTo(Description description) { - description.appendText("Asciidoctor snippet"); + description.appendText(this.snippetFormat.getFileExtension() + " snippet"); description.appendText(getLinesAsString()); } @@ -123,17 +150,15 @@ public final class SnippetMatchers { } /** - * A {@link Matcher} for an Asciidoctor code block. + * Base class for code block matchers. * * @param The type of the matcher */ - public static class AsciidoctorCodeBlockMatcher> - extends AbstractSnippetContentMatcher { + public static class CodeBlockMatcher> extends + AbstractSnippetContentMatcher { - protected AsciidoctorCodeBlockMatcher(String language) { - this.addLine("[source," + language + "]"); - this.addLine("----"); - this.addLine("----"); + protected CodeBlockMatcher(SnippetFormat snippetFormat) { + super(snippetFormat); } @SuppressWarnings("unchecked") @@ -144,32 +169,84 @@ public final class SnippetMatchers { } + /** + * A {@link Matcher} for an Asciidoctor code block. + * + * @param The type of the matcher + */ + public static class AsciidoctorCodeBlockMatcher> + extends CodeBlockMatcher { + + protected AsciidoctorCodeBlockMatcher(String language) { + super(SnippetFormats.asciidoctor()); + this.addLine("[source," + language + "]"); + this.addLine("----"); + this.addLine("----"); + } + + } + + /** + * A {@link Matcher} for a Markdown code block. + * + * @param The type of the matcher + */ + public static class MarkdownCodeBlockMatcher> + extends CodeBlockMatcher { + + protected MarkdownCodeBlockMatcher(String language) { + super(SnippetFormats.markdown()); + this.addLine("```" + language); + this.addLine("```"); + } + + } + /** * A {@link Matcher} for an HTTP request or response. * * @param The type of the matcher */ public static abstract class HttpMatcher> extends - AsciidoctorCodeBlockMatcher> { + BaseMatcher { - private int headerOffset = 3; + private final CodeBlockMatcher delegate; - protected HttpMatcher() { - super("http"); + private int headerOffset; + + protected HttpMatcher(CodeBlockMatcher delegate, int headerOffset) { + this.delegate = delegate; + this.headerOffset = headerOffset; } @SuppressWarnings("unchecked") public T header(String name, String value) { - this.addLine(this.headerOffset++, name + ": " + value); + this.delegate.addLine(this.headerOffset++, name + ": " + value); return (T) this; } @SuppressWarnings("unchecked") public T header(String name, long value) { - this.addLine(this.headerOffset++, name + ": " + value); + this.delegate.addLine(this.headerOffset++, name + ": " + value); return (T) this; } + @SuppressWarnings("unchecked") + public T content(String content) { + this.delegate.addLine(-1, content); + return (T) this; + } + + @Override + public boolean matches(Object item) { + return this.delegate.matches(item); + } + + @Override + public void describeTo(Description description) { + this.delegate.describeTo(description); + } + } /** @@ -178,7 +255,9 @@ public final class SnippetMatchers { public static final class HttpResponseMatcher extends HttpMatcher { - private HttpResponseMatcher(HttpStatus status) { + private HttpResponseMatcher(HttpStatus status, CodeBlockMatcher delegate, + int headerOffset) { + super(delegate, headerOffset); this.content("HTTP/1.1 " + status.value() + " " + status.getReasonPhrase()); this.content(""); } @@ -190,20 +269,41 @@ public final class SnippetMatchers { */ public static final class HttpRequestMatcher extends HttpMatcher { - private HttpRequestMatcher(RequestMethod requestMethod, String uri) { + private HttpRequestMatcher(RequestMethod requestMethod, String uri, + CodeBlockMatcher delegate, int headerOffset) { + super(delegate, headerOffset); this.content(requestMethod.name() + " " + uri + " HTTP/1.1"); this.content(""); } } + /** + * Base class for table matchers. + * + * @param The concrete type of the matcher + */ + public static abstract class TableMatcher> extends + AbstractSnippetContentMatcher { + + protected TableMatcher(SnippetFormat snippetFormat) { + super(snippetFormat); + } + + public abstract T row(String... entries); + + public abstract T configuration(String configuration); + + } + /** * A {@link Matcher} for an Asciidoctor table. */ public static final class AsciidoctorTableMatcher extends - AbstractSnippetContentMatcher { + TableMatcher { private AsciidoctorTableMatcher(String title, String... columns) { + super(SnippetFormats.asciidoctor()); if (StringUtils.hasText(title)) { this.addLine("." + title); } @@ -216,6 +316,7 @@ public final class SnippetMatchers { this.addLine("|==="); } + @Override public AsciidoctorTableMatcher row(String... entries) { for (String entry : entries) { this.addLine(-1, "|" + entry); @@ -224,19 +325,68 @@ public final class SnippetMatchers { return this; } + @Override public AsciidoctorTableMatcher configuration(String configuration) { this.addLine(0, configuration); return this; } + + } + + /** + * A {@link Matcher} for a Markdown table. + */ + public static final class MarkdownTableMatcher extends + TableMatcher { + + private MarkdownTableMatcher(String title, String... columns) { + super(SnippetFormats.asciidoctor()); + if (StringUtils.hasText(title)) { + this.addLine(title); + } + String header = StringUtils.collectionToDelimitedString( + Arrays.asList(columns), " | "); + this.addLine(header); + List components = new ArrayList<>(); + for (String column : columns) { + StringBuilder dashes = new StringBuilder(); + for (int i = 0; i < column.length(); i++) { + dashes.append("-"); + } + components.add(dashes.toString()); + } + this.addLine(StringUtils.collectionToDelimitedString(components, " | ")); + this.addLine(""); + } + + @Override + public MarkdownTableMatcher row(String... entries) { + this.addLine(-1, StringUtils.collectionToDelimitedString( + Arrays.asList(entries), " | ")); + return this; + } + + @Override + public MarkdownTableMatcher configuration(String configuration) { + throw new UnsupportedOperationException( + "Markdown does not support table configuration"); + } + } /** * A {@link Matcher} for a snippet file. */ - public static class SnippetMatcher extends BaseMatcher { + public static final class SnippetMatcher extends BaseMatcher { + + private final SnippetFormat snippetFormat; private Matcher expectedContents; + private SnippetMatcher(SnippetFormat snippetFormat) { + this.snippetFormat = snippetFormat; + } + @Override public boolean matches(Object item) { if (snippetFileExists(item)) { @@ -285,7 +435,8 @@ public final class SnippetMatchers { this.expectedContents.describeTo(description); } else { - description.appendText("Asciidoctor snippet"); + description + .appendText(this.snippetFormat.getFileExtension() + " snippet"); } } @@ -295,4 +446,5 @@ public final class SnippetMatchers { } } + } diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/curl-request-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/curl-request-with-title.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/curl-request-with-title.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/curl-request-with-title.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/http-request-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/http-request-with-title.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/http-request-with-title.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/http-request-with-title.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/http-response-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/http-response-with-title.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/http-response-with-title.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/http-response-with-title.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/links-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/links-with-extra-column.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/links-with-extra-column.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/links-with-extra-column.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/links-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/links-with-title.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/links-with-title.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/links-with-title.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/path-parameters-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/path-parameters-with-extra-column.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/path-parameters-with-extra-column.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/path-parameters-with-extra-column.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/path-parameters-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/path-parameters-with-title.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/path-parameters-with-title.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/path-parameters-with-title.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/request-fields-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-fields-with-extra-column.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/request-fields-with-extra-column.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-fields-with-extra-column.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/request-fields-with-list-description.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-fields-with-list-description.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/request-fields-with-list-description.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-fields-with-list-description.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/request-fields-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-fields-with-title.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/request-fields-with-title.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-fields-with-title.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/request-headers-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-headers-with-extra-column.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/request-headers-with-extra-column.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-headers-with-extra-column.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/request-headers-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-headers-with-title.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/request-headers-with-title.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-headers-with-title.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/request-parameters-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-parameters-with-extra-column.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/request-parameters-with-extra-column.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-parameters-with-extra-column.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/request-parameters-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-parameters-with-title.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/request-parameters-with-title.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/request-parameters-with-title.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/response-fields-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/response-fields-with-extra-column.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/response-fields-with-extra-column.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/response-fields-with-extra-column.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/response-fields-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/response-fields-with-title.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/response-fields-with-title.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/response-fields-with-title.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/response-headers-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/response-headers-with-extra-column.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/response-headers-with-extra-column.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/response-headers-with-extra-column.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/response-headers-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/response-headers-with-title.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/custom-snippet-templates/response-headers-with-title.snippet rename to spring-restdocs-core/src/test/resources/custom-snippet-templates/adoc/response-headers-with-title.snippet diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/curl-request-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/curl-request-with-title.snippet new file mode 100644 index 00000000..483ab6c7 --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/curl-request-with-title.snippet @@ -0,0 +1,4 @@ +{{title}} +```bash +$ curl {{url}} {{options}} +``` \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/http-request-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/http-request-with-title.snippet new file mode 100644 index 00000000..363a9369 --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/http-request-with-title.snippet @@ -0,0 +1,8 @@ +{{title}} +```http +{{method}} {{path}} HTTP/1.1 +{{#headers}} +{{name}}: {{value}} +{{/headers}} +{{requestBody}} +``` \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/http-response-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/http-response-with-title.snippet new file mode 100644 index 00000000..103fa6dc --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/http-response-with-title.snippet @@ -0,0 +1,8 @@ +{{title}} +```http +HTTP/1.1 {{statusCode}} {{statusReason}} +{{#headers}} +{{name}}: {{value}} +{{/headers}} +{{responseBody}} +``` \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/links-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/links-with-extra-column.snippet new file mode 100644 index 00000000..a7e3349d --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/links-with-extra-column.snippet @@ -0,0 +1,5 @@ +Relation | Description | Foo +-------- | ----------- | --- +{{#links}} +{{rel}} | {{description}} | {{foo}} +{{/links}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/links-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/links-with-title.snippet new file mode 100644 index 00000000..ecaaeb33 --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/links-with-title.snippet @@ -0,0 +1,6 @@ +{{title}} +Relation | Description +-------- | ----------- +{{#links}} +{{rel}} | {{description}} +{{/links}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/path-parameters-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/path-parameters-with-extra-column.snippet new file mode 100644 index 00000000..260502b6 --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/path-parameters-with-extra-column.snippet @@ -0,0 +1,5 @@ +Parameter | Description | Foo +--------- | ----------- | --- +{{#parameters}} +{{name}} | {{description}} | {{foo}} +{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/path-parameters-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/path-parameters-with-title.snippet new file mode 100644 index 00000000..b63b6235 --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/path-parameters-with-title.snippet @@ -0,0 +1,6 @@ +{{title}} +Parameter | Description +--------- | ----------- +{{#parameters}} +{{name}} | {{description}} +{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-fields-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-fields-with-extra-column.snippet new file mode 100644 index 00000000..e4ec4c60 --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-fields-with-extra-column.snippet @@ -0,0 +1,5 @@ +Path | Type | Description | Foo +---- | ---- | ----------- | --- +{{#fields}} +{{path}} | {{type}} | {{description}} | {{foo}} +{{/fields}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-fields-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-fields-with-title.snippet new file mode 100644 index 00000000..24bb63fa --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-fields-with-title.snippet @@ -0,0 +1,6 @@ +{{title}} +Path | Type | Description +---- | ---- | ----------- +{{#fields}} +{{path}} | {{type}} | {{description}} +{{/fields}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-headers-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-headers-with-extra-column.snippet new file mode 100644 index 00000000..af392f2a --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-headers-with-extra-column.snippet @@ -0,0 +1,5 @@ +Name | Description | Foo +---- | ----------- | --- +{{#headers}} +{{name}} | {{description}} | {{foo}} +{{/headers}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-headers-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-headers-with-title.snippet new file mode 100644 index 00000000..d57ed7ba --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-headers-with-title.snippet @@ -0,0 +1,6 @@ +{{title}} +Name | Description +---- | ----------- +{{#headers}} +{{name}} | {{description}} +{{/headers}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-parameters-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-parameters-with-extra-column.snippet new file mode 100644 index 00000000..260502b6 --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-parameters-with-extra-column.snippet @@ -0,0 +1,5 @@ +Parameter | Description | Foo +--------- | ----------- | --- +{{#parameters}} +{{name}} | {{description}} | {{foo}} +{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-parameters-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-parameters-with-title.snippet new file mode 100644 index 00000000..b63b6235 --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/request-parameters-with-title.snippet @@ -0,0 +1,6 @@ +{{title}} +Parameter | Description +--------- | ----------- +{{#parameters}} +{{name}} | {{description}} +{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/response-fields-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/response-fields-with-extra-column.snippet new file mode 100644 index 00000000..e4ec4c60 --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/response-fields-with-extra-column.snippet @@ -0,0 +1,5 @@ +Path | Type | Description | Foo +---- | ---- | ----------- | --- +{{#fields}} +{{path}} | {{type}} | {{description}} | {{foo}} +{{/fields}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/response-fields-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/response-fields-with-title.snippet new file mode 100644 index 00000000..24bb63fa --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/response-fields-with-title.snippet @@ -0,0 +1,6 @@ +{{title}} +Path | Type | Description +---- | ---- | ----------- +{{#fields}} +{{path}} | {{type}} | {{description}} +{{/fields}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/response-headers-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/response-headers-with-extra-column.snippet new file mode 100644 index 00000000..af392f2a --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/response-headers-with-extra-column.snippet @@ -0,0 +1,5 @@ +Name | Description | Foo +---- | ----------- | --- +{{#headers}} +{{name}} | {{description}} | {{foo}} +{{/headers}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/response-headers-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/response-headers-with-title.snippet new file mode 100644 index 00000000..d57ed7ba --- /dev/null +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/md/response-headers-with-title.snippet @@ -0,0 +1,6 @@ +{{title}} +Name | Description +---- | ----------- +{{#headers}} +{{name}} | {{description}} +{{/headers}} \ No newline at end of file diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java index f9beacac..bb7d7071 100644 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java +++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java @@ -67,8 +67,8 @@ public class MockMvcRestDocumentationConfigurer public RequestPostProcessor beforeMockMvcCreated( ConfigurableMockMvcBuilder builder, WebApplicationContext context) { return new ConfigurerApplyingRequestPostProcessor(this.restDocumentation, - Arrays.asList(getTemplateEngineConfigurer(), - getWriterResolverConfigurer(), snippets(), this.uriConfigurer)); + Arrays.asList(snippets(), getTemplateEngineConfigurer(), + getWriterResolverConfigurer(), this.uriConfigurer)); } @Override diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java index d444dd17..0c48c480 100644 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java +++ b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java @@ -75,6 +75,8 @@ import static org.springframework.restdocs.request.RequestDocumentation.pathPara import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.restdocs.snippet.SnippetFormats.asciidoctor; +import static org.springframework.restdocs.snippet.SnippetFormats.markdown; import static org.springframework.restdocs.test.SnippetMatchers.codeBlock; import static org.springframework.restdocs.test.SnippetMatchers.httpRequest; import static org.springframework.restdocs.test.SnippetMatchers.httpResponse; @@ -123,6 +125,20 @@ public class MockMvcRestDocumentationIntegrationTests { "http-request.adoc", "http-response.adoc", "curl-request.adoc"); } + @Test + public void markdownSnippetGeneration() throws Exception { + MockMvc mockMvc = MockMvcBuilders + .webAppContextSetup(this.context) + .apply(new MockMvcRestDocumentationConfigurer(this.restDocumentation) + .snippets().withEncoding("UTF-8").withFormat(markdown())).build(); + + mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andDo(document("basic-markdown")); + assertExpectedSnippetFilesExist(new File( + "build/generated-snippets/basic-markdown"), "http-request.md", + "http-response.md", "curl-request.md"); + } + @Test public void curlSnippetWithContent() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) @@ -130,10 +146,11 @@ public class MockMvcRestDocumentationIntegrationTests { mockMvc.perform(post("/").accept(MediaType.APPLICATION_JSON).content("content")) .andExpect(status().isOk()).andDo(document("curl-snippet-with-content")); - assertThat(new File( - "build/generated-snippets/curl-snippet-with-content/curl-request.adoc"), - is(snippet().withContents( - codeBlock("bash").content( + assertThat( + new File( + "build/generated-snippets/curl-snippet-with-content/curl-request.adoc"), + is(snippet(asciidoctor()).withContents( + codeBlock(asciidoctor(), "bash").content( "$ curl " + "'http://localhost:8080/' -i -X POST " + "-H 'Accept: application/json' -d 'content'")))); } @@ -150,8 +167,8 @@ public class MockMvcRestDocumentationIntegrationTests { assertThat( new File( "build/generated-snippets/curl-snippet-with-query-string/curl-request.adoc"), - is(snippet().withContents( - codeBlock("bash").content( + is(snippet(asciidoctor()).withContents( + codeBlock(asciidoctor(), "bash").content( "$ curl " + "'http://localhost:8080/?foo=bar' -i -X POST " + "-H 'Accept: application/json' -d 'a=alpha'")))); @@ -298,9 +315,9 @@ public class MockMvcRestDocumentationIntegrationTests { assertThat( new File("build/generated-snippets/original-request/http-request.adoc"), - is(snippet().withContents( - httpRequest(RequestMethod.GET, "/").header("a", "alpha") - .header("b", "bravo") + is(snippet(asciidoctor()).withContents( + httpRequest(asciidoctor(), RequestMethod.GET, "/") + .header("a", "alpha").header("b", "bravo") .header("Content-Type", "application/json") .header("Accept", MediaType.APPLICATION_JSON_VALUE) .header("Host", "localhost") @@ -310,8 +327,9 @@ public class MockMvcRestDocumentationIntegrationTests { assertThat( new File( "build/generated-snippets/preprocessed-request/http-request.adoc"), - is(snippet().withContents( - httpRequest(RequestMethod.GET, "/").header("b", "bravo") + is(snippet(asciidoctor()).withContents( + httpRequest(asciidoctor(), RequestMethod.GET, "/") + .header("b", "bravo") .header("Content-Type", "application/json") .header("Accept", MediaType.APPLICATION_JSON_VALUE) .content(prettyPrinted)))); @@ -337,8 +355,8 @@ public class MockMvcRestDocumentationIntegrationTests { + "\"href\":\"href\"}]}"; assertThat( new File("build/generated-snippets/original-response/http-response.adoc"), - is(snippet().withContents( - httpResponse(HttpStatus.OK) + is(snippet(asciidoctor()).withContents( + httpResponse(asciidoctor(), HttpStatus.OK) .header("a", "alpha") .header("Content-Type", "application/json") .header(HttpHeaders.CONTENT_LENGTH, @@ -348,8 +366,8 @@ public class MockMvcRestDocumentationIntegrationTests { assertThat( new File( "build/generated-snippets/preprocessed-response/http-response.adoc"), - is(snippet().withContents( - httpResponse(HttpStatus.OK) + is(snippet(asciidoctor()).withContents( + httpResponse(asciidoctor(), HttpStatus.OK) .header("Content-Type", "application/json") .header(HttpHeaders.CONTENT_LENGTH, prettyPrinted.getBytes().length) @@ -376,7 +394,7 @@ public class MockMvcRestDocumentationIntegrationTests { } assertThat(new File( "build/generated-snippets/custom-snippet-template/curl-request.adoc"), - is(snippet().withContents(equalTo("Custom curl request")))); + is(snippet(asciidoctor()).withContents(equalTo("Custom curl request")))); mockMvc.perform(get("/")).andDo( document( @@ -395,9 +413,9 @@ public class MockMvcRestDocumentationIntegrationTests { .andExpect(status().isOk()).andDo(document("custom-context-path")); assertThat( new File("build/generated-snippets/custom-context-path/curl-request.adoc"), - is(snippet() + is(snippet(asciidoctor()) .withContents( - codeBlock("bash") + codeBlock(asciidoctor(), "bash") .content( "$ curl 'http://localhost:8080/custom/' -i -H 'Accept: application/json'")))); } diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurer.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurer.java index 5dbdb1c0..256d6527 100644 --- a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurer.java +++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurer.java @@ -51,8 +51,8 @@ public final class RestAssuredRestDocumentationConfigurer RestAssuredRestDocumentationConfigurer(RestDocumentation restDocumentation) { this.restDocumentation = restDocumentation; - this.configurers = Arrays.asList(getTemplateEngineConfigurer(), - getWriterResolverConfigurer(), snippets()); + this.configurers = Arrays.asList(snippets(), getTemplateEngineConfigurer(), + getWriterResolverConfigurer()); } @Override diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationIntegrationTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationIntegrationTests.java index 3ae6d13f..eeea60b4 100644 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationIntegrationTests.java +++ b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationIntegrationTests.java @@ -70,6 +70,7 @@ import static org.springframework.restdocs.request.RequestDocumentation.requestP import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; import static org.springframework.restdocs.restassured.operation.preprocess.RestAssuredPreprocessors.modifyUris; +import static org.springframework.restdocs.snippet.SnippetFormats.asciidoctor; import static org.springframework.restdocs.test.SnippetMatchers.codeBlock; import static org.springframework.restdocs.test.SnippetMatchers.httpRequest; import static org.springframework.restdocs.test.SnippetMatchers.httpResponse; @@ -114,8 +115,8 @@ public class RestAssuredRestDocumentationIntegrationTests { assertThat( new File( "build/generated-snippets/curl-snippet-with-content/curl-request.adoc"), - is(snippet().withContents( - codeBlock("bash").content( + is(snippet(asciidoctor()).withContents( + codeBlock(asciidoctor(), "bash").content( "$ curl 'http://localhost:" + this.port + "/' -i " + "-X POST -H 'Accept: application/json' " + "-H 'Content-Type: " + contentType + "' " @@ -133,8 +134,8 @@ public class RestAssuredRestDocumentationIntegrationTests { assertThat( new File( "build/generated-snippets/curl-snippet-with-query-string/curl-request.adoc"), - is(snippet().withContents( - codeBlock("bash").content( + is(snippet(asciidoctor()).withContents( + codeBlock(asciidoctor(), "bash").content( "$ curl " + "'http://localhost:" + this.port + "/?foo=bar' -i -X POST " + "-H 'Accept: application/json' " @@ -261,9 +262,9 @@ public class RestAssuredRestDocumentationIntegrationTests { .then().statusCode(200); assertThat( new File("build/generated-snippets/original-request/http-request.adoc"), - is(snippet() + is(snippet(asciidoctor()) .withContents( - httpRequest(RequestMethod.GET, "/") + httpRequest(asciidoctor(), RequestMethod.GET, "/") .header("a", "alpha") .header("b", "bravo") .header("Accept", @@ -277,9 +278,9 @@ public class RestAssuredRestDocumentationIntegrationTests { assertThat( new File( "build/generated-snippets/preprocessed-request/http-request.adoc"), - is(snippet() + is(snippet(asciidoctor()) .withContents( - httpRequest(RequestMethod.GET, "/") + httpRequest(asciidoctor(), RequestMethod.GET, "/") .header("b", "bravo") .header("Accept", MediaType.APPLICATION_JSON_VALUE) @@ -308,8 +309,8 @@ public class RestAssuredRestDocumentationIntegrationTests { assertThat( new File( "build/generated-snippets/preprocessed-response/http-response.adoc"), - is(snippet().withContents( - httpResponse(HttpStatus.OK) + is(snippet(asciidoctor()).withContents( + httpResponse(asciidoctor(), HttpStatus.OK) .header("Foo", "https://api.example.com/foo/bar") .header("Content-Type", "application/json;charset=UTF-8") .header(HttpHeaders.CONTENT_LENGTH, @@ -335,7 +336,7 @@ public class RestAssuredRestDocumentationIntegrationTests { } assertThat(new File( "build/generated-snippets/custom-snippet-template/curl-request.adoc"), - is(snippet().withContents(equalTo("Custom curl request")))); + is(snippet(asciidoctor()).withContents(equalTo("Custom curl request")))); } private void assertExpectedSnippetFilesExist(File directory, String... snippets) {