Search This Blog

Thursday, 30 April 2015

Using the Jasmine testing framework in an Adobe Designer LiveCycle Form

Jasmine is a popular testing framework for JavaScript applications.  There's lots of tutorials on using Jasmine so I wont go into a lot of detail but as an example if we had a form that has a item price field, a quantity field and a total field with Jasmine we can write a test something like;

 describe("Total Calculation: ", function()
 {
  beforeEach(function ()
  {
   TotalPriceCalculation.ItemPrice.rawValue = 1.75;
   TotalPriceCalculation.Quantity = 5000;
   TotalPriceCalculation.Total.execCalculate();
  });

  it("Equals ItemPrice * Quantity", function()
  {
   expect(TotalPriceCalculation.Total.rawValue).toEqual(8750);
  });
  it("Formatted in Australian Dollars ", function()
  {
   expect(TotalPriceCalculation.Total.formattedValue).toEqual("$8,750.00");
  });
 });

This is JavaScript code, describe, is a Jasmine function that takes a description and function as arguments. beforeEach is another function that is executed before every it  function which performs the actual test.  The above test will hopefully give us a result like;


The toEqual function after the expect is one of the standard Jasmine matches, the complete list is;

toBe( null | true | false )
toEqual( value )
toMatch( regex | string )
toBeDefined()
toBeUndefined()
toBeNull()
toBeTruthy()
toBeFalsy()
toContain( string )
toBeLessThan( number )
toBeGreaterThan( number )
toBeNaN()
toBeCloseTo( number, precision )


And the Jasmine framework allows us to add our own matches, so for the XFA environment I have added;

toBeVisible()
toBeInvisible()
toBeHidden()
toBeMandatory()
toBeProtected()
toBeOpen()
toBeValid()
toBeXFANull()
toHaveItemCount( number )
toHaveError( string )
toHaveFontColor ( "r,g,b" )


These are just helper functions target.toBeVisible() is the same as target.presence.toEqual("visible").

Other helper functions added for the XFA environment are

 /**
  * Raises the change event for a specified field
  * @param {XFA object}  field     The target field of the change event
  * @param {string}   newText    The value of the field after the change is applied
  * @param {string}   [change=newText] The value typed or pasted into the field that raises the change event
  * @param {boolean}  [keyDown=false]  The user is using an arrow key (listbox and dropdown only)
  * @param {boolean}  [modifier=false] The user is holding the Ctrl key down
  * @param {boolean}  [shift=false]  The user is holding the shift key down
  */

function execChangeEvent(field, newText, change, keyDown, modifier, shift) {


 /**
  * Raises the exit event for a specified field
  * @param {XFA object}  field     The target field of the exit event
  * @param {various}  rawValue    The value of the field before the exit event called, undefined doesn't update the field
  * @param {string}   [commitKey=3]  How the current value of the field was set: 0 - not set, escape key pressed, 1 - mouse click, 2 - enter key, 3 - tabbing
  * @param {boolean}  [modifier=false] The user is holding the Ctrl key down
  * @param {boolean}  [shift=false]  The user is holding the shift key down
  */

function execExitEvent(field, rawValue, commitKey, modifier, shift) {


 /**
  * Reset fields to their default values
  */

function resetData(/*arguments*/) {


In this sample Jasmine.xdp is a script object which is the standalone Jasmine code download with a fairly small modification to cater for the differences in the Adobe JavaScript environment.  The changes are commented at the start of the code but relate to the app.setTimeOut() function taking a string instead of a JavaScript statement.

Hopefully this sample will give you enough to get you going JasmineSample.pdf.  All the tests in this sample pass but when you get some that fail an exception will be thrown, you may want to set "When exception is thrown" to ignore in Acrobat JavaScript options.

The XFA bootstrap is in JasmineXFA.xdp and the Jasmine test are in JasmineSpec.xdp, all zipped in JasmineXDP.zip with the XDP of the sample.

The JasmineSpec.xdp is a form fragment, the tests are run as part of a click event of the "Run Jasmine Specs" button, the results are displayed in a text object in the same fragment.  The click event must start with the statement;

var jasmine = JasmineXFA.Bootstrap({environment:this})

this, adds the Jasmine functions and the XFA helpers and ends with the statement;

jasmine.getEnv().execute(); 

Which starts the actual tests.

The sample by default generates a pass/fail log to the console log as well. If you find generating output to the console log is getting in the way of your own debug statements you can turn the Jasmine output off by passing the includeConsoleReporter:false option.

var jasmine = JasmineXFA.Bootstrap({environment:this, includeConsoleReporter:false})

I tend to develop the tests as an embedded script object which allows me to use the control-click method of referencing fields, then when ready to be released change the script to a fragment and comment it out.  The XDP file being an XML file allows XML comments <!-- -->.  So in the XML Source view will look like;

<!--subform usehref=".\JasmineSpec.xdp#som($template.#subform.JasmineSpec)"></subform-->

I am using the Jasmine framework version 2.0, the latest is 2.2 but I have not been successful in getting later version to run in the Acrobat environment, again because of the differences in the app.setTimeOut() function.  I'm not sure what I am missing but 2.0 has provided enough functionality for me so far.




Thursday, 23 April 2015

Adobe Dialog Manager (ADM) in Acrobat JavaScript: insertEntryInList, removeAllEntriesFromList and insertSeparatorEntryInList

This sample demonstrates the undocumented list methods available when using Adobe Dialog Manager (ADM) in Acrobat JavaScript, also called a custom dialog using the app.execDialog()
method.

These methods, on the JavaScript API > Dialog object, are;

insertEntryInList(object)

removeAllEntriesFromList(item_id)

insertSeparatorEntryInList(item_id)

As they are undocumented they could disappear in future releases of Adobe Reader but they are also used by Reader itself,  if you open a JavaScript debugger console and type IWDistributionServer you will see the source code of one of the JavaScript functions that use it.

This sample implements a ListPicker control, ListPicker.pdf


Once the selections are made the you can move a separator line (the blue line in the selection list) up and down.

The separator line can be added to a list with the load method by using the property name "\-" but seems to always appears at the beginning, so the code which you would expect to add a separator as the second item.

dialog.load({ "LST1" : { "abc" : -1, "\\-" : -2, "def" : -3 } })

Adds it as the first.










To achieve the effect we wanted we would need to use;

dialog.insertEntryInList({ "LST1" : { "abc" : -1 } })
dialog.insertSeparatorEntryInList("LST1");    
dialog.insertEntryInList({ "LST1" : { "def" : -3 } })

Thursday, 16 April 2015

Adobe Dialog Manager (ADM) in Acrobat JavaScript: progress_bar

This sample demonstrates the undocumented progress_bar control when using Adobe Dialog Manager (ADM) in Acrobat JavaScript, also called a custom dialog using the app.execDialog()
method.


The control can be defined with the following code;
 


{

  type: "progress_bar",

  width: 100,

  height: 22,

  item_id: "PBAR"

}



 Updates to the position of the progress bar can be made with 
 
          

dialog.load({"PBAR": (100 / this.fieldCount) * validFieldCount});



 This code sets the progress bar to a value between 0 and 100 depending on how many valid fields there are.



I started playing with the progress bar control when attempting to develop a timed dialog using  app.setInterval()  but the timer seems to be paused when a dialog is display (as it is if a menu is opened)
 


Still there are some other uses and this sample uses a progress bar to show how much of the dialog is complete and another to indicate the password strength.


Progress_Bar.pdf 
 


Thursday, 9 April 2015

Adobe LiveCycle Designer Tip #9 - The JavaScript console and string literals

One of the little mysteries with working Acrobat JavaScript is how the JavaScript console handles string literals.

If you open Acrobat and then the JavaScript console, (using Ctrl-J) and type

'abc'.length  .. we get a response of 3

and

“abc”.length  .. also gives a response of 3

But if we are using the JavaScript console when performing a PDF Preview from LiveCycle Designer then

'abc'.length  .. still gives us 3

but

“abc”.length  .. returns undefined

Even worse

'a"b"c'.length .. also returns undefined, (that is a double quote within single quotes)

So it seems we can’t use double quotes at all, but we can you just need to escape them (even though JavaScript should not require us to), so

'a\"b\"c'.length .. returns 5

This doesn’t cause problems daily but every now and then … maybe if I’m having trouble with a SOM expression involving a predicate, say something like

Content.dataNode.resolveNode('$.Location.[Level == "Lead"]')

Thursday, 2 April 2015

Windows Search and XDP files

By default the Windows Search will only pick up the file properties of XDP files.  But since XDP files are really just XML files we can set the search filter handler to the XML Filter and then our search will look in the element and attribute values of the XDP file.

To do this we need to edit the Registry.  If we look at the registry key for the .xml file type (which has a key of HKEY_CLASSES_ROOT\.xml) we see


To make Windows Search treat XDP files like XML all we need to do copy the PersistentHandler key with a default value of the GUID for the XML Filter which is {7E9D8D44-6926-426F-AA2B-217A819A5CCE} to the XDP class key HKEY_CLASSES_ROOT\.xdp.

Once we have done that, if you go to Index Options, select Advanced ... File Types and scroll down to XDP you will see the XML Filter is being used.


Make sure you have the "Index Properties and File Contents" selected, then go back to Index Settings tab and click the Rebuild button ... wait several hours and your searches will now include the contents of the XDP files.