tagged-template-literals.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. describe("tagged template literal errors", () => {
  2. test("undefined variables in template expression throw a ReferenceError", () => {
  3. expect(() => {
  4. foo`bar${baz}`;
  5. }).toThrowWithMessage(ReferenceError, "'foo' is not defined");
  6. expect(() => {
  7. function foo() {}
  8. foo`bar${baz}`;
  9. }).toThrowWithMessage(ReferenceError, "'baz' is not defined");
  10. });
  11. test("cannot tag a non-function", () => {
  12. expect(() => {
  13. undefined``;
  14. }).toThrowWithMessage(TypeError, "undefined is not a function");
  15. });
  16. });
  17. describe("tagged template literal functionality", () => {
  18. test("empty template tag", () => {
  19. function test1(strings) {
  20. expect(strings).toBeInstanceOf(Array);
  21. expect(strings).toHaveLength(1);
  22. expect(strings[0]).toBe("");
  23. return 42;
  24. }
  25. expect(test1``).toBe(42);
  26. });
  27. test("tagging a template literal", () => {
  28. function test2(s) {
  29. return function (strings) {
  30. expect(strings).toBeInstanceOf(Array);
  31. expect(strings).toHaveLength(1);
  32. expect(strings[0]).toBe("bar");
  33. return s + strings[0];
  34. };
  35. }
  36. expect(test2("foo")`bar`).toBe("foobar");
  37. });
  38. test("tagging an object function key", () => {
  39. var test3 = {
  40. foo(strings, p1) {
  41. expect(strings).toBeInstanceOf(Array);
  42. expect(strings).toHaveLength(2);
  43. expect(strings[0]).toBe("");
  44. expect(strings[1]).toBe("");
  45. expect(p1).toBe("bar");
  46. },
  47. };
  48. test3.foo`${"bar"}`;
  49. });
  50. test("tagging with a variable in a template expression", () => {
  51. function test4(strings, p1) {
  52. expect(strings).toBeInstanceOf(Array);
  53. expect(strings).toHaveLength(2);
  54. expect(strings[0]).toBe("foo");
  55. expect(strings[1]).toBe("");
  56. expect(p1).toBe(42);
  57. }
  58. var bar = 42;
  59. test4`foo${bar}`;
  60. });
  61. test("template tag result of another template tag", () => {
  62. function test5(strings, p1, p2) {
  63. expect(strings).toBeInstanceOf(Array);
  64. expect(strings).toHaveLength(3);
  65. expect(strings[0]).toBe("foo");
  66. expect(strings[1]).toBe("baz");
  67. expect(strings[2]).toBe("");
  68. expect(p1).toBe(42);
  69. expect(p2).toBe("qux");
  70. return (strings, value) => `${value}${strings[0]}`;
  71. }
  72. var bar = 42;
  73. expect(test5`foo${bar}baz${"qux"}``test${123}`).toBe("123test");
  74. });
  75. test("general test", () => {
  76. function review(strings, name, rating) {
  77. return `${strings[0]}**${name}**${strings[1]}_${rating}_${strings[2]}`;
  78. }
  79. var name = "SerenityOS";
  80. var rating = "great";
  81. expect(review`${name} is a ${rating} project!`).toBe(
  82. "**SerenityOS** is a _great_ project!"
  83. );
  84. });
  85. test("template object structure", () => {
  86. const getTemplateObject = (...rest) => rest;
  87. const getRawTemplateStrings = arr => arr.raw;
  88. let o = getTemplateObject`foo\nbar`;
  89. expect(Object.getOwnPropertyNames(o[0])).toContain("raw");
  90. let raw = getRawTemplateStrings`foo${1 + 3}\nbar`;
  91. expect(Object.getOwnPropertyNames(raw)).not.toContain("raw");
  92. expect(raw).toHaveLength(2);
  93. expect(raw[0]).toBe("foo");
  94. expect(raw[1]).toHaveLength(5);
  95. expect(raw[1]).toBe("\\nbar");
  96. });
  97. test("invalid escapes give undefined cooked values but can be accessed in raw form", () => {
  98. let calls = 0;
  99. let lastValue = null;
  100. function noCookedButRaw(values) {
  101. ++calls;
  102. expect(values).not.toBeNull();
  103. expect(values.raw).toHaveLength(1);
  104. expect(values.raw[0].length).toBeGreaterThan(0);
  105. expect(values.raw[0].charAt(0)).toBe("\\");
  106. expect(values[0]).toBeUndefined();
  107. lastValue = values.raw[0];
  108. }
  109. noCookedButRaw`\u`;
  110. expect(calls).toBe(1);
  111. expect(lastValue).toBe("\\u");
  112. noCookedButRaw`\01`;
  113. expect(calls).toBe(2);
  114. expect(lastValue).toBe("\\01");
  115. noCookedButRaw`\u{10FFFFF}`;
  116. expect(calls).toBe(3);
  117. expect(lastValue).toBe("\\u{10FFFFF}");
  118. });
  119. test("for multiple values gives undefined only for invalid strings", () => {
  120. let restValue = null;
  121. let stringsValue = null;
  122. let calls = 0;
  123. function extractArguments(value, ...arguments) {
  124. ++calls;
  125. restValue = arguments;
  126. stringsValue = value;
  127. }
  128. extractArguments`valid${1}invalid\u`;
  129. expect(calls).toBe(1);
  130. expect(restValue).toHaveLength(1);
  131. expect(restValue[0]).toBe(1);
  132. expect(stringsValue).toHaveLength(2);
  133. expect(stringsValue[0]).toBe("valid");
  134. expect(stringsValue[1]).toBeUndefined();
  135. expect(stringsValue.raw).toHaveLength(2);
  136. expect(stringsValue.raw[0]).toBe("valid");
  137. expect(stringsValue.raw[1]).toBe("invalid\\u");
  138. });
  139. test.xfail("string value gets cached per AST node", () => {
  140. function call(func, val) {
  141. return func`template${val}second`;
  142. }
  143. let firstResult = call(value => value, 1);
  144. let secondResult = call(value => value, 2);
  145. expect(firstResult).toBe(secondResult);
  146. });
  147. test.xfail("this value of call comes from reference", () => {
  148. let thisValue = null;
  149. const obj = {
  150. func() {
  151. thisValue = this;
  152. },
  153. };
  154. obj.func``;
  155. expect(thisValue).toBe(obj);
  156. });
  157. });