2
0

scripting_spec.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2022 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * Javascript code in this page
  21. */
  22. "use strict";
  23. var _display_utils = require("../../display/display_utils.js");
  24. const sandboxBundleSrc = "../../build/generic/build/pdf.sandbox.js";
  25. describe("Scripting", function () {
  26. let sandbox, send_queue, test_id, ref, windowAlert;
  27. function getId() {
  28. const id = `${ref++}R`;
  29. return id;
  30. }
  31. function myeval(code) {
  32. const key = (test_id++).toString();
  33. return sandbox.eval(code, key).then(() => {
  34. const result = send_queue.get(key).result;
  35. send_queue.delete(key);
  36. return result;
  37. });
  38. }
  39. beforeAll(function () {
  40. test_id = 0;
  41. ref = 1;
  42. send_queue = new Map();
  43. window.dispatchEvent = event => {
  44. if (event.detail.command) {
  45. send_queue.set(event.detail.command, event.detail);
  46. } else if (send_queue.has(event.detail.id)) {
  47. const prev = send_queue.get(event.detail.id);
  48. Object.assign(prev, event.detail);
  49. } else {
  50. send_queue.set(event.detail.id, event.detail);
  51. }
  52. };
  53. windowAlert = window.alert;
  54. window.alert = value => {
  55. const command = "alert";
  56. send_queue.set(command, {
  57. command,
  58. value
  59. });
  60. };
  61. const promise = (0, _display_utils.loadScript)(sandboxBundleSrc).then(() => {
  62. return window.pdfjsSandbox.QuickJSSandbox();
  63. });
  64. sandbox = {
  65. createSandbox(data) {
  66. promise.then(sbx => sbx.create(data));
  67. },
  68. dispatchEventInSandbox(data) {
  69. return promise.then(sbx => sbx.dispatchEvent(data));
  70. },
  71. nukeSandbox() {
  72. promise.then(sbx => sbx.nukeSandbox());
  73. },
  74. eval(code, key) {
  75. return promise.then(sbx => sbx.evalForTesting(code, key));
  76. }
  77. };
  78. });
  79. afterAll(function () {
  80. sandbox.nukeSandbox();
  81. sandbox = null;
  82. send_queue = null;
  83. window.alert = windowAlert;
  84. });
  85. describe("Sandbox", function () {
  86. it("should send a value, execute an action and get back a new value", async () => {
  87. function compute(n) {
  88. let s = 0;
  89. for (let i = 0; i < n; i++) {
  90. s += i;
  91. }
  92. return s;
  93. }
  94. const number = 123;
  95. const expected = ((number - 1) * number / 2).toString();
  96. const refId = getId();
  97. const data = {
  98. objects: {
  99. field: [{
  100. id: refId,
  101. value: "",
  102. actions: {
  103. Keystroke: [`${compute.toString()}event.value = compute(parseInt(event.value));`]
  104. },
  105. type: "text"
  106. }]
  107. },
  108. calculationOrder: [],
  109. appInfo: {
  110. language: "en-US",
  111. platform: "Linux x86_64"
  112. }
  113. };
  114. sandbox.createSandbox(data);
  115. await sandbox.dispatchEventInSandbox({
  116. id: refId,
  117. value: `${number}`,
  118. name: "Keystroke",
  119. willCommit: true
  120. });
  121. expect(send_queue.has(refId)).toEqual(true);
  122. expect(send_queue.get(refId)).toEqual({
  123. id: refId,
  124. valueAsString: expected
  125. });
  126. });
  127. });
  128. describe("Doc", function () {
  129. it("should treat globalThis as the doc", async () => {
  130. const refId = getId();
  131. const data = {
  132. objects: {
  133. field: [{
  134. id: refId,
  135. value: "",
  136. actions: {},
  137. type: "text"
  138. }]
  139. },
  140. appInfo: {
  141. language: "en-US",
  142. platform: "Linux x86_64"
  143. },
  144. calculationOrder: [],
  145. dispatchEventName: "_dispatchMe"
  146. };
  147. sandbox.createSandbox(data);
  148. await myeval(`(this.foobar = 123456, 0)`);
  149. const value = await myeval(`this.getField("field").doc.foobar`);
  150. expect(value).toEqual(123456);
  151. });
  152. it("should get field using a path", async () => {
  153. const base = value => {
  154. return {
  155. id: getId(),
  156. value,
  157. actions: {},
  158. type: "text"
  159. };
  160. };
  161. const data = {
  162. objects: {
  163. A: [base(1)],
  164. "A.B": [base(2)],
  165. "A.B.C": [base(3)],
  166. "A.B.C.D": [base(4)],
  167. "A.B.C.D.E": [base(5)],
  168. "A.B.C.D.E.F": [base(6)],
  169. "A.B.C.D.G": [base(7)],
  170. C: [base(8)]
  171. },
  172. appInfo: {
  173. language: "en-US",
  174. platform: "Linux x86_64"
  175. },
  176. calculationOrder: [],
  177. dispatchEventName: "_dispatchMe"
  178. };
  179. sandbox.createSandbox(data);
  180. let value = await myeval(`this.getField("A").value`);
  181. expect(value).toEqual(1);
  182. value = await myeval(`this.getField("B.C").value`);
  183. expect(value).toEqual(3);
  184. value = await myeval(`this.getField("B.C").value`);
  185. expect(value).toEqual(3);
  186. value = await myeval(`this.getField("B.C.D#0").value`);
  187. expect(value).toEqual(5);
  188. value = await myeval(`this.getField("B.C.D#1").value`);
  189. expect(value).toEqual(7);
  190. value = await myeval(`this.getField("C").value`);
  191. expect(value).toEqual(8);
  192. value = await myeval(`this.getField("A.B.C.D").getArray().map((x) => x.value)`);
  193. expect(value).toEqual([5, 7]);
  194. });
  195. });
  196. describe("Util", function () {
  197. beforeAll(function () {
  198. sandbox.createSandbox({
  199. appInfo: {
  200. language: "en-US",
  201. platform: "Linux x86_64"
  202. },
  203. objects: {},
  204. calculationOrder: []
  205. });
  206. });
  207. describe("printd", function () {
  208. it("should print a date according to a format", async () => {
  209. const date = `new Date("Sun Apr 15 2007 03:14:15")`;
  210. let value = await myeval(`util.printd(0, ${date})`);
  211. expect(value).toEqual("D:20070415031415");
  212. value = await myeval(`util.printd(1, ${date})`);
  213. expect(value).toEqual("2007.04.15 03:14:15");
  214. value = await myeval(`util.printd(2, ${date})`);
  215. expect(value).toEqual("4/15/07 3:14:15 am");
  216. value = await myeval(`util.printd("mmmm mmm mm m", ${date})`);
  217. expect(value).toEqual("April Apr 04 4");
  218. value = await myeval(`util.printd("dddd ddd dd d", ${date})`);
  219. expect(value).toEqual("Sunday Sun 15 15");
  220. });
  221. });
  222. describe("scand", function () {
  223. it("should parse a date according to a format", async () => {
  224. const date = new Date("Sun Apr 15 2007 03:14:15");
  225. let value = await myeval(`util.scand(0, "D:20070415031415").toString()`);
  226. expect(new Date(value)).toEqual(date);
  227. value = await myeval(`util.scand(1, "2007.04.15 03:14:15").toString()`);
  228. expect(new Date(value)).toEqual(date);
  229. value = await myeval(`util.scand(2, "4/15/07 3:14:15 am").toString()`);
  230. expect(new Date(value)).toEqual(date);
  231. });
  232. });
  233. describe("printf", function () {
  234. it("should print some data according to a format", async () => {
  235. let value = await myeval(`util.printf("Integer numbers: %d, %d,...", 1.234, 56.789)`);
  236. expect(value).toEqual("Integer numbers: 1, 56,...");
  237. value = await myeval(`util.printf("Hex numbers: %x, %x,...", 1234, 56789)`);
  238. expect(value).toEqual("Hex numbers: 4D2, DDD5,...");
  239. value = await myeval(`util.printf("Hex numbers with 0x: %#x, %#x,...", 1234, 56789)`);
  240. expect(value).toEqual("Hex numbers with 0x: 0x4D2, 0xDDD5,...");
  241. value = await myeval(`util.printf("Decimal number: %,0+.3f", 1234567.89123)`);
  242. expect(value).toEqual("Decimal number: +1,234,567.891");
  243. value = await myeval(`util.printf("Decimal number: %,0+8.3f", 1.234567)`);
  244. expect(value).toEqual("Decimal number: + 1.235");
  245. value = await myeval(`util.printf("Decimal number: %,0.2f", -12.34567)`);
  246. expect(value).toEqual("Decimal number: -12.35");
  247. });
  248. it("should print a string with no argument", async () => {
  249. const value = await myeval(`util.printf("hello world")`);
  250. expect(value).toEqual("hello world");
  251. });
  252. it("print a string with a percent", async () => {
  253. const value = await myeval(`util.printf("%%s")`);
  254. expect(value).toEqual("%%s");
  255. });
  256. });
  257. describe("printx", function () {
  258. it("should print some data according to a format", async () => {
  259. const value = await myeval(`util.printx("9 (999) 999-9999", "aaa14159697489zzz")`);
  260. expect(value).toEqual("1 (415) 969-7489");
  261. });
  262. });
  263. });
  264. describe("Events", function () {
  265. it("should trigger an event and modify the source", async () => {
  266. const refId = getId();
  267. const data = {
  268. objects: {
  269. field: [{
  270. id: refId,
  271. value: "",
  272. actions: {
  273. test: [`event.source.value = "123";`]
  274. },
  275. type: "text"
  276. }]
  277. },
  278. appInfo: {
  279. language: "en-US",
  280. platform: "Linux x86_64"
  281. },
  282. calculationOrder: []
  283. };
  284. sandbox.createSandbox(data);
  285. await sandbox.dispatchEventInSandbox({
  286. id: refId,
  287. value: "",
  288. name: "test",
  289. willCommit: true
  290. });
  291. expect(send_queue.has(refId)).toEqual(true);
  292. expect(send_queue.get(refId)).toEqual({
  293. id: refId,
  294. value: "123"
  295. });
  296. });
  297. it("should trigger a Keystroke event and invalidate it", async () => {
  298. const refId = getId();
  299. const data = {
  300. objects: {
  301. field: [{
  302. id: refId,
  303. value: "",
  304. actions: {
  305. Keystroke: [`event.rc = false;`]
  306. },
  307. type: "text"
  308. }]
  309. },
  310. appInfo: {
  311. language: "en-US",
  312. platform: "Linux x86_64"
  313. },
  314. calculationOrder: []
  315. };
  316. sandbox.createSandbox(data);
  317. await sandbox.dispatchEventInSandbox({
  318. id: refId,
  319. value: "hell",
  320. name: "Keystroke",
  321. willCommit: false,
  322. change: "o",
  323. selStart: 4,
  324. selEnd: 4
  325. });
  326. expect(send_queue.has(refId)).toEqual(true);
  327. expect(send_queue.get(refId)).toEqual({
  328. id: refId,
  329. value: "hell",
  330. selRange: [4, 4]
  331. });
  332. });
  333. it("should trigger a Keystroke event and change it", async () => {
  334. const refId = getId();
  335. const data = {
  336. objects: {
  337. field: [{
  338. id: refId,
  339. value: "",
  340. actions: {
  341. Keystroke: [`event.change = "a";`]
  342. },
  343. type: "text"
  344. }]
  345. },
  346. appInfo: {
  347. language: "en-US",
  348. platform: "Linux x86_64"
  349. },
  350. calculationOrder: []
  351. };
  352. sandbox.createSandbox(data);
  353. await sandbox.dispatchEventInSandbox({
  354. id: refId,
  355. value: "hell",
  356. name: "Keystroke",
  357. willCommit: false,
  358. change: "o",
  359. selStart: 4,
  360. selEnd: 4
  361. });
  362. expect(send_queue.has(refId)).toEqual(true);
  363. expect(send_queue.get(refId)).toEqual({
  364. id: refId,
  365. value: "hella"
  366. });
  367. });
  368. it("should trigger an invalid commit Keystroke event", async () => {
  369. const refId = getId();
  370. const data = {
  371. objects: {
  372. field: [{
  373. id: refId,
  374. value: "",
  375. actions: {
  376. test: [`event.rc = false;`]
  377. },
  378. type: "text"
  379. }]
  380. },
  381. appInfo: {
  382. language: "en-US",
  383. platform: "Linux x86_64"
  384. },
  385. calculationOrder: []
  386. };
  387. sandbox.createSandbox(data);
  388. await sandbox.dispatchEventInSandbox({
  389. id: refId,
  390. value: "",
  391. name: "test",
  392. willCommit: true
  393. });
  394. expect(send_queue.has(refId)).toEqual(false);
  395. });
  396. it("should trigger a valid commit Keystroke event", async () => {
  397. const refId1 = getId();
  398. const refId2 = getId();
  399. const data = {
  400. objects: {
  401. field1: [{
  402. id: refId1,
  403. value: "",
  404. actions: {
  405. Validate: [`event.value = "world";`]
  406. },
  407. type: "text"
  408. }],
  409. field2: [{
  410. id: refId2,
  411. value: "",
  412. actions: {
  413. Calculate: [`event.value = "hello";`]
  414. },
  415. type: "text"
  416. }]
  417. },
  418. appInfo: {
  419. language: "en-US",
  420. platform: "Linux x86_64"
  421. },
  422. calculationOrder: [refId2]
  423. };
  424. sandbox.createSandbox(data);
  425. await sandbox.dispatchEventInSandbox({
  426. id: refId1,
  427. value: "hello",
  428. name: "Keystroke",
  429. willCommit: true
  430. });
  431. expect(send_queue.has(refId1)).toEqual(true);
  432. expect(send_queue.get(refId1)).toEqual({
  433. id: refId1,
  434. value: "world",
  435. valueAsString: "world"
  436. });
  437. });
  438. });
  439. describe("Color", function () {
  440. beforeAll(function () {
  441. sandbox.createSandbox({
  442. appInfo: {
  443. language: "en-US",
  444. platform: "Linux x86_64"
  445. },
  446. objects: {},
  447. calculationOrder: []
  448. });
  449. });
  450. function round(color) {
  451. return [color[0], ...color.slice(1).map(x => Math.round(x * 1000) / 1000)];
  452. }
  453. it("should convert RGB color for different color spaces", async () => {
  454. let value = await myeval(`color.convert(["RGB", 0.1, 0.2, 0.3], "T")`);
  455. expect(round(value)).toEqual(["T"]);
  456. value = await myeval(`color.convert(["RGB", 0.1, 0.2, 0.3], "G")`);
  457. expect(round(value)).toEqual(["G", 0.181]);
  458. value = await myeval(`color.convert(["RGB", 0.1, 0.2, 0.3], "RGB")`);
  459. expect(round(value)).toEqual(["RGB", 0.1, 0.2, 0.3]);
  460. value = await myeval(`color.convert(["RGB", 0.1, 0.2, 0.3], "CMYK")`);
  461. expect(round(value)).toEqual(["CMYK", 0.9, 0.8, 0.7, 0.7]);
  462. });
  463. it("should convert CMYK color for different color spaces", async () => {
  464. let value = await myeval(`color.convert(["CMYK", 0.1, 0.2, 0.3, 0.4], "T")`);
  465. expect(round(value)).toEqual(["T"]);
  466. value = await myeval(`color.convert(["CMYK", 0.1, 0.2, 0.3, 0.4], "G")`);
  467. expect(round(value)).toEqual(["G", 0.371]);
  468. value = await myeval(`color.convert(["CMYK", 0.1, 0.2, 0.3, 0.4], "RGB")`);
  469. expect(round(value)).toEqual(["RGB", 0.5, 0.3, 0.4]);
  470. value = await myeval(`color.convert(["CMYK", 0.1, 0.2, 0.3, 0.4], "CMYK")`);
  471. expect(round(value)).toEqual(["CMYK", 0.1, 0.2, 0.3, 0.4]);
  472. });
  473. it("should convert Gray color for different color spaces", async () => {
  474. let value = await myeval(`color.convert(["G", 0.1], "T")`);
  475. expect(round(value)).toEqual(["T"]);
  476. value = await myeval(`color.convert(["G", 0.1], "G")`);
  477. expect(round(value)).toEqual(["G", 0.1]);
  478. value = await myeval(`color.convert(["G", 0.1], "RGB")`);
  479. expect(round(value)).toEqual(["RGB", 0.1, 0.1, 0.1]);
  480. value = await myeval(`color.convert(["G", 0.1], "CMYK")`);
  481. expect(round(value)).toEqual(["CMYK", 0, 0, 0, 0.9]);
  482. });
  483. it("should convert Transparent color for different color spaces", async () => {
  484. let value = await myeval(`color.convert(["T"], "T")`);
  485. expect(round(value)).toEqual(["T"]);
  486. value = await myeval(`color.convert(["T"], "G")`);
  487. expect(round(value)).toEqual(["G", 0]);
  488. value = await myeval(`color.convert(["T"], "RGB")`);
  489. expect(round(value)).toEqual(["RGB", 0, 0, 0]);
  490. value = await myeval(`color.convert(["T"], "CMYK")`);
  491. expect(round(value)).toEqual(["CMYK", 0, 0, 0, 1]);
  492. });
  493. });
  494. describe("App", function () {
  495. beforeAll(function () {
  496. sandbox.createSandbox({
  497. appInfo: {
  498. language: "en-US",
  499. platform: "Linux x86_64"
  500. },
  501. objects: {},
  502. calculationOrder: []
  503. });
  504. });
  505. it("should test language", async () => {
  506. let value = await myeval(`app.language`);
  507. expect(value).toEqual("ENU");
  508. value = await myeval(`app.language = "hello"`);
  509. expect(value).toEqual("app.language is read-only");
  510. });
  511. it("should test platform", async () => {
  512. let value = await myeval(`app.platform`);
  513. expect(value).toEqual("UNIX");
  514. value = await myeval(`app.platform = "hello"`);
  515. expect(value).toEqual("app.platform is read-only");
  516. });
  517. });
  518. describe("AForm", function () {
  519. beforeAll(function () {
  520. sandbox.createSandbox({
  521. appInfo: {
  522. language: "en-US",
  523. platform: "Linux x86_64"
  524. },
  525. objects: {},
  526. calculationOrder: [],
  527. dispatchEventName: "_dispatchMe"
  528. });
  529. });
  530. describe("AFExtractNums", function () {
  531. it("should extract numbers", async () => {
  532. let value = await myeval(`AFExtractNums("123 456 789")`);
  533. expect(value).toEqual(["123", "456", "789"]);
  534. value = await myeval(`AFExtractNums("123.456")`);
  535. expect(value).toEqual(["123", "456"]);
  536. value = await myeval(`AFExtractNums("123")`);
  537. expect(value).toEqual(["123"]);
  538. value = await myeval(`AFExtractNums(".123")`);
  539. expect(value).toEqual(["0", "123"]);
  540. value = await myeval(`AFExtractNums(",123")`);
  541. expect(value).toEqual(["0", "123"]);
  542. });
  543. });
  544. describe("AFMakeNumber", function () {
  545. it("should convert string to number", async () => {
  546. let value = await myeval(`AFMakeNumber("123.456")`);
  547. expect(value).toEqual(123.456);
  548. value = await myeval(`AFMakeNumber(123.456)`);
  549. expect(value).toEqual(123.456);
  550. value = await myeval(`AFMakeNumber("-123.456")`);
  551. expect(value).toEqual(-123.456);
  552. value = await myeval(`AFMakeNumber("-123,456")`);
  553. expect(value).toEqual(-123.456);
  554. value = await myeval(`AFMakeNumber("not a number")`);
  555. expect(value).toEqual(null);
  556. });
  557. });
  558. describe("AFMakeArrayFromList", function () {
  559. it("should split a string into an array of strings", async () => {
  560. const value = await myeval(`AFMakeArrayFromList("aaaa, bbbbbbb,cc,ddd, e")`);
  561. expect(value).toEqual(["aaaa", " bbbbbbb", "cc", "ddd", "e"]);
  562. });
  563. });
  564. describe("AFNumber_format", function () {
  565. it("should format a number", async () => {
  566. const refId = getId();
  567. const data = {
  568. objects: {
  569. field: [{
  570. id: refId,
  571. value: "",
  572. actions: {
  573. test1: [`AFNumber_Format(2, 0, 0, 0, "€", false);` + `event.source.value = event.value;`],
  574. test2: [`AFNumber_Format(1, 3, 0, 0, "$", true);` + `event.source.value = event.value;`],
  575. test3: [`AFNumber_Format(2, 0, 1, 0, "€", false);` + `event.source.value = event.value;`],
  576. test4: [`AFNumber_Format(2, 0, 2, 0, "€", false);` + `event.source.value = event.value;`],
  577. test5: [`AFNumber_Format(2, 0, 3, 0, "€", false);` + `event.source.value = event.value;`]
  578. },
  579. type: "text"
  580. }]
  581. },
  582. appInfo: {
  583. language: "en-US",
  584. platform: "Linux x86_64"
  585. },
  586. calculationOrder: [],
  587. dispatchEventName: "_dispatchMe"
  588. };
  589. sandbox.createSandbox(data);
  590. await sandbox.dispatchEventInSandbox({
  591. id: refId,
  592. value: "123456.789",
  593. name: "test1"
  594. });
  595. expect(send_queue.has(refId)).toEqual(true);
  596. expect(send_queue.get(refId)).toEqual({
  597. id: refId,
  598. value: "123,456.79€"
  599. });
  600. send_queue.delete(refId);
  601. await sandbox.dispatchEventInSandbox({
  602. id: refId,
  603. value: "223456.789",
  604. name: "test2"
  605. });
  606. expect(send_queue.has(refId)).toEqual(true);
  607. expect(send_queue.get(refId)).toEqual({
  608. id: refId,
  609. value: "$223456,8"
  610. });
  611. send_queue.delete(refId);
  612. await sandbox.dispatchEventInSandbox({
  613. id: refId,
  614. value: "-323456.789",
  615. name: "test3"
  616. });
  617. expect(send_queue.has(refId)).toEqual(true);
  618. expect(send_queue.get(refId)).toEqual({
  619. id: refId,
  620. value: "323,456.79€",
  621. textColor: ["RGB", 1, 0, 0]
  622. });
  623. send_queue.delete(refId);
  624. await sandbox.dispatchEventInSandbox({
  625. id: refId,
  626. value: "-423456.789",
  627. name: "test4"
  628. });
  629. expect(send_queue.has(refId)).toEqual(true);
  630. expect(send_queue.get(refId)).toEqual({
  631. id: refId,
  632. value: "(423,456.79€)"
  633. });
  634. send_queue.delete(refId);
  635. await sandbox.dispatchEventInSandbox({
  636. id: refId,
  637. value: "-52345.678",
  638. name: "test5"
  639. });
  640. expect(send_queue.has(refId)).toEqual(true);
  641. expect(send_queue.get(refId)).toEqual({
  642. id: refId,
  643. value: "(52,345.68€)",
  644. textColor: ["RGB", 1, 0, 0]
  645. });
  646. });
  647. });
  648. describe("AFNumber_Keystroke", function () {
  649. it("should validate a number on a keystroke event", async () => {
  650. const refId = getId();
  651. const data = {
  652. objects: {
  653. field: [{
  654. id: refId,
  655. value: "",
  656. actions: {
  657. Validate: [`AFNumber_Keystroke(null, 0, null, null, null, null);`]
  658. },
  659. type: "text",
  660. name: "MyField"
  661. }]
  662. },
  663. appInfo: {
  664. language: "en-US",
  665. platform: "Linux x86_64"
  666. },
  667. calculationOrder: [],
  668. dispatchEventName: "_dispatchMe"
  669. };
  670. sandbox.createSandbox(data);
  671. await sandbox.dispatchEventInSandbox({
  672. id: refId,
  673. value: "123456.789",
  674. name: "Keystroke",
  675. willCommit: true
  676. });
  677. expect(send_queue.has(refId)).toEqual(true);
  678. expect(send_queue.get(refId)).toEqual({
  679. id: refId,
  680. value: "123456.789",
  681. valueAsString: "123456.789"
  682. });
  683. });
  684. it("should not validate a number on a keystroke event", async () => {
  685. const refId = getId();
  686. const data = {
  687. objects: {
  688. field: [{
  689. id: refId,
  690. value: "",
  691. actions: {
  692. Validate: [`AFNumber_Keystroke(null, 0, null, null, null, null);`]
  693. },
  694. type: "text",
  695. name: "MyField"
  696. }]
  697. },
  698. appInfo: {
  699. language: "en-US",
  700. platform: "Linux x86_64"
  701. },
  702. calculationOrder: [],
  703. dispatchEventName: "_dispatchMe"
  704. };
  705. sandbox.createSandbox(data);
  706. await sandbox.dispatchEventInSandbox({
  707. id: refId,
  708. value: "123s456.789",
  709. name: "Keystroke",
  710. willCommit: true
  711. });
  712. expect(send_queue.has("alert")).toEqual(true);
  713. expect(send_queue.get("alert")).toEqual({
  714. command: "alert",
  715. value: "The value entered does not match the format of the field [ MyField ]"
  716. });
  717. });
  718. });
  719. describe("AFPercent_Format", function () {
  720. it("should format a percentage", async () => {
  721. const refId = getId();
  722. const data = {
  723. objects: {
  724. field: [{
  725. id: refId,
  726. value: "",
  727. actions: {
  728. test1: [`AFPercent_Format(2, 1, false);` + `event.source.value = event.value;`],
  729. test2: [`AFPercent_Format(2, 1, true);` + `event.source.value = event.value;`]
  730. },
  731. type: "text"
  732. }]
  733. },
  734. appInfo: {
  735. language: "en-US",
  736. platform: "Linux x86_64"
  737. },
  738. calculationOrder: [],
  739. dispatchEventName: "_dispatchMe"
  740. };
  741. sandbox.createSandbox(data);
  742. await sandbox.dispatchEventInSandbox({
  743. id: refId,
  744. value: "0.456789",
  745. name: "test1"
  746. });
  747. expect(send_queue.has(refId)).toEqual(true);
  748. expect(send_queue.get(refId)).toEqual({
  749. id: refId,
  750. value: "45.68%"
  751. });
  752. send_queue.delete(refId);
  753. await sandbox.dispatchEventInSandbox({
  754. id: refId,
  755. value: "0.456789",
  756. name: "test2"
  757. });
  758. expect(send_queue.has(refId)).toEqual(true);
  759. expect(send_queue.get(refId)).toEqual({
  760. id: refId,
  761. value: "%45.68"
  762. });
  763. });
  764. });
  765. describe("AFDate_Format", function () {
  766. it("should format a date", async () => {
  767. const refId = getId();
  768. const data = {
  769. objects: {
  770. field: [{
  771. id: refId,
  772. value: "",
  773. actions: {
  774. test1: [`AFDate_Format(0);event.source.value = event.value;`],
  775. test2: [`AFDate_Format(12);event.source.value = event.value;`]
  776. },
  777. type: "text"
  778. }]
  779. },
  780. appInfo: {
  781. language: "en-US",
  782. platform: "Linux x86_64"
  783. },
  784. calculationOrder: [],
  785. dispatchEventName: "_dispatchMe"
  786. };
  787. sandbox.createSandbox(data);
  788. await sandbox.dispatchEventInSandbox({
  789. id: refId,
  790. value: "Sun Apr 15 2007 03:14:15",
  791. name: "test1"
  792. });
  793. expect(send_queue.has(refId)).toEqual(true);
  794. expect(send_queue.get(refId)).toEqual({
  795. id: refId,
  796. value: "4/15"
  797. });
  798. send_queue.delete(refId);
  799. await sandbox.dispatchEventInSandbox({
  800. id: refId,
  801. value: "Sun Apr 15 2007 03:14:15",
  802. name: "test2"
  803. });
  804. expect(send_queue.has(refId)).toEqual(true);
  805. expect(send_queue.get(refId)).toEqual({
  806. id: refId,
  807. value: "4/15/07 3:14 am"
  808. });
  809. });
  810. });
  811. describe("AFRange_Validate", function () {
  812. it("should validate a number in range [a, b]", async () => {
  813. const refId = getId();
  814. const data = {
  815. objects: {
  816. field: [{
  817. id: refId,
  818. value: "",
  819. actions: {
  820. Validate: [`AFRange_Validate(true, 123, true, 456);`]
  821. },
  822. type: "text"
  823. }]
  824. },
  825. appInfo: {
  826. language: "en-US",
  827. platform: "Linux x86_64"
  828. },
  829. calculationOrder: [],
  830. dispatchEventName: "_dispatchMe"
  831. };
  832. sandbox.createSandbox(data);
  833. await sandbox.dispatchEventInSandbox({
  834. id: refId,
  835. value: "321",
  836. name: "Keystroke",
  837. willCommit: true
  838. });
  839. expect(send_queue.has(refId)).toEqual(true);
  840. expect(send_queue.get(refId)).toEqual({
  841. id: refId,
  842. value: "321",
  843. valueAsString: "321"
  844. });
  845. });
  846. it("should invalidate a number out of range [a, b]", async () => {
  847. const refId = getId();
  848. const data = {
  849. objects: {
  850. field: [{
  851. id: refId,
  852. value: "",
  853. actions: {
  854. Validate: [`AFRange_Validate(true, 123, true, 456);`]
  855. },
  856. type: "text"
  857. }]
  858. },
  859. appInfo: {
  860. language: "en-US",
  861. platform: "Linux x86_64"
  862. },
  863. calculationOrder: [],
  864. dispatchEventName: "_dispatchMe"
  865. };
  866. sandbox.createSandbox(data);
  867. await sandbox.dispatchEventInSandbox({
  868. id: refId,
  869. value: "12",
  870. name: "Keystroke",
  871. willCommit: true
  872. });
  873. expect(send_queue.has("alert")).toEqual(true);
  874. expect(send_queue.get("alert")).toEqual({
  875. command: "alert",
  876. value: "Invalid value: must be greater than or equal to 123 and less than or equal to 456."
  877. });
  878. });
  879. });
  880. describe("ASSimple_Calculate", function () {
  881. it("should compute the sum of several fields", async () => {
  882. const refIds = [0, 1, 2, 3].map(_ => getId());
  883. const data = {
  884. objects: {
  885. field1: [{
  886. id: refIds[0],
  887. value: "",
  888. actions: {},
  889. type: "text"
  890. }],
  891. field2: [{
  892. id: refIds[1],
  893. value: "",
  894. actions: {},
  895. type: "text"
  896. }],
  897. field3: [{
  898. id: refIds[2],
  899. value: "",
  900. actions: {},
  901. type: "text"
  902. }],
  903. field4: [{
  904. id: refIds[3],
  905. value: "",
  906. actions: {
  907. Calculate: [`AFSimple_Calculate("SUM", ["field1", "field2", "field3"]);`]
  908. },
  909. type: "text"
  910. }]
  911. },
  912. appInfo: {
  913. language: "en-US",
  914. platform: "Linux x86_64"
  915. },
  916. calculationOrder: [refIds[3]],
  917. dispatchEventName: "_dispatchMe"
  918. };
  919. sandbox.createSandbox(data);
  920. await sandbox.dispatchEventInSandbox({
  921. id: refIds[0],
  922. value: "1",
  923. name: "Keystroke",
  924. willCommit: true
  925. });
  926. expect(send_queue.has(refIds[3])).toEqual(true);
  927. expect(send_queue.get(refIds[3])).toEqual({
  928. id: refIds[3],
  929. value: 1,
  930. valueAsString: "1"
  931. });
  932. await sandbox.dispatchEventInSandbox({
  933. id: refIds[1],
  934. value: "2",
  935. name: "Keystroke",
  936. willCommit: true
  937. });
  938. expect(send_queue.has(refIds[3])).toEqual(true);
  939. expect(send_queue.get(refIds[3])).toEqual({
  940. id: refIds[3],
  941. value: 3,
  942. valueAsString: "3"
  943. });
  944. await sandbox.dispatchEventInSandbox({
  945. id: refIds[2],
  946. value: "3",
  947. name: "Keystroke",
  948. willCommit: true
  949. });
  950. expect(send_queue.has(refIds[3])).toEqual(true);
  951. expect(send_queue.get(refIds[3])).toEqual({
  952. id: refIds[3],
  953. value: 6,
  954. valueAsString: "6"
  955. });
  956. });
  957. });
  958. describe("AFSpecial_KeystrokeEx", function () {
  959. it("should validate a phone number on a keystroke event", async () => {
  960. const refId = getId();
  961. const data = {
  962. objects: {
  963. field: [{
  964. id: refId,
  965. value: "",
  966. actions: {
  967. Keystroke: [`AFSpecial_KeystrokeEx("9AXO");`]
  968. },
  969. type: "text"
  970. }]
  971. },
  972. appInfo: {
  973. language: "en-US",
  974. platform: "Linux x86_64"
  975. },
  976. calculationOrder: [],
  977. dispatchEventName: "_dispatchMe"
  978. };
  979. sandbox.createSandbox(data);
  980. await sandbox.dispatchEventInSandbox({
  981. id: refId,
  982. value: "",
  983. change: "3",
  984. name: "Keystroke",
  985. willCommit: false,
  986. selStart: 0,
  987. selEnd: 0
  988. });
  989. expect(send_queue.has(refId)).toEqual(false);
  990. await sandbox.dispatchEventInSandbox({
  991. id: refId,
  992. value: "3",
  993. change: "F",
  994. name: "Keystroke",
  995. willCommit: false,
  996. selStart: 1,
  997. selEnd: 1
  998. });
  999. expect(send_queue.has(refId)).toEqual(false);
  1000. await sandbox.dispatchEventInSandbox({
  1001. id: refId,
  1002. value: "3F",
  1003. change: "?",
  1004. name: "Keystroke",
  1005. willCommit: false,
  1006. selStart: 2,
  1007. selEnd: 2
  1008. });
  1009. expect(send_queue.has(refId)).toEqual(false);
  1010. await sandbox.dispatchEventInSandbox({
  1011. id: refId,
  1012. value: "3F?",
  1013. change: "@",
  1014. name: "Keystroke",
  1015. willCommit: false,
  1016. selStart: 3,
  1017. selEnd: 3
  1018. });
  1019. expect(send_queue.has(refId)).toEqual(true);
  1020. expect(send_queue.get(refId)).toEqual({
  1021. id: refId,
  1022. value: "3F?",
  1023. selRange: [3, 3]
  1024. });
  1025. send_queue.delete(refId);
  1026. await sandbox.dispatchEventInSandbox({
  1027. id: refId,
  1028. value: "3F?",
  1029. change: "0",
  1030. name: "Keystroke",
  1031. willCommit: false,
  1032. selStart: 3,
  1033. selEnd: 3
  1034. });
  1035. expect(send_queue.has(refId)).toEqual(false);
  1036. await sandbox.dispatchEventInSandbox({
  1037. id: refId,
  1038. value: "3F?0",
  1039. name: "Keystroke",
  1040. willCommit: true,
  1041. selStart: 4,
  1042. selEnd: 4
  1043. });
  1044. expect(send_queue.has(refId)).toEqual(true);
  1045. expect(send_queue.get(refId)).toEqual({
  1046. id: refId,
  1047. valueAsString: "3F?0"
  1048. });
  1049. });
  1050. });
  1051. describe("AFSpecial_Keystroke", function () {
  1052. it("should validate a zip code on a keystroke event", async () => {
  1053. const refId = getId();
  1054. const data = {
  1055. objects: {
  1056. field: [{
  1057. id: refId,
  1058. value: "",
  1059. actions: {
  1060. Keystroke: [`AFSpecial_Keystroke(0);`]
  1061. },
  1062. type: "text"
  1063. }]
  1064. },
  1065. appInfo: {
  1066. language: "en-US",
  1067. platform: "Linux x86_64"
  1068. },
  1069. calculationOrder: [],
  1070. dispatchEventName: "_dispatchMe"
  1071. };
  1072. sandbox.createSandbox(data);
  1073. let value = "";
  1074. const changes = "12345";
  1075. let i = 0;
  1076. for (; i < changes.length; i++) {
  1077. const change = changes.charAt(i);
  1078. await sandbox.dispatchEventInSandbox({
  1079. id: refId,
  1080. value,
  1081. change,
  1082. name: "Keystroke",
  1083. willCommit: false,
  1084. selStart: i,
  1085. selEnd: i
  1086. });
  1087. expect(send_queue.has(refId)).toEqual(false);
  1088. value += change;
  1089. }
  1090. await sandbox.dispatchEventInSandbox({
  1091. id: refId,
  1092. value,
  1093. change: "A",
  1094. name: "Keystroke",
  1095. willCommit: false,
  1096. selStart: i,
  1097. selEnd: i
  1098. });
  1099. expect(send_queue.has(refId)).toEqual(true);
  1100. expect(send_queue.get(refId)).toEqual({
  1101. id: refId,
  1102. value,
  1103. selRange: [i, i]
  1104. });
  1105. send_queue.delete(refId);
  1106. });
  1107. it("should validate a US phone number (long) on a keystroke event", async () => {
  1108. const refId = getId();
  1109. const data = {
  1110. objects: {
  1111. field: [{
  1112. id: refId,
  1113. value: "",
  1114. actions: {
  1115. Keystroke: [`AFSpecial_Keystroke(2);`]
  1116. },
  1117. type: "text"
  1118. }]
  1119. },
  1120. appInfo: {
  1121. language: "en-US",
  1122. platform: "Linux x86_64"
  1123. },
  1124. calculationOrder: [],
  1125. dispatchEventName: "_dispatchMe"
  1126. };
  1127. sandbox.createSandbox(data);
  1128. let value = "";
  1129. const changes = "(123) 456-7890";
  1130. let i = 0;
  1131. for (; i < changes.length; i++) {
  1132. const change = changes.charAt(i);
  1133. await sandbox.dispatchEventInSandbox({
  1134. id: refId,
  1135. value,
  1136. change,
  1137. name: "Keystroke",
  1138. willCommit: false,
  1139. selStart: i,
  1140. selEnd: i
  1141. });
  1142. expect(send_queue.has(refId)).toEqual(false);
  1143. value += change;
  1144. }
  1145. await sandbox.dispatchEventInSandbox({
  1146. id: refId,
  1147. value,
  1148. change: "A",
  1149. name: "Keystroke",
  1150. willCommit: false,
  1151. selStart: i,
  1152. selEnd: i
  1153. });
  1154. expect(send_queue.has(refId)).toEqual(true);
  1155. expect(send_queue.get(refId)).toEqual({
  1156. id: refId,
  1157. value,
  1158. selRange: [i, i]
  1159. });
  1160. send_queue.delete(refId);
  1161. });
  1162. it("should validate a US phone number (short) on a keystroke event", async () => {
  1163. const refId = getId();
  1164. const data = {
  1165. objects: {
  1166. field: [{
  1167. id: refId,
  1168. value: "",
  1169. actions: {
  1170. Keystroke: [`AFSpecial_Keystroke(2);`]
  1171. },
  1172. type: "text"
  1173. }]
  1174. },
  1175. appInfo: {
  1176. language: "en-US",
  1177. platform: "Linux x86_64"
  1178. },
  1179. calculationOrder: [],
  1180. dispatchEventName: "_dispatchMe"
  1181. };
  1182. sandbox.createSandbox(data);
  1183. let value = "";
  1184. const changes = "123-4567";
  1185. let i = 0;
  1186. for (; i < changes.length; i++) {
  1187. const change = changes.charAt(i);
  1188. await sandbox.dispatchEventInSandbox({
  1189. id: refId,
  1190. value,
  1191. change,
  1192. name: "Keystroke",
  1193. willCommit: false,
  1194. selStart: i,
  1195. selEnd: i
  1196. });
  1197. expect(send_queue.has(refId)).toEqual(false);
  1198. value += change;
  1199. }
  1200. await sandbox.dispatchEventInSandbox({
  1201. id: refId,
  1202. value,
  1203. change: "A",
  1204. name: "Keystroke",
  1205. willCommit: false,
  1206. selStart: i,
  1207. selEnd: i
  1208. });
  1209. expect(send_queue.has(refId)).toEqual(true);
  1210. expect(send_queue.get(refId)).toEqual({
  1211. id: refId,
  1212. value,
  1213. selRange: [i, i]
  1214. });
  1215. send_queue.delete(refId);
  1216. });
  1217. });
  1218. describe("eMailValidate", function () {
  1219. it("should validate an e-mail address", async () => {
  1220. let value = await myeval(`eMailValidate(123)`);
  1221. expect(value).toEqual(false);
  1222. value = await myeval(`eMailValidate("foo@bar.com")`);
  1223. expect(value).toEqual(true);
  1224. value = await myeval(`eMailValidate("foo bar")`);
  1225. expect(value).toEqual(false);
  1226. });
  1227. });
  1228. });
  1229. });