DemoMonkey allows you to turn your software demo in a fully tailored demo for your prospect in minutes: You simply provide text & visual replacements for your application's UI and DemoMonkey turns your generic demo into a personalized experience for your audience.
Curious how this works? Read on to learn how you can use DemoMonkey, to tamper your web application to demo almost anything.
Running into an issue? Having a suggestion to make DemoMonkey better? Raise an issue.
- Configurations
- Options
- Variables
- Imports: Reuse existing configurations
- Snippets
- Generic Commands
- Run regular expressions
- Replace with filters
- Replace Attribute
- Search and Hash
- Protect text from replacements
- Hide Elements
- Replace Images
- Recolor images
- Block, Delay, Redirect URLs
- Conditional Replacements
- Modify CSS
- Overwrite HTML
- Replace based on CSS selector
- Add Javascript
- Replace Neighbor
- Insert before and after a DOM element
- Replace and patch response of AJAX requests
- Demo Stages
- Commands in Namespaces
- Learn More
DemoMonkey is driven by configuration files you write in the integrated editor. You provide lists of search and replace patterns, that will be applied once DemoMonkey is running. The structure of the file is similar to an ini file:
search = replaceLike ini files the format provides sections and comments to structure your document:
; Replace all the cities and countries in our webapp
[Cities]
San Francisco = Berlin
Paris = Rome
[Countries]
USA = Germany
France = ItalyAdditionally you can use options, commands, imports and variables to achieve even more:
; The configuration only works if the url matches the following regex:
@include[] = /^https?://.*demo.*\.myapp\.com(:[0-9]+)?/.*$/
; You can use $customer in replacements as variable
$customer = My Customer//Set The customer className
; Import another configuration called "Cities"
+Cities
; Replace Order with Flight case insensitive
!/Order/i = FlightNote, that include or exclude is an option that is required for every configuration you create.
Below you will find a list of available options and commands.
Options allow you to change the behavior of DemoMonkey. Since you might have multiple options of the same name it's good practice to write them in array notation:
@include = ... ; not an array, can be overwritten
@include[] = ... ; an array, will be appendedAs mentioned before you have to provide either an @include or @exclude for your configuration to work. This protects you from having a monkey on every website:
; The configuration only works on sites that include demomonkey
@include[] = /demomonkey/
; The configuration does not work on demomonkey.net
@exclude[] = /demomonkey.net/If your configuration is a template or snippet (see below), you can disable the warning that an @include or @exclude is required:
@template
; the following configuration is not used standalone but reused from within others
...
...Some commands (see below) are grouped into a namespace. You can import a namespace, so you can use it's commands outside the namespace:
@namespace[] = appdynamics
; without the line above you would have to write !appdynamics.hideApplication(test) below
!hideApplication(test)By default DemoMonkey is applied on all tags within the document except script and style. You can change this by
providing allow- and blocklists:
@blocklist[] = input
@blocklist[] = textarea
@allowlist[] = scriptIn some special cases, you need to replace text elements, that are not captured by the default mechanisms of demo monkey. One example is the placeholder of an input field. You can define textAttributes, that will be replaced like any other visible text on the page:
@textAttributes[] = placeholder,data-labelIf a configuration is outdated and should not appear in the list of available configurations in the popup you can mark it as deprecated:
@deprecatedYou can provide tags in your configuration, to improve DemoMonkey's search:
@tags[] = my,super,demoYou can use options to provide further information about your configuration, like adding yourself as the author:
@author[] = Demo Monkey <demomonkey@demomonkey.net>To improve the reusability of your demo configurations you can add variables:
$customer=Example Inc
$prospect=severin.neumann
$domain=example.com
; Use curly braces when using the variables to avoid confusion where the variable name ends.
api.demoapp.com = api.${domain}
john.doe@demoapp=${prospect}@${domain}You can quickly update the values of the variables before meeting with another prospect.
There is also a Variables tab in your demo configuration, where you can update the value. Here you also have a full
text editor, so if you need to provide some more complex values, a good approach is defining a variable and setting the
value in the background.
If you go to Settings > Global Variables you can provide variables that are useable in all your configurations.
Here you also can provide colors via a picker and images via upload which will be base64 encoded for you.
Sometimes you don't care about the value of the replacement itself or you want to provide some randomness in the
replacement, e.g.when providing a name of a person. In this case you can use the special variables of the form
${random.<function>(<attributes>)}, which provide you access to the methods provided by Chance:
John Doe = ${random.name()}
Berlin = ${random.city()}
Your error code is 404 = Your error code is ${random.integer({min: 400, max: 599})}Like variables imports make your configurations reusable. Introduced by a plus sign (+) you can load one configuration
into another one. For example, if you have a list of Cities defined in a configuration called CitiesTemplate,
you can simply import it:
+CitiesTemplateAs a good practice have a list of reusable templates like CitiesTemplate and mark them as such with the @template
option, so they will not appear in the list of configurations in the popup:
@template
San Francisco = Berlin
Honolulu = Oslo
Seattle = Amsterdam
Cupertino = Paris
New York = Moscow
Bangalore = Sydney
London = Cairo
New England = Beijing
Cleveland = Tokyo
Palo Alto = Vienna
Redwood City = Sao Paulo
Mountain View = DubaiNote, that also variables are imported. So if you have a variable $customersHeadquarter in your CitiesTemplate
configuration, you can overwrite this value in the importing configuration.
By default, you have an optional feature enabled that is called Autocomplete. This also allows you to use snippets.
By typing % you get a list of all your configurations. Select or type the name of one of them and DemoMonkey will copy
the content of the other configuration into the one in front of you.
A @template marker in your configuration will be removed when you load the snippet into a configuration.
Also, you can introduce snippet variables and you can use <tab> to jump through them:
@template
ECommerce = ${1}
Inventory = ${2:default}Do not make use of variables in your snippet templates since the underlying editor will assume that those are snippet variables and remove them before copying the content.
Beyond simple text replacements you can use commands to achieve more complex things, like using regular expressions, replace images or URLs.
Out of the box all patterns you define are simple word-by-word replacements. If you want to apply regular expressions, you can introduce them via a command:
!/e?-?commerce/i = Business IntelligenceThere is a special modifier p that will try to preserve the letter case of your replacement. For example,
if you want to replace ORDER and Order with Transfer and TRANSFER you can use the following:
!/Order/pi = TransferYou can call the simple search and replace command explicitly using !replace. You can then provide additional
parameters, to filter by location and by CSS and you can specific the DOM attribute of the parent node which will be
replaced instead of the text:
!replace(DemoMonkey, /usage.html, h1 > a.external-link, title) = TestMonkeyThe !replaceAttribute command is just an alias for !replace(search, locationFilter, cssFilter, attribute) with
attribute as the second parameter. This is useful if you don't need a location or css filter.
For example, if you have a link <a href="http://www.demomonkey.net">DemoMonkey</a> and you want to replace the href
attribute you can do the following:
!replaceAttribute(http://www.demomonkey.net, href) = https://www.example.comThe !replace function also provides the capability to use another function instead of a string replacement, i.e. hashing.
You can either use !replace(search, locationFilter, cssFilter, attribute, replaceFn) = replace or one of the following
aliases:
!replaceFunction(DemoMonkey, hash) = SHA256
!hash(DemoMonkey) = SHA256Sometimes you want to make sure that certain text is protected from replacements, e.g. links in the navigation or text
on a button. You can use !protect to make sure that those are not replaced:
!protect(DemoMonkey)
; the following replacement will not be applied on every instance of "DemoMonkey"
Demo = TestNote, that the order of patterns in a configuration is important. So it is best practice to put all instances of
!protect at the top of your demo configuration.
If you want to hide elements in your web applications UI you can use the !hide command to not only make the text
invisible but also a given number of parent elements in the DOM.
For example, if you have a row in a table (<tr class="row-price"><td>Price</td><td>500 Euro</td></tr>) you can use
the following to hide the whole row:
!hide(Price, 2, row-price, /cartpage.html)The src attribute of an image can be changed with the !replaceImage command:
!replaceImage(http://your.app/path/to/your/logo.png) = http://your.other.server/image.pngIf the image you want to provide is given as data-url, you can use *
as wildcards in the search pattern and only
provide a part of the base64 string:
!replaceImage(*RFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJ*) = http://your.other.server/image.pngAnother option is using global variables. Go to Settings > Global Variables and upload an image and give it a name:
!replaceImage("${imageVariable}") = http://your.other.server/image.pngTo put a layer with a specific color above an image you can use !recolorImage:
!recolorImage(http://your.app/path/to/your/logo.png) = rgb(255,0,0)For the replacement you can either use RGB, a color hex code (without #) or a
CSS color like midnightblue
You can hook into web requests with DemoMonkey, so that you can either replace the target url, block the request or
introduce some delay before the request is completed.
Since this set of commands requires higher privileges from your browser, you need to enable DemoMonkey explicitly to
hook into your web requests: Go to Settings > Optional Features and turn on the Hook into Web Requests.
With this enabled, you can use the following commands:
!blockUrl(http://www.demomonkey.net/)
!delayUrl(http://api.demomonkey.net/v1/*) = 5
!redirectUrl(https://www.example.com/checkout) = https://www.example.com/errorNote, that to make those commands work you also need to have an @include option to match and no @exclude option
to block them:
@include[] = /demomonkey/
@exclude[] = /api/
; the following will be executed
!blockUrl(http://www.demomonkey.net/)
; the following will not be executed
!blockUrl(http://api.demomonkey.net/)If you want to hook only into certain types of requests, you can provide a type as second parameter:
!blockUrl(http://api.demomonkey.net/, xmlhttprequest)The available types are:
*main_framesub_framestylesheetscriptimagefontobjectxmlhttprequestpingcsp_reportmediawebsocketother
Some but not all commands of demo monkey allow you to restrict application of the command based on CSS selectors or URL
location. The if command is a generic
approach to this:
!if(home.html, h1.title, DemoMonkey) = ExampleThis will replace DemoMonkey with Example only if the URL contains home.html and if the css of the parent node
matches the given selector h1.title.
If you only want to replace by location or by css selector you can skip one of the parameters or use shorthand commands:
!if(home.html,,!replaceImage(*/logo.png)) = https://my-server/other-logo.png
!ifLocation(home.html,!replaceImage(*/logo.png)) = https://my-server/other-logo.png
!if(,h1.title,!replace(DemoMonkey)) = Example
!ifSelector(h1.title,!replace(DemoMonkey)) = ExampleIf you'd like to change a style attribute of an element you can use the !style command.
; change the font color of every occurrence of DemoMonkey to red.
!style(DemoMonkey, color) = red!overwriteHTML allows you to select an element and completely change it's inner HTML:
$html=<head>503 - Internal Server Error</head><body>503 - Internal Server Error</body>
!overwriteHTML('checkout.html', '') = $htmlThe second parameter allows you to provide a CSS selector and only replace the selected part of the website.
There is shortcut command called !overwritePage which will not replace the <body> but put a full screen
<iframe> on top of it containing a target URL:
!overwritePage('checkout.html', '') = https://svrnm.github.io/error-pages/whoops-no-message/It's a good practice to use a variable for the replacement, because in the Variables tab of your configuration you can
use a HTML editor for input.
Sometimes a simple replacement can not do the job, especially when the word you'd like to replace is short or common.
One way to get there is using !querySelector:
!querySelector(\#cart > .items-count) = 15This will replace the number of items in the cart without you needing to replace every occurrence of 15 or without you
needing to make sure that the number of items is fixed.
Additionally you can add an attribute that should be replaced:
!querySelector(\#cart > .items-count, style.background) = redIf you need to add a small piece of JavaScript into a website, you can do this with DemoMonkey:
!addScript() = console.log('hello')For more complex scripts it is a good practice to put them into a variable:
!addScript() = ${myScript}With that you can use the Variables tab to edit the content of the script.
As an alternative you can provide attributes in the list of the parameters, including a src:
!addScript(id\=myscriptid, src\=https://myserver/script.js)Note that you need to escape the = in the parameters. Also, be aware that there might be a
Content Security Policy that does not allow you
to inject scripts this way.
DemoMonkey is not a perfect match for more complex use cases. There are multiple chrome extensions that do a better job, if needed.
The command !replaceNeighbor allows you to search for labels close by and replace them. This is especially useful
to change numbers, which might not be unique or static. You can use this command like the following:
!replaceNeighbor(Cart Items, 3, .items-count) = 15Read this command like the following: Search for an element with text "Cart Items", go up 3 elements in the DOM tree, search for a sibling with selector ".items-count" and replace the text of it with "15"
To add content before or after a DOM element you can use !insertBefore and !insertAfter:
!insertBefore(DemoMonkey, 2, index.html) = $beforeHtml
!insertAfter(DemoMonkey, 2, index.html) = $afterHtmlTo hook into AJAX calls and to manipulate their response, you first need to go to Settings > Optional Features
and turn on Hook into Ajax. Now you can replace a response or you can apply a JSON patch on them:
; replace the whole response with the given replacement
!replaceAjaxResponse(/v1/items) = '[{"name": "Demo"}, {"name": "Monkey"}]'
; apply a search and replace on the response,
; here every occurrence of "Example" is replaced with "DemoMonkey"
!replaceAjaxResponse(/v1/items, Example) = DemoMonkey
; apply a json patch on the given response
!patchAjaxResponse(/v1/items) = [{"op": "replace", "path": "/0/name", "value": "DemoMonkey"}]Use this feature with care, since this will patch native javascript functionality to intercept API calls.
If you have a set of common demo flows you use regularly, DemoMonkey can help you to understand how long it takes you to go from one step (or stage) of your flow to another. Provide "stages" by providing parts of the URL or parts of the page title:
!stage(homepage.html, Homepage) = Demo Start
!stage(WIZARD_STEP_1) = Demo - Step1
!stage(,Overview) = Demo - Last StepWhile going through your demo, DemoMonkey will measure the time it takes you to go from one stage to another
and log that information into the Logs you can access from the main dashboard navigation.
DemoMonkey has namespaces that hold commands that are specific to a certain web application.
As of now the only namespace that exists is appdynamics. Since DemoMonkey is open source, you
can easily contribute your own
The following set of commands is specific to the AppDynamics controller UI.
All commands require a appdynamics prefix or you can include the namespace anywhere in your configuration:
@namespace[] = appdynamicsYou can replace icons on the flowmap with !replaceFlowmapIcon:
!replaceFlowmapIcon(api.mainsupplier.com) = SAP
!replaceFlowmapIcon(ECommerce-Service) = PHPDemoMonkey will provide you with a list of tier and backend types you can use for replacement via autocompletion.
Besides the generic !hide command there are multiple specific commands to hide elements in the AppDynamics controller UI:
!hideApplication(E-Commerce)
!hideBusinessTransaction(/checkout)
!hideDatabase(E-Commerce Database)
!hideBrowserApplication(eCommerce Browser)
!hideDashboard(AD-*)
!hideMobileApplication(Ecommerce iOS)
!hideBusinessJourney(Loan Approval)
!hideAnalyticsSearch(AD-*)To change the state of a connection on the flowmap to critical or warning, use the following command:
!replaceFlowmapConnection(ECommerce-Services, Inventory-Services) = warningNote, that the order of the elements is important. The first attribute is the exit node, the second node is the downstream entry node.
If you'd like to force a connection to being set to a certain value, even if no baseline is set, you can set a force parameter:
!replaceFlowmapConnection(ECommerce-Services, Inventory-Services, 1) = warningTo set a different protocol or to make the connection async, add the keywords to the replacement:
!replaceFlowmapConnection(ECommerce-Services, Inventory-Services, 1) = warning,async,httpFinally, you can also hide flowmap connections, either by using the keyword hide in your replacement or
by using a special command:
!hideFlowmapConnection(ECommerce-Services, Inventory-Services)To change the number of nodes (and tiers) on a flowmap node, use the following:
!replaceNodeCount(NodeName) = 5This will increase the node count to 5. If you have a business application with nodes and tiers you can provide two numbers to have both replaced:
!replaceNodeCount(OtherApplication) = 15,31Finally, if you have a AWS Lambda or an OpenTelemetry tier, you can set them here as well:
!replaceNodeCount(LambdaApp) = lambda
!replaceNodeCount(Service_OT) = otelIf you'd like to turn the lights for your prospects country red or orange on one of the EUM maps, you can do the following:
!replaceGeoStatus(Germany) = criticalPlease note, that this does not work for all countries consistently.
The documentation might not include all commands and all options you have with DemoMonkey.
If you want to learn more about DemoMonkey, the quickest way is, to get in touch