- What is TestInvite?
- Build Your First Test
- Run Your First Assessment
- Taking the Assessment
- Viewing the Results
- Question Bank Overview
- Common Question Features
- Scoring
- Question Types
- Question Authoring
- Browsing Questions
- Content Blocks
- Media Library
- Metadata Rules & Schema
- Tests Overview
- My Tests
- Creating a Test
- The Test Editor
- Test Settings
- Sections & Pages
- Adding Questions
- Page Builders
- Test Profile
- Reporting
- Test Papers
- Analytics
- Publishing a Test
- Test Library
- Marketplace
- Tasks Overview
- Creating a Task
- Task Dashboard
- Steps
- Task Settings
- Candidates
- Test Sessions
- Sent Mails
- Proctoring
- Analytics
- Analytics Overview
- Test Analytics
- Question Analytics
- Per-Question Analytics
- Results Explorer
- Segment Analysis
Writing the Function
The function must be a single synchronous function named anonymous that returns a plain object; helpers go inside it, and object spread, optional chaining (?.) and nullish coalescing (??) are rejected by the parser.
The function turns the Report Input into the values your template prints. It is optional, but recommended for every report beyond the simplest.
The contract
function anonymous(data) {
// ...compute...
return { /* a plain object */ };
}| Rule | Why |
|---|---|
Exactly one top-level function, named anonymous, taking one parameter. | The validator rejects anything else — including const anonymous = (data) => …. |
Helper functions go inside anonymous. | A second top-level statement is rejected. |
Not async; no await or Promises. | The function runs synchronously. |
Return a plain object — not an array, string or null. | The template's variables are the keys of that object. |
Return only data: objects, arrays, strings, numbers, booleans, null. | The output is copied out of the sandbox. A function or class instance inside it fails with could not be cloned. |
No network, require, import, fetch or timers. | They don't exist in the sandbox. |
| Finish within 5 seconds and 128 MB. | Longer runs are stopped with Script execution timed out. |
Supported syntax
JavaScript up to ES2017 is supported: const/let, arrow functions, template literals, destructuring, for…of, Object.keys/values/entries, array spread [...list]. Three newer forms are rejected with “Unexpected token”:
| Not supported | Write instead |
|---|---|
Object spread { ...obj, x: 1 } | Object.assign({}, obj, { x: 1 }) |
Optional chaining a?.b | a && a.b |
Nullish coalescing a ?? b | a !== undefined && a !== null ? a : b |
Design guidelines
- Put the logic in the function, keep the template simple. Compute percentages, bands, sorted lists and chosen texts in JavaScript; the template only loops and prints. The Output: Data panel lets you check every value before any HTML is involved.
- Return a view model, not the raw input. Name values for the page —
overall.percent,dimensions[].band.label— so the template never digs throughresult.dimensions["Aptitude/Verbal"].stats.point. - Guard everything that may be missing:
result,ticket,normy,testReporting,dates.completed. Return aready: falseflag and render a friendly message for it. - Never divide by zero — a section or dimension can have a
totalPointof 0. - Keep all display texts in one lookup object (band descriptions, level texts). There is one place to edit, and the longest text is easy to find when you check the PDF layout.
- Make the order explicit. Sort lists yourself instead of relying on object key order.
Complete example
Overall score and band, the pass/fail verdict from the test's own Pass / Fail settings, a table of top-level dimensions with their benchmark percentile, and section scores. It handles unscored sessions and missing benchmark or reporting data. The matching template is on Writing the Template.
function anonymous(data) {
// ---- helpers (must live INSIDE the function) ----
function pct(point, total) {
if (!total) return 0;
return Math.round((point / total) * 100);
}
function bandOf(p) {
if (p >= 80) return { key: "high", label: "Strong", color: "#1b7f4b" };
if (p >= 60) return { key: "mid", label: "Proficient", color: "#b7791f" };
return { key: "low", label: "Developing", color: "#c53030" };
}
const result = data.result;
if (!result) {
// session not completed / not scored yet
return { ready: false };
}
// ---- overall ----
const percent = pct(result.stats.point, result.stats.totalPoint);
// pass/fail from the test's own Pass / Fail settings (may be missing in some contexts)
let verdict = null;
const pf = data.testReporting && data.testReporting.passFail;
if (pf && pf.active) {
const min = pf.pass.min;
const threshold = min.score * 100; // stored as a 0-1 fraction
const passed = min.inclusive ? percent >= threshold : percent > threshold;
verdict = { passed: passed, label: passed ? pf.pass.content.label : pf.fail.content.label };
}
// ---- dimensions: only top-level dimensions ("Aptitude", not "Aptitude/Verbal") ----
const normDims = data.normy && data.normy.normativeResult ? data.normy.normativeResult.dimensions : {};
const dimensions = Object.keys(result.dimensions)
.filter(function (name) { return name.indexOf("/") === -1; })
.sort()
.map(function (name) {
const s = result.dimensions[name].stats;
const p = pct(s.point, s.totalPoint);
const norm = normDims[name];
return {
name: name,
point: s.point,
totalPoint: s.totalPoint,
percent: p,
band: bandOf(p),
percentile: norm ? norm.percentile : null
};
});
// ---- sections ----
const sections = result.sections.map(function (sec, i) {
return {
title: sec.title || "Section " + (i + 1),
percent: pct(sec.stats.point, sec.stats.totalPoint)
};
});
return {
ready: true,
candidate: {
name: data.ticket && data.ticket.info ? data.ticket.info.fullname : "",
email: data.ticket && data.ticket.info ? data.ticket.info.email : ""
},
testTitle: data.info.testTitle,
completedAt: data.dates.completed ? data.dates.completed.text : "",
overall: { point: result.stats.point, totalPoint: result.stats.totalPoint, percent: percent, band: bandOf(percent) },
verdict: verdict,
dimensions: dimensions,
sections: sections
};
}Its output, for a candidate with 34 of 50 points and a 60% pass mark, starts like this — the exact shape the template works with:
{
"ready": true,
"candidate": { "name": "Jane Doe", "email": "jane@example.com" },
"testTitle": "Sales Aptitude Test",
"overall": { "point": 34, "totalPoint": 50, "percent": 68,
"band": { "key": "mid", "label": "Proficient", "color": "#b7791f" } },
"verdict": { "passed": true, "label": "Pass" },
"dimensions": [ { "name": "Aptitude", "percent": 73, "percentile": null, … }, … ],
"sections": [ { "title": "Reasoning", "percent": 73 }, … ]
}