1. """Tests for jslex."""
    
  2. # originally from https://bitbucket.org/ned/jslex
    
  3. 
    
  4. from django.test import SimpleTestCase
    
  5. from django.utils.jslex import JsLexer, prepare_js_for_gettext
    
  6. 
    
  7. 
    
  8. class JsTokensTest(SimpleTestCase):
    
  9.     LEX_CASES = [
    
  10.         # ids
    
  11.         ("a ABC $ _ a123", ["id a", "id ABC", "id $", "id _", "id a123"]),
    
  12.         (
    
  13.             "\\u1234 abc\\u0020 \\u0065_\\u0067",
    
  14.             ["id \\u1234", "id abc\\u0020", "id \\u0065_\\u0067"],
    
  15.         ),
    
  16.         # numbers
    
  17.         (
    
  18.             "123 1.234 0.123e-3 0 1E+40 1e1 .123",
    
  19.             [
    
  20.                 "dnum 123",
    
  21.                 "dnum 1.234",
    
  22.                 "dnum 0.123e-3",
    
  23.                 "dnum 0",
    
  24.                 "dnum 1E+40",
    
  25.                 "dnum 1e1",
    
  26.                 "dnum .123",
    
  27.             ],
    
  28.         ),
    
  29.         ("0x1 0xabCD 0XABcd", ["hnum 0x1", "hnum 0xabCD", "hnum 0XABcd"]),
    
  30.         ("010 0377 090", ["onum 010", "onum 0377", "dnum 0", "dnum 90"]),
    
  31.         ("0xa123ghi", ["hnum 0xa123", "id ghi"]),
    
  32.         # keywords
    
  33.         (
    
  34.             "function Function FUNCTION",
    
  35.             ["keyword function", "id Function", "id FUNCTION"],
    
  36.         ),
    
  37.         (
    
  38.             "const constructor in inherits",
    
  39.             ["keyword const", "id constructor", "keyword in", "id inherits"],
    
  40.         ),
    
  41.         ("true true_enough", ["reserved true", "id true_enough"]),
    
  42.         # strings
    
  43.         (""" 'hello' "hello" """, ["string 'hello'", 'string "hello"']),
    
  44.         (
    
  45.             r""" 'don\'t' "don\"t" '"' "'" '\'' "\"" """,
    
  46.             [
    
  47.                 r"""string 'don\'t'""",
    
  48.                 r'''string "don\"t"''',
    
  49.                 r"""string '"'""",
    
  50.                 r'''string "'"''',
    
  51.                 r"""string '\''""",
    
  52.                 r'''string "\""''',
    
  53.             ],
    
  54.         ),
    
  55.         (r'"ƃuıxǝ⅂ ʇdıɹɔsɐʌɐſ\""', [r'string "ƃuıxǝ⅂ ʇdıɹɔsɐʌɐſ\""']),
    
  56.         # comments
    
  57.         ("a//b", ["id a", "linecomment //b"]),
    
  58.         (
    
  59.             "/****/a/=2//hello",
    
  60.             ["comment /****/", "id a", "punct /=", "dnum 2", "linecomment //hello"],
    
  61.         ),
    
  62.         (
    
  63.             "/*\n * Header\n */\na=1;",
    
  64.             ["comment /*\n * Header\n */", "id a", "punct =", "dnum 1", "punct ;"],
    
  65.         ),
    
  66.         # punctuation
    
  67.         ("a+++b", ["id a", "punct ++", "punct +", "id b"]),
    
  68.         # regex
    
  69.         (r"a=/a*/,1", ["id a", "punct =", "regex /a*/", "punct ,", "dnum 1"]),
    
  70.         (r"a=/a*[^/]+/,1", ["id a", "punct =", "regex /a*[^/]+/", "punct ,", "dnum 1"]),
    
  71.         (r"a=/a*\[^/,1", ["id a", "punct =", r"regex /a*\[^/", "punct ,", "dnum 1"]),
    
  72.         (r"a=/\//,1", ["id a", "punct =", r"regex /\//", "punct ,", "dnum 1"]),
    
  73.         # next two are from https://www-archive.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions  # NOQA
    
  74.         (
    
  75.             'for (var x = a in foo && "</x>" || mot ? z:/x:3;x<5;y</g/i) {xyz(x++);}',
    
  76.             [
    
  77.                 "keyword for",
    
  78.                 "punct (",
    
  79.                 "keyword var",
    
  80.                 "id x",
    
  81.                 "punct =",
    
  82.                 "id a",
    
  83.                 "keyword in",
    
  84.                 "id foo",
    
  85.                 "punct &&",
    
  86.                 'string "</x>"',
    
  87.                 "punct ||",
    
  88.                 "id mot",
    
  89.                 "punct ?",
    
  90.                 "id z",
    
  91.                 "punct :",
    
  92.                 "regex /x:3;x<5;y</g",
    
  93.                 "punct /",
    
  94.                 "id i",
    
  95.                 "punct )",
    
  96.                 "punct {",
    
  97.                 "id xyz",
    
  98.                 "punct (",
    
  99.                 "id x",
    
  100.                 "punct ++",
    
  101.                 "punct )",
    
  102.                 "punct ;",
    
  103.                 "punct }",
    
  104.             ],
    
  105.         ),
    
  106.         (
    
  107.             'for (var x = a in foo && "</x>" || mot ? z/x:3;x<5;y</g/i) {xyz(x++);}',
    
  108.             [
    
  109.                 "keyword for",
    
  110.                 "punct (",
    
  111.                 "keyword var",
    
  112.                 "id x",
    
  113.                 "punct =",
    
  114.                 "id a",
    
  115.                 "keyword in",
    
  116.                 "id foo",
    
  117.                 "punct &&",
    
  118.                 'string "</x>"',
    
  119.                 "punct ||",
    
  120.                 "id mot",
    
  121.                 "punct ?",
    
  122.                 "id z",
    
  123.                 "punct /",
    
  124.                 "id x",
    
  125.                 "punct :",
    
  126.                 "dnum 3",
    
  127.                 "punct ;",
    
  128.                 "id x",
    
  129.                 "punct <",
    
  130.                 "dnum 5",
    
  131.                 "punct ;",
    
  132.                 "id y",
    
  133.                 "punct <",
    
  134.                 "regex /g/i",
    
  135.                 "punct )",
    
  136.                 "punct {",
    
  137.                 "id xyz",
    
  138.                 "punct (",
    
  139.                 "id x",
    
  140.                 "punct ++",
    
  141.                 "punct )",
    
  142.                 "punct ;",
    
  143.                 "punct }",
    
  144.             ],
    
  145.         ),
    
  146.         # Various "illegal" regexes that are valid according to the std.
    
  147.         (
    
  148.             r"""/????/, /++++/, /[----]/ """,
    
  149.             ["regex /????/", "punct ,", "regex /++++/", "punct ,", "regex /[----]/"],
    
  150.         ),
    
  151.         # Stress cases from https://stackoverflow.com/questions/5533925/what-javascript-constructs-does-jslex-incorrectly-lex/5573409#5573409  # NOQA
    
  152.         (r"""/\[/""", [r"""regex /\[/"""]),
    
  153.         (r"""/[i]/""", [r"""regex /[i]/"""]),
    
  154.         (r"""/[\]]/""", [r"""regex /[\]]/"""]),
    
  155.         (r"""/a[\]]/""", [r"""regex /a[\]]/"""]),
    
  156.         (r"""/a[\]]b/""", [r"""regex /a[\]]b/"""]),
    
  157.         (r"""/[\]/]/gi""", [r"""regex /[\]/]/gi"""]),
    
  158.         (r"""/\[[^\]]+\]/gi""", [r"""regex /\[[^\]]+\]/gi"""]),
    
  159.         (
    
  160.             r"""
    
  161.                 rexl.re = {
    
  162.                 NAME: /^(?![0-9])(?:\w)+|^"(?:[^"]|"")+"/,
    
  163.                 UNQUOTED_LITERAL: /^@(?:(?![0-9])(?:\w|\:)+|^"(?:[^"]|"")+")\[[^\]]+\]/,
    
  164.                 QUOTED_LITERAL: /^'(?:[^']|'')*'/,
    
  165.                 NUMERIC_LITERAL: /^[0-9]+(?:\.[0-9]*(?:[eE][-+][0-9]+)?)?/,
    
  166.                 SYMBOL: /^(?:==|=|<>|<=|<|>=|>|!~~|!~|~~|~|!==|!=|!~=|!~|!|&|\||\.|\:|,|\(|\)|\[|\]|\{|\}|\?|\:|;|@|\^|\/\+|\/|\*|\+|-)/
    
  167.                 };
    
  168.             """,  # NOQA
    
  169.             [
    
  170.                 "id rexl",
    
  171.                 "punct .",
    
  172.                 "id re",
    
  173.                 "punct =",
    
  174.                 "punct {",
    
  175.                 "id NAME",
    
  176.                 "punct :",
    
  177.                 r"""regex /^(?![0-9])(?:\w)+|^"(?:[^"]|"")+"/""",
    
  178.                 "punct ,",
    
  179.                 "id UNQUOTED_LITERAL",
    
  180.                 "punct :",
    
  181.                 r"""regex /^@(?:(?![0-9])(?:\w|\:)+|^"(?:[^"]|"")+")\[[^\]]+\]/""",
    
  182.                 "punct ,",
    
  183.                 "id QUOTED_LITERAL",
    
  184.                 "punct :",
    
  185.                 r"""regex /^'(?:[^']|'')*'/""",
    
  186.                 "punct ,",
    
  187.                 "id NUMERIC_LITERAL",
    
  188.                 "punct :",
    
  189.                 r"""regex /^[0-9]+(?:\.[0-9]*(?:[eE][-+][0-9]+)?)?/""",
    
  190.                 "punct ,",
    
  191.                 "id SYMBOL",
    
  192.                 "punct :",
    
  193.                 r"""regex /^(?:==|=|<>|<=|<|>=|>|!~~|!~|~~|~|!==|!=|!~=|!~|!|&|\||\.|\:|,|\(|\)|\[|\]|\{|\}|\?|\:|;|@|\^|\/\+|\/|\*|\+|-)/""",  # NOQA
    
  194.                 "punct }",
    
  195.                 "punct ;",
    
  196.             ],
    
  197.         ),
    
  198.         (
    
  199.             r"""
    
  200.                 rexl.re = {
    
  201.                 NAME: /^(?![0-9])(?:\w)+|^"(?:[^"]|"")+"/,
    
  202.                 UNQUOTED_LITERAL: /^@(?:(?![0-9])(?:\w|\:)+|^"(?:[^"]|"")+")\[[^\]]+\]/,
    
  203.                 QUOTED_LITERAL: /^'(?:[^']|'')*'/,
    
  204.                 NUMERIC_LITERAL: /^[0-9]+(?:\.[0-9]*(?:[eE][-+][0-9]+)?)?/,
    
  205.                 SYMBOL: /^(?:==|=|<>|<=|<|>=|>|!~~|!~|~~|~|!==|!=|!~=|!~|!|&|\||\.|\:|,|\(|\)|\[|\]|\{|\}|\?|\:|;|@|\^|\/\+|\/|\*|\+|-)/
    
  206.                 };
    
  207.                 str = '"';
    
  208.             """,  # NOQA
    
  209.             [
    
  210.                 "id rexl",
    
  211.                 "punct .",
    
  212.                 "id re",
    
  213.                 "punct =",
    
  214.                 "punct {",
    
  215.                 "id NAME",
    
  216.                 "punct :",
    
  217.                 r"""regex /^(?![0-9])(?:\w)+|^"(?:[^"]|"")+"/""",
    
  218.                 "punct ,",
    
  219.                 "id UNQUOTED_LITERAL",
    
  220.                 "punct :",
    
  221.                 r"""regex /^@(?:(?![0-9])(?:\w|\:)+|^"(?:[^"]|"")+")\[[^\]]+\]/""",
    
  222.                 "punct ,",
    
  223.                 "id QUOTED_LITERAL",
    
  224.                 "punct :",
    
  225.                 r"""regex /^'(?:[^']|'')*'/""",
    
  226.                 "punct ,",
    
  227.                 "id NUMERIC_LITERAL",
    
  228.                 "punct :",
    
  229.                 r"""regex /^[0-9]+(?:\.[0-9]*(?:[eE][-+][0-9]+)?)?/""",
    
  230.                 "punct ,",
    
  231.                 "id SYMBOL",
    
  232.                 "punct :",
    
  233.                 r"""regex /^(?:==|=|<>|<=|<|>=|>|!~~|!~|~~|~|!==|!=|!~=|!~|!|&|\||\.|\:|,|\(|\)|\[|\]|\{|\}|\?|\:|;|@|\^|\/\+|\/|\*|\+|-)/""",  # NOQA
    
  234.                 "punct }",
    
  235.                 "punct ;",
    
  236.                 "id str",
    
  237.                 "punct =",
    
  238.                 """string '"'""",
    
  239.                 "punct ;",
    
  240.             ],
    
  241.         ),
    
  242.         (
    
  243.             r' this._js = "e.str(\"" + this.value.replace(/\\/g, "\\\\")'
    
  244.             r'.replace(/"/g, "\\\"") + "\")"; ',
    
  245.             [
    
  246.                 "keyword this",
    
  247.                 "punct .",
    
  248.                 "id _js",
    
  249.                 "punct =",
    
  250.                 r'''string "e.str(\""''',
    
  251.                 "punct +",
    
  252.                 "keyword this",
    
  253.                 "punct .",
    
  254.                 "id value",
    
  255.                 "punct .",
    
  256.                 "id replace",
    
  257.                 "punct (",
    
  258.                 r"regex /\\/g",
    
  259.                 "punct ,",
    
  260.                 r'string "\\\\"',
    
  261.                 "punct )",
    
  262.                 "punct .",
    
  263.                 "id replace",
    
  264.                 "punct (",
    
  265.                 r'regex /"/g',
    
  266.                 "punct ,",
    
  267.                 r'string "\\\""',
    
  268.                 "punct )",
    
  269.                 "punct +",
    
  270.                 r'string "\")"',
    
  271.                 "punct ;",
    
  272.             ],
    
  273.         ),
    
  274.     ]
    
  275. 
    
  276. 
    
  277. def make_function(input, toks):
    
  278.     def test_func(self):
    
  279.         lexer = JsLexer()
    
  280.         result = [
    
  281.             "%s %s" % (name, tok) for name, tok in lexer.lex(input) if name != "ws"
    
  282.         ]
    
  283.         self.assertEqual(result, toks)
    
  284. 
    
  285.     return test_func
    
  286. 
    
  287. 
    
  288. for i, (input, toks) in enumerate(JsTokensTest.LEX_CASES):
    
  289.     setattr(JsTokensTest, "test_case_%d" % i, make_function(input, toks))
    
  290. 
    
  291. 
    
  292. GETTEXT_CASES = (
    
  293.     (
    
  294.         r"""
    
  295.             a = 1; /* /[0-9]+/ */
    
  296.             b = 0x2a0b / 1; // /[0-9]+/
    
  297.             c = 3;
    
  298.         """,
    
  299.         r"""
    
  300.             a = 1; /* /[0-9]+/ */
    
  301.             b = 0x2a0b / 1; // /[0-9]+/
    
  302.             c = 3;
    
  303.         """,
    
  304.     ),
    
  305.     (
    
  306.         r"""
    
  307.             a = 1.234e-5;
    
  308.             /*
    
  309.              * /[0-9+/
    
  310.              */
    
  311.             b = .0123;
    
  312.         """,
    
  313.         r"""
    
  314.             a = 1.234e-5;
    
  315.             /*
    
  316.              * /[0-9+/
    
  317.              */
    
  318.             b = .0123;
    
  319.         """,
    
  320.     ),
    
  321.     (
    
  322.         r"""
    
  323.             x = y / z;
    
  324.             alert(gettext("hello"));
    
  325.             x /= 3;
    
  326.         """,
    
  327.         r"""
    
  328.             x = y / z;
    
  329.             alert(gettext("hello"));
    
  330.             x /= 3;
    
  331.         """,
    
  332.     ),
    
  333.     (
    
  334.         r"""
    
  335.             s = "Hello \"th/foo/ere\"";
    
  336.             s = 'He\x23llo \'th/foo/ere\'';
    
  337.             s = 'slash quote \", just quote "';
    
  338.         """,
    
  339.         r"""
    
  340.             s = "Hello \"th/foo/ere\"";
    
  341.             s = "He\x23llo \'th/foo/ere\'";
    
  342.             s = "slash quote \", just quote \"";
    
  343.         """,
    
  344.     ),
    
  345.     (
    
  346.         r"""
    
  347.             s = "Line continuation\
    
  348.             continued /hello/ still the string";/hello/;
    
  349.         """,
    
  350.         r"""
    
  351.             s = "Line continuation\
    
  352.             continued /hello/ still the string";"REGEX";
    
  353.         """,
    
  354.     ),
    
  355.     (
    
  356.         r"""
    
  357.             var regex = /pattern/;
    
  358.             var regex2 = /matter/gm;
    
  359.             var regex3 = /[*/]+/gm.foo("hey");
    
  360.         """,
    
  361.         r"""
    
  362.             var regex = "REGEX";
    
  363.             var regex2 = "REGEX";
    
  364.             var regex3 = "REGEX".foo("hey");
    
  365.         """,
    
  366.     ),
    
  367.     (
    
  368.         r"""
    
  369.             for (var x = a in foo && "</x>" || mot ? z:/x:3;x<5;y</g/i) {xyz(x++);}
    
  370.             for (var x = a in foo && "</x>" || mot ? z/x:3;x<5;y</g/i) {xyz(x++);}
    
  371.         """,
    
  372.         r"""
    
  373.             for (var x = a in foo && "</x>" || mot ? z:"REGEX"/i) {xyz(x++);}
    
  374.             for (var x = a in foo && "</x>" || mot ? z/x:3;x<5;y<"REGEX") {xyz(x++);}
    
  375.         """,
    
  376.     ),
    
  377.     (
    
  378.         """
    
  379.             \\u1234xyz = gettext('Hello there');
    
  380.         """,
    
  381.         r"""
    
  382.             Uu1234xyz = gettext("Hello there");
    
  383.         """,
    
  384.     ),
    
  385. )
    
  386. 
    
  387. 
    
  388. class JsToCForGettextTest(SimpleTestCase):
    
  389.     pass
    
  390. 
    
  391. 
    
  392. def make_function(js, c):
    
  393.     def test_func(self):
    
  394.         self.assertEqual(prepare_js_for_gettext(js), c)
    
  395. 
    
  396.     return test_func
    
  397. 
    
  398. 
    
  399. for i, pair in enumerate(GETTEXT_CASES):
    
  400.     setattr(JsToCForGettextTest, "test_case_%d" % i, make_function(*pair))