# Middleware helper functions
OnExtraction only
Middleware helper functions are available for the OnExtraction hook only.
Middleware helper functions allow data extraction from third-party JavaScript given a document.
By default, third-party JavaScript is not executed, so using these functions you will be able to mock the window object and execute targeted scripts that will populate the mocked object with the data you need to retrieve.
The two helper functions are installMock
and execute
.
To enable them, add the helpers
property as an argument of the hook.
export const onExtraction: onExtractionFunction<ChartbeatProps> = async ({ document, helpers }): Promise<ChartbeatProps | undefined> => ...
TIP
The helper functions are available as the helpers
property of the ExtractionArguments
, an object passed to the hook on runtime.
# installMock
Use it to get a reference to the window property we are interested in.
Its first parameter is a createMock
function. It will mock the object we want to access and it's created from an interface defined in the same Middleware. The second parameter is the name of the target property on the Window object.
interface CustomProp {
targetData: string;
}
const config = helpers.installMock(
createMock<CustomProp>(),
'customProp'
);
TIP
In this example, we expect a window.customProp
object containing a targetData
property.
# execute
Use it to run the target JS script.
Its only parameter is a string. The method will execute the script that contains the text matching the input parameter.
helpers.execute('_sf_async_config');
TIP
The execute
method parses all the scripts until there's a match with the input string.
# Use helpers to mock a window
object
In this example, we'll use a tenant that has a third-party JavaScript that when executed, populates the sfConfig
property on the Window object, which contains key information we need to retrieve for our Analytics Provider, precisely the Author and Section parameters.
As we don't have access to the Window object in the OnExtraction hook, we'll use the installMock
helper to get a reference to the window property we are interested in.
First, create an interface and name it after the window property you want to mock.
interface SFConfig {
author: string;
section: string;
uid: number,
}
Then, call the installMock
method, passing as parameters:
- The
createMock
function with the interface you just created. - A string with the window property you want to retrieve.
const config = helpers.installMock(
createMock<SFConfig>(),
'sfConfig'
);
Afterward, use the execute
method to run the target script. As a parameter, add a string that partially matches the content of the script.
helpers.execute('_sf_async_config');
Pattern
The parameter of the execute
function is a pattern partly matching the content in the script. As most of the scripts are plain <script>
tags that don't have attributes, CSS selectors can't be used.
Once the script has run, it will have modified window.sfConfig
and its values will be automatically available via the config
object.
Your Middleware implementation should look like that:
import { ChartbeatProps } from '@marfeel/analytics-providers-chartbeat';
import { onExtractionFunction } from '@marfeel/middlewares-types';
import { createMock } from 'ts-auto-mock';
interface SFConfig {
author: string;
section: string;
uid: number,
}
export const onExtraction: onExtractionFunction<ChartbeatProps> = async ({ document, helpers }): Promise<ChartbeatProps | undefined> => {
const config = helpers.installMock(
createMock<SFConfig>(),
'sfConfig'
);
helpers.execute('_sf_async_config');
return {
vars: {
author: config.author,
section: config.section
}
};
};
# Use helpers to access arguments
You can also use the mock system to access variables used by a third-party script. This is extremely helpful when you have a script where some parameters are passed to a function and you want to extract those values.
<script>
var id = 'SPHWPJS';
var params = {
adsvolume: 100,
autostart: true,
html5: true,
url: 'https://mdstrm.com/video/5f07248bc1d4bc07b4f40991.m3u8',
volume: 100
};
play(id, params);
</script>
Here, you can use a combination of installMock
and execute
to access the values from the script, much like you would when writing a test.
First, we define a type Play
that matches the signature of the function we are interested in.
type Play = (id: string, params: object) => void;
Then we use installMock
to create a mock of the play function on the Window object.
const mock = helpers.installMock(
createMock<Play>(),
'play'
);
Once we have the mock set up, we'll find the script containing the text play(id, params);
and execute it.
helpers.execute('play(id, params);');
Now, we can use the mock object to access the values passed to the function via mock.play.args
. It will contain an array of all the calls to the function, and each element will be an array of the arguments passed.
const [[id, params]] = mock.play.args;
In the end, the Middleware should look like that:
import { onExtractionFunction } from '@marfeel/middlewares-types';
import { createMock } from 'ts-auto-mock';
export const onExtraction: onExtractionFunction<ChartbeatProps> = async ({ document, helpers }): Promise<ChartbeatProps | undefined> => {
type Play = (id: string, params: object) => void;
const mock = helpers.installMock(
createMock<Play>(),
'play'
);
helpers.execute('play(id, params);');
const [[id, params]] = mock.play.args;
return {
id: id,
url: params.url
};
};
# Use helpers to access redefined variables
You can also access variables that are defined on the window
but never used as function arguments like this:
var params = {
id: 'video-id',
url: 'https://mdstrm.com/video/5f07248bc1d4bc07b4f40991.m3u8',
};
WARNING
This method only works for variables defined with var
; not let
or const
as those are block-scoped.
First, we create an interface
and name it after the var
to mock:
interface Params {
id: string;
url: string;
}
TIP
Notice how the interface has the same name, params
, but is in PascalCase, like all the other TyepScript interfaces!
Then, we use installMock
to create a mock of the variable we want from the 3rd party script:
helpers.installmock(
createmock<Params>(),
'params'
);
TIP
Notice how the mock is not stored in a variable?
This is because the 3rd party script we're about to execute is going to redefine the params
variable anyway.
In this variable mocking mechanism, contrary to other methods, the variable value is going to be available directly in the window
object.
Why do we even mock, then? Due to JavaScript binding, if we didn't define that params
variable before executing the 3rd party script, it would never receive a value. We have to define that variable before the script executes, for it to get updated in our window
object.
Weird? I know, but that's JavaScript.
Once we have set up the mock, we use the helpers.execute
function to find the script containing the text params
:
helpers.execute('params');
And we can finally use the window
argument to retrieve the updated values of the variable:
const { id, url } = window.params;
In the end, the Middleware should look like this:
import { onExtractionFunction } from '@marfeel/middlewares-types';
import { createMock } from 'ts-auto-mock';
interface Params {
id: string;
url: string;
}
export const onExtraction: onExtractionFunction<ChartbeatProps> = async ({ window, document, helpers }): Promise<ChartbeatProps | undefined> => {
helpers.installmock(
createmock<params>(),
'params'
);
helpers.execute('params');
const { id, url } = window.params;
return {
id,
params
};
};