/* * Copyright 2008 The Closure Compiler 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.google.javascript.jscomp; import static com.google.javascript.jscomp.JsMessage.Style.RELAX; import static com.google.javascript.jscomp.JsMessageVisitor.MESSAGE_TREE_MALFORMED; import com.google.common.collect.Maps; import com.google.javascript.jscomp.JsMessage.Style; import java.util.Map; /** * Test which checks that replacer works correctly. * */ public class ReplaceMessagesTest extends CompilerTestCase { private Map messages; private Style style = RELAX; private boolean strictReplacement; @Override protected CompilerPass getProcessor(Compiler compiler) { return new ReplaceMessages(compiler, new SimpleMessageBundle(), false, style, strictReplacement); } @Override protected int getNumRepetitions() { // No longer valid on the second run. return 1; } @Override protected void setUp() { messages = Maps.newHashMap(); strictReplacement = false; style = RELAX; } public void testReplaceSimpleMessage() { registerMessage(new JsMessage.Builder("MSG_A") .appendStringPart("Hi\nthere") .build()); test("/** @desc d */\n" + "var MSG_A = goog.getMsg('asdf');", "var MSG_A=\"Hi\\nthere\""); } public void testNameReplacement() { registerMessage(new JsMessage.Builder("MSG_B") .appendStringPart("One ") .appendPlaceholderReference("measly") .appendStringPart(" ph") .build()); test("/** @desc d */\n" + "var MSG_B=goog.getMsg('asdf {$measly}', {measly: x});", "var MSG_B=\"One \"+ (x +\" ph\" )"); } public void testGetPropReplacement() { registerMessage(new JsMessage.Builder("MSG_C") .appendPlaceholderReference("amount") .build()); test("/** @desc d */\n" + "var MSG_C = goog.getMsg('${$amount}', {amount: a.b.amount});", "var MSG_C=a.b.amount"); } public void testFunctionCallReplacement() { registerMessage(new JsMessage.Builder("MSG_D") .appendPlaceholderReference("amount") .build()); test("/** @desc d */\n" + "var MSG_D = goog.getMsg('${$amount}', {amount: getAmt()});", "var MSG_D=getAmt()"); } public void testMethodCallReplacement() { registerMessage(new JsMessage.Builder("MSG_E") .appendPlaceholderReference("amount") .build()); test("/** @desc d */\n" + "var MSG_E = goog.getMsg('${$amount}', {amount: obj.getAmt()});", "var MSG_E=obj.getAmt()"); } public void testHookReplacement() { registerMessage(new JsMessage.Builder("MSG_F") .appendStringPart("#") .appendPlaceholderReference("amount") .appendStringPart(".") .build()); test("/** @desc d */\n" + "var MSG_F = goog.getMsg('${$amount}', {amount: (a ? b : c)});", "var MSG_F=\"#\"+((a?b:c)+\".\")"); } public void testAddReplacement() { registerMessage(new JsMessage.Builder("MSG_G") .appendPlaceholderReference("amount") .build()); test("/** @desc d */\n" + "var MSG_G = goog.getMsg('${$amount}', {amount: x + ''});", "var MSG_G=x+\"\""); } public void testPlaceholderValueReferencedTwice() { registerMessage(new JsMessage.Builder("MSG_H") .appendPlaceholderReference("dick") .appendStringPart(", ") .appendPlaceholderReference("dick") .appendStringPart(" and ") .appendPlaceholderReference("jane") .build()); test("/** @desc d */\n" + "var MSG_H = goog.getMsg('{$dick}{$jane}', {jane: x, dick: y});", "var MSG_H=y+(\", \"+(y+(\" and \"+x)))"); } public void testPlaceholderNameInLowerCamelCase() { registerMessage(new JsMessage.Builder("MSG_I") .appendStringPart("Sum: $") .appendPlaceholderReference("amtEarned") .build()); test("/** @desc d */\n" + "var MSG_I = goog.getMsg('${$amtEarned}', {amtEarned: x});", "var MSG_I=\"Sum: $\"+x"); } public void testQualifiedMessageName() { registerMessage(new JsMessage.Builder("MSG_J") .appendStringPart("One ") .appendPlaceholderReference("measly") .appendStringPart(" ph") .build()); test("/** @desc d */\n" + "a.b.c.MSG_J = goog.getMsg('asdf {$measly}', {measly: x});", "a.b.c.MSG_J=\"One \"+(x+\" ph\")"); } public void testSimpleMessageReplacementMissing() { style = Style.LEGACY; test("/** @desc d */\n" + "var MSG_E = 'd*6a0@z>t';", "var MSG_E = 'd*6a0@z>t'"); } public void testSimpleMessageReplacementMissingWithNewStyle() { test("/** @desc d */\n" + "var MSG_E = goog.getMsg('missing');", "var MSG_E = 'missing'"); } public void testStrictModeAndMessageReplacementAbsentInBundle() { strictReplacement = true; test("var MSG_E = 'Hello';", "var MSG_E = 'Hello';", ReplaceMessages.BUNDLE_DOES_NOT_HAVE_THE_MESSAGE); } public void testStrictModeAndMessageReplacementAbsentInNonEmptyBundle() { registerMessage(new JsMessage.Builder("MSG_J") .appendStringPart("One ") .appendPlaceholderReference("measly") .appendStringPart(" ph") .build()); strictReplacement = true; test("var MSG_E = 'Hello';", "var MSG_E = 'Hello';", ReplaceMessages.BUNDLE_DOES_NOT_HAVE_THE_MESSAGE); } public void testFunctionReplacementMissing() { style = Style.LEGACY; test("var MSG_F = function() {return 'asdf'};", "var MSG_F = function() {return\"asdf\"}"); } public void testFunctionWithParamReplacementMissing() { style = Style.LEGACY; test( "var MSG_G = function(measly) {return 'asdf' + measly};", "var MSG_G=function(measly){return\"asdf\"+measly}"); } public void testPlaceholderNameInLowerUnderscoreCase() { test( "var MSG_J = goog.getMsg('${$amt_earned}', {amt_earned: x});", "var MSG_J = goog.getMsg('${$amt_earned}', {amt_earned: x});", MESSAGE_TREE_MALFORMED); } public void testBadPlaceholderReferenceInReplacement() { registerMessage(new JsMessage.Builder("MSG_K") .appendPlaceholderReference("amount") .build()); test( "var MSG_K = goog.getMsg('Hi {$jane}', {jane: x});", "var MSG_K = goog.getMsg('Hi {$jane}', {jane: x});", MESSAGE_TREE_MALFORMED); } public void testLegacyStyleNoPlaceholdersVarSyntax() { registerMessage(new JsMessage.Builder("MSG_A") .appendStringPart("Hi\nthere") .build()); style = Style.LEGACY; test("var MSG_A = 'd*6a0@z>t';", "var MSG_A=\"Hi\\nthere\""); } public void testLegacyStyleNoPlaceholdersFunctionSyntax() { registerMessage(new JsMessage.Builder("MSG_B") .appendStringPart("Hi\nthere") .build()); style = Style.LEGACY; test("var MSG_B = function() {return 'asdf'};", "var MSG_B=function(){return\"Hi\\nthere\"}"); } public void testLegacyStyleOnePlaceholder() { registerMessage(new JsMessage.Builder("MSG_C") .appendStringPart("One ") .appendPlaceholderReference("measly") .appendStringPart(" ph") .build()); style = Style.LEGACY; test( "var MSG_C = function(measly) {return 'asdf' + measly};", "var MSG_C=function(measly){return\"One \"+(measly+\" ph\")}"); } public void testLegacyStyleTwoPlaceholders() { registerMessage(new JsMessage.Builder("MSG_D") .appendPlaceholderReference("dick") .appendStringPart(" and ") .appendPlaceholderReference("jane") .build()); style = Style.LEGACY; test( "var MSG_D = function(jane, dick) {return jane + dick};", "var MSG_D=function(jane,dick){return dick+(\" and \"+jane)}"); } public void testLegacyStylePlaceholderNameInLowerCamelCase() { registerMessage(new JsMessage.Builder("MSG_E") .appendStringPart("Sum: $") .appendPlaceholderReference("amtEarned") .build()); style = Style.LEGACY; test( "var MSG_E = function(amtEarned) {return amtEarned + 'x'};", "var MSG_E=function(amtEarned){return\"Sum: $\"+amtEarned}"); } public void testLegacyStylePlaceholderNameInLowerUnderscoreCase() { registerMessage(new JsMessage.Builder("MSG_F") .appendStringPart("Sum: $") .appendPlaceholderReference("amt_earned") .build()); // Placeholder named in lower-underscore case (discouraged nowadays) style = Style.LEGACY; test( "var MSG_F = function(amt_earned) {return amt_earned + 'x'};", "var MSG_F=function(amt_earned){return\"Sum: $\"+amt_earned}"); } public void testLegacyStyleBadPlaceholderReferenceInReplacemen() { registerMessage(new JsMessage.Builder("MSG_B") .appendStringPart("Ola, ") .appendPlaceholderReference("chimp") .build()); test("var MSG_B = function(chump) {return chump + 'x'};", "var MSG_B = function(chump) {return chump + 'x'};", JsMessageVisitor.MESSAGE_TREE_MALFORMED); } public void testTranslatedPlaceHolderMissMatch() { registerMessage(new JsMessage.Builder("MSG_A") .appendPlaceholderReference("a") .appendStringPart("!") .build()); test("var MSG_A = goog.getMsg('{$a}');", "var MSG_A = goog.getMsg('{$a}');", MESSAGE_TREE_MALFORMED); } public void testBadFallbackSyntax1() { test("/** @desc d */\n" + "var MSG_A = goog.getMsg('asdf');" + "var x = goog.getMsgWithFallback(MSG_A);", null, JsMessageVisitor.BAD_FALLBACK_SYNTAX); } public void testBadFallbackSyntax2() { test("var x = goog.getMsgWithFallback('abc', 'bcd');", null, JsMessageVisitor.BAD_FALLBACK_SYNTAX); } public void testBadFallbackSyntax3() { test("/** @desc d */\n" + "var MSG_A = goog.getMsg('asdf');" + "var x = goog.getMsgWithFallback(MSG_A, y);", null, JsMessageVisitor.FALLBACK_ARG_ERROR); } public void testBadFallbackSyntax4() { test("/** @desc d */\n" + "var MSG_A = goog.getMsg('asdf');" + "var x = goog.getMsgWithFallback(y, MSG_A);", null, JsMessageVisitor.FALLBACK_ARG_ERROR); } public void testUseFallback() { registerMessage(new JsMessage.Builder("MSG_B") .appendStringPart("translated") .build()); test("/** @desc d */\n" + "var MSG_A = goog.getMsg('msg A');" + "/** @desc d */\n" + "var MSG_B = goog.getMsg('msg B');" + "var x = goog.getMsgWithFallback(MSG_A, MSG_B);", "var MSG_A = 'msg A';" + "var MSG_B = 'translated';" + "var x = MSG_B;"); } public void testFallbackEmptyBundle() { test("/** @desc d */\n" + "var MSG_A = goog.getMsg('msg A');" + "/** @desc d */\n" + "var MSG_B = goog.getMsg('msg B');" + "var x = goog.getMsgWithFallback(MSG_A, MSG_B);", "var MSG_A = 'msg A';" + "var MSG_B = 'msg B';" + "var x = MSG_A;"); } public void testNoUseFallback() { registerMessage(new JsMessage.Builder("MSG_A") .appendStringPart("translated") .build()); test("/** @desc d */\n" + "var MSG_A = goog.getMsg('msg A');" + "/** @desc d */\n" + "var MSG_B = goog.getMsg('msg B');" + "var x = goog.getMsgWithFallback(MSG_A, MSG_B);", "var MSG_A = 'translated';" + "var MSG_B = 'msg B';" + "var x = MSG_A;"); } public void testNoUseFallback2() { registerMessage(new JsMessage.Builder("MSG_C") .appendStringPart("translated") .build()); test("/** @desc d */\n" + "var MSG_A = goog.getMsg('msg A');" + "/** @desc d */\n" + "var MSG_B = goog.getMsg('msg B');" + "var x = goog.getMsgWithFallback(MSG_A, MSG_B);", "var MSG_A = 'msg A';" + "var MSG_B = 'msg B';" + "var x = MSG_A;"); } private void registerMessage(JsMessage message) { messages.put(message.getKey(), message); } private class SimpleMessageBundle implements MessageBundle { @Override public JsMessage getMessage(String id) { return messages.get(id); } @Override public Iterable getAllMessages() { return messages.values(); } @Override public JsMessage.IdGenerator idGenerator() { return null; } } }