/*
* Copyright 2012 Google Inc.
*
* 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.google.template.soy.data;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
import com.google.common.html.types.SafeHtml;
import com.google.common.html.types.SafeHtmlBuilder;
import com.google.common.html.types.SafeHtmlProto;
import com.google.common.html.types.SafeHtmls;
import com.google.common.html.types.SafeScript;
import com.google.common.html.types.SafeScriptProto;
import com.google.common.html.types.SafeScripts;
import com.google.common.html.types.SafeStyleSheet;
import com.google.common.html.types.SafeStyleSheetProto;
import com.google.common.html.types.SafeStyleSheets;
import com.google.common.html.types.SafeUrl;
import com.google.common.html.types.SafeUrlProto;
import com.google.common.html.types.SafeUrls;
import com.google.template.soy.data.SanitizedContent.ContentKind;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Unit tests for SanitizedContents utility class.
*
*/
@RunWith(JUnit4.class)
public class SanitizedContentsTest {
@Test
public void testUnsanitizedText() {
assertThat(SanitizedContents.unsanitizedText("Hello World"))
.isEqualTo(SanitizedContent.create("Hello World", ContentKind.TEXT, null));
}
@Test
public void testConcatCombinesHtml() throws Exception {
String text1 = "one";
String text2 = "two";
String text3 = "three";
SanitizedContent content1 = SanitizedContent.create(text1, ContentKind.HTML, null);
SanitizedContent content2 = SanitizedContent.create(text2, ContentKind.HTML, null);
SanitizedContent content3 = SanitizedContent.create(text3, ContentKind.HTML, null);
assertThat(SanitizedContents.concatHtml(content1, content2, content3))
.isEqualTo(SanitizedContent.create(text1 + text2 + text3, ContentKind.HTML, null));
}
@Test
public void testConcatReturnsEmpty() throws Exception {
assertThat(SanitizedContents.concatHtml())
.isEqualTo(SanitizedContent.create("", ContentKind.HTML, Dir.NEUTRAL));
}
@Test
public void testConcatThrowsExceptionOnDifferentNonHtml() throws Exception {
try {
SanitizedContents.concatHtml(
SanitizedContents.emptyString(ContentKind.HTML),
SanitizedContents.emptyString(ContentKind.CSS));
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage()).isEqualTo("Can only concat HTML");
}
}
@Test
public void testConcatCombinesHtmlDir() throws Exception {
SanitizedContent EMPTY_HTML = SanitizedContents.emptyString(ContentKind.HTML);
SanitizedContent LTR_HTML = SanitizedContent.create(".", ContentKind.HTML, Dir.LTR);
SanitizedContent RTL_HTML = SanitizedContent.create(".", ContentKind.HTML, Dir.RTL);
SanitizedContent NEUTRAL_HTML = SanitizedContent.create(".", ContentKind.HTML, Dir.NEUTRAL);
SanitizedContent UNKNOWN_DIR_HTML = SanitizedContent.create(".", ContentKind.HTML, null);
// empty -> neutral
assertThat(SanitizedContents.concatHtml(EMPTY_HTML).getContentDirection())
.isEqualTo(Dir.NEUTRAL);
// x -> x
assertThat(SanitizedContents.concatHtml(LTR_HTML).getContentDirection()).isEqualTo(Dir.LTR);
assertThat(SanitizedContents.concatHtml(RTL_HTML).getContentDirection()).isEqualTo(Dir.RTL);
assertThat(SanitizedContents.concatHtml(NEUTRAL_HTML).getContentDirection())
.isEqualTo(Dir.NEUTRAL);
assertThat(SanitizedContents.concatHtml(UNKNOWN_DIR_HTML).getContentDirection()).isNull();
// x + unknown -> unknown
assertThat(SanitizedContents.concatHtml(LTR_HTML, UNKNOWN_DIR_HTML).getContentDirection())
.isNull();
assertThat(SanitizedContents.concatHtml(UNKNOWN_DIR_HTML, LTR_HTML).getContentDirection())
.isNull();
assertThat(SanitizedContents.concatHtml(RTL_HTML, UNKNOWN_DIR_HTML).getContentDirection())
.isNull();
assertThat(SanitizedContents.concatHtml(UNKNOWN_DIR_HTML, RTL_HTML).getContentDirection())
.isNull();
assertThat(SanitizedContents.concatHtml(NEUTRAL_HTML, UNKNOWN_DIR_HTML).getContentDirection())
.isNull();
assertThat(SanitizedContents.concatHtml(UNKNOWN_DIR_HTML, NEUTRAL_HTML).getContentDirection())
.isNull();
assertThat(
SanitizedContents.concatHtml(UNKNOWN_DIR_HTML, UNKNOWN_DIR_HTML).getContentDirection())
.isNull();
// x + neutral -> x
assertThat(SanitizedContents.concatHtml(LTR_HTML, NEUTRAL_HTML).getContentDirection())
.isEqualTo(Dir.LTR);
assertThat(SanitizedContents.concatHtml(NEUTRAL_HTML, LTR_HTML).getContentDirection())
.isEqualTo(Dir.LTR);
assertThat(SanitizedContents.concatHtml(RTL_HTML, NEUTRAL_HTML).getContentDirection())
.isEqualTo(Dir.RTL);
assertThat(SanitizedContents.concatHtml(NEUTRAL_HTML, RTL_HTML).getContentDirection())
.isEqualTo(Dir.RTL);
assertThat(SanitizedContents.concatHtml(NEUTRAL_HTML, NEUTRAL_HTML).getContentDirection())
.isEqualTo(Dir.NEUTRAL);
// x + x -> x
assertThat(SanitizedContents.concatHtml(LTR_HTML, LTR_HTML).getContentDirection())
.isEqualTo(Dir.LTR);
assertThat(SanitizedContents.concatHtml(RTL_HTML, RTL_HTML).getContentDirection())
.isEqualTo(Dir.RTL);
// LTR + RTL -> unknown
assertThat(SanitizedContents.concatHtml(LTR_HTML, RTL_HTML).getContentDirection()).isNull();
assertThat(SanitizedContents.concatHtml(LTR_HTML, RTL_HTML).getContentDirection()).isNull();
}
private void assertResourceNameValid(boolean valid, String resourceName, ContentKind kind) {
try {
SanitizedContents.pretendValidateResource(resourceName, kind);
assertWithMessage("No exception was thrown, but wasn't expected to be valid.")
.that(valid)
.isTrue();
} catch (IllegalArgumentException e) {
assertWithMessage("Exception was thrown, but was expected to be valid.")
.that(valid)
.isFalse();
}
}
@Test
public void testPretendValidateResource() {
// Correct resources.
assertResourceNameValid(true, "test.js", ContentKind.JS);
assertResourceNameValid(true, "/test/foo.bar.js", ContentKind.JS);
assertResourceNameValid(true, "test.html", ContentKind.HTML);
assertResourceNameValid(true, "test.svg", ContentKind.HTML);
assertResourceNameValid(true, "test.css", ContentKind.CSS);
// Wrong resource kind.
assertResourceNameValid(false, "test.css", ContentKind.HTML);
assertResourceNameValid(false, "test.html", ContentKind.JS);
assertResourceNameValid(false, "test.js", ContentKind.CSS);
// No file extensions supported for these kinds.
assertResourceNameValid(false, "test.attributes", ContentKind.ATTRIBUTES);
// Missing extension entirely.
assertResourceNameValid(false, "test", ContentKind.JS);
}
@Test
public void testGetDefaultDir() {
assertThat(SanitizedContents.getDefaultDir(ContentKind.JS)).isEqualTo(Dir.LTR);
assertThat(SanitizedContents.getDefaultDir(ContentKind.CSS)).isEqualTo(Dir.LTR);
assertThat(SanitizedContents.getDefaultDir(ContentKind.ATTRIBUTES)).isEqualTo(Dir.LTR);
assertThat(SanitizedContents.getDefaultDir(ContentKind.URI)).isEqualTo(Dir.LTR);
assertThat(SanitizedContents.getDefaultDir(ContentKind.TEXT)).isNull();
assertThat(SanitizedContents.getDefaultDir(ContentKind.HTML)).isNull();
}
@Test
public void testConstantUri() {
// Passing case. We actually can't test a failing case because it won't compile.
SanitizedContent uri = SanitizedContents.constantUri("itms://blahblah");
assertThat(uri.getContent()).isEqualTo("itms://blahblah");
assertThat(uri.getContentKind()).isEqualTo(ContentKind.URI);
assertThat(uri.getContentDirection())
.isEqualTo(SanitizedContents.getDefaultDir(ContentKind.URI));
}
@Test
public void testCommonSafeHtmlTypeConversions() {
final String helloWorldHtml = "Hello <em>World</em>";
final SafeHtml safeHtml =
SafeHtmls.concat(
SafeHtmls.htmlEscape("Hello "),
new SafeHtmlBuilder("em").escapeAndAppendContent("World").build());
final SanitizedContent sanitizedHtml = SanitizedContents.fromSafeHtml(safeHtml);
assertThat(sanitizedHtml.getContentKind()).isEqualTo(ContentKind.HTML);
assertThat(sanitizedHtml.getContent()).isEqualTo(helloWorldHtml);
assertThat(sanitizedHtml.toSafeHtml()).isEqualTo(safeHtml);
// Proto conversions.
final SafeHtmlProto safeHtmlProto = sanitizedHtml.toSafeHtmlProto();
assertThat(SafeHtmls.fromProto(safeHtmlProto)).isEqualTo(safeHtml);
assertThat(SanitizedContents.fromSafeHtmlProto(safeHtmlProto).getContent())
.isEqualTo(helloWorldHtml);
}
@Test
public void testCommonSafeScriptTypeConversions() {
final String testScript = "window.alert('hello');";
final SafeScript safeScript = SafeScripts.fromConstant(testScript);
final SanitizedContent sanitizedScript = SanitizedContents.fromSafeScript(safeScript);
assertThat(sanitizedScript.getContentKind()).isEqualTo(ContentKind.JS);
assertThat(sanitizedScript.getContent()).isEqualTo(testScript);
assertThat(sanitizedScript.getContent()).isEqualTo(safeScript.getSafeScriptString());
// Proto conversions.
final SafeScriptProto safeScriptProto = SafeScripts.toProto(safeScript);
assertThat(SafeScripts.fromProto(safeScriptProto)).isEqualTo(safeScript);
assertThat(SanitizedContents.fromSafeScriptProto(safeScriptProto).getContent())
.isEqualTo(testScript);
}
@Test
public void testCommonSafeStyleSheetTypeConversions() {
final String testCss = "div { display: none; }";
final SafeStyleSheet safeCss = SafeStyleSheets.fromConstant(testCss);
final SanitizedContent sanitizedCss = SanitizedContents.fromSafeStyleSheet(safeCss);
assertThat(sanitizedCss.getContentKind()).isEqualTo(ContentKind.CSS);
assertThat(sanitizedCss.getContent()).isEqualTo(testCss);
assertThat(sanitizedCss.toSafeStyleSheet()).isEqualTo(safeCss);
// Proto conversions.
final SafeStyleSheetProto safeCssProto = sanitizedCss.toSafeStyleSheetProto();
assertThat(SafeStyleSheets.fromProto(safeCssProto)).isEqualTo(safeCss);
assertThat(SanitizedContents.fromSafeStyleSheetProto(safeCssProto).getContent())
.isEqualTo(testCss);
}
@Test
public void testCommonSafeUrlTypeConversions() {
final String testUrl = "http://blahblah";
final SanitizedContent sanitizedConstantUri = SanitizedContents.constantUri(testUrl);
final SafeUrl safeUrl = SafeUrls.fromConstant(testUrl);
final SanitizedContent sanitizedUrl = SanitizedContents.fromSafeUrl(safeUrl);
assertThat(sanitizedUrl.getContentKind()).isEqualTo(ContentKind.URI);
assertThat(sanitizedUrl.getContent()).isEqualTo(testUrl);
assertThat(sanitizedUrl).isEqualTo(sanitizedConstantUri);
// Proto conversions.
final SafeUrlProto safeUrlProto = SafeUrls.toProto(safeUrl);
final SanitizedContent sanitizedUrlProto = SanitizedContents.fromSafeUrlProto(safeUrlProto);
assertThat(sanitizedUrlProto.getContent()).isEqualTo(testUrl);
assertThat(sanitizedUrlProto).isEqualTo(sanitizedConstantUri);
}
@Test
public void testWrongContentKindThrows_html() {
final SanitizedContent notHtml =
UnsafeSanitizedContentOrdainer.ordainAsSafe("not HTML", ContentKind.CSS);
try {
notHtml.toSafeHtml();
fail("Should have thrown on SanitizedContent of kind other than HTML");
} catch (IllegalStateException expected) {
}
try {
notHtml.toSafeHtmlProto();
fail("Should have thrown on SanitizedContent of kind other than HTML");
} catch (IllegalStateException expected) {
}
}
@Test
public void testWrongContentKindThrows_script() {
final SanitizedContent notJs =
UnsafeSanitizedContentOrdainer.ordainAsSafe("not JS", ContentKind.URI);
try {
notJs.toSafeScriptProto();
fail("Should have thrown on SanitizedContent of kind other than JS");
} catch (IllegalStateException expected) {
}
}
@Test
public void testWrongContentKindThrows_css() {
final SanitizedContent notCss =
UnsafeSanitizedContentOrdainer.ordainAsSafe("not CSS", ContentKind.HTML);
try {
notCss.toSafeStyleProto();
fail("Should have thrown on SanitizedContent of kind other than CSS");
} catch (IllegalStateException expected) {
}
try {
notCss.toSafeStyleSheet();
fail("Should have thrown on SanitizedContent of kind other than CSS");
} catch (IllegalStateException expected) {
}
try {
notCss.toSafeStyleSheetProto();
fail("Should have thrown on SanitizedContent of kind other than CSS");
} catch (IllegalStateException expected) {
}
}
@Test
public void testWrongContentKindThrows_uri() {
final SanitizedContent notUri =
UnsafeSanitizedContentOrdainer.ordainAsSafe("not URI", ContentKind.HTML);
try {
notUri.toSafeUrlProto();
fail("Should have thrown on SanitizedContent of kind other than URI");
} catch (IllegalStateException expected) {
}
try {
notUri.toTrustedResourceUrlProto();
fail("Should have thrown on SanitizedContent of kind other than URI");
} catch (IllegalStateException expected) {
}
}
@Test
public void testInvalidStyleSheetContentThrows() {
for (String css :
new String[] {
"display: none;", "{ display: none; }", "/* a comment */", "@import url('test.css');"
}) {
final SanitizedContent cssStyle =
UnsafeSanitizedContentOrdainer.ordainAsSafe(css, ContentKind.CSS);
try {
cssStyle.toSafeStyleSheet();
fail("Should have thrown on CSS SanitizedContent with invalid stylesheet:" + css);
} catch (IllegalStateException expected) {
}
try {
cssStyle.toSafeStyleSheetProto();
fail("Should have thrown on CSS SanitizedContent with invalid stylesheet:" + css);
} catch (IllegalStateException expected) {
}
}
}
}