So, would you rather work with TestComplete script running wild like this example?
What if we edit the code in a TypeScript supported IDE? The IDE is on a first-name basis with TypeScript, but TestComplete’s Log is a stranger in these parts.
TypeScript allows us to define the “shape” of objects from external libraries, including TestComplete runtime objects. By “shape”, I mean the name, members and types of the objects in an external library. Take a look at the declaration of the Log object below:
The declaration for Log informs the TypeScript IDE and compiler to allow TestComplete’s Log object. Message defines that the first parameter “message” is a required string and the remaining optional parameters will accept any type. The TypeScript IDE is muy happy with the change.
Nothing prevents me from using generic JSON instead of the object returned by Log.CreateNewAttributes(). TestComplete logs the message without complaint, but the behavior is incorrect — the logged message doesn’t show in red.
By ratcheting down the TypeScript definition, we can build in increasingly stronger type safety. The LogAttributes declaration defines its properties:
In the screenshot of the TypeScript IDE, the second example flags the attributes object as not being compatible with the Log.Message() method, warning us that we’re not supplying the correct parameter type.
Note: There are limits to what TypeScript can help us with. For example, TypeScript doesn’t know anything about TestComplete’s runtime objects. TypeScript doesallow type compatibility through structural subtyping. If all the members in the second example’s attributes object were defined, i.e. “BackColor”, “Bold” and so on, TypeScript would allow it. But the TestComplete Log runtime object will still not perform correctly because it doesn’t have the object returned from CreateNewAttributes, it just has an object with the same properties. Even with these limitations, TypeScript does get us closer to our goals of type safety and easier to maintain code.
This brings us back around to wrapping TestComplete objects to perform useful actions. The Browser TypeScript class below wraps several disparate pieces. Normally, I have to remember which object runs and navigates the browser, how to get the page after navigating and how to get the browser window so I can move or close the window.
TypeScript uses the extends keyword to add properties and methods to an existing class. I can inherit from the Browser object and create a new SelfieBrowser class that knows how to take a screenshot of the current page: