@@ -30,6 +30,30 @@ const _ = require('lodash'),
3030
3131 executeContext = require ( './execute-context' ) ;
3232
33+ function execTemplate ( template ) {
34+ return new Promise ( ( resolve , reject ) => {
35+ const _module = { exports : { } } ,
36+ scope = Scope . create ( {
37+ eval : true ,
38+ ignore : [ 'require' ] ,
39+ block : [ 'bridge' ]
40+ } ) ;
41+
42+ scope . import ( {
43+ Buffer : require ( 'buffer' ) . Buffer ,
44+ module : _module
45+ } ) ;
46+
47+ scope . exec ( template , ( err ) => {
48+ if ( err ) {
49+ return reject ( err ) ;
50+ }
51+
52+ return resolve ( _module ?. exports || { } ) ;
53+ } ) ;
54+ } ) ;
55+ }
56+
3357module . exports = function ( bridge , glob ) {
3458 // @note we use a common scope for all executions. this causes issues when scripts are run inside the sandbox
3559 // in parallel, but we still use this way for the legacy "persistent" behaviour needed in environment
@@ -42,53 +66,70 @@ module.exports = function (bridge, glob) {
4266 // For caching required information provided during
4367 // initialization which will be used during execution
4468 let initializationOptions = { } ,
45- initializeExecution ;
69+ templatesRegistry = { } ,
70+ getProtocolMetadata = ( protocolName ) => {
71+ return templatesRegistry [ protocolName ] || templatesRegistry . _default || null ;
72+ } ;
4673
4774 /**
4875 * @param {Object } options
49- * @param {String } [options.template]
76+ * @param {String } [options.template] - Template string - To be deprecated, use `templates` instead
77+ * @param {Record<String, String> } [options.templates] - Map of template names to template strings
78+ * @param {String } [options.chaiPlugin]
5079 * @param {Boolean } [options.disableLegacyAPIs]
5180 * @param {Array.<String> } [options.disabledAPIs]
5281 */
53- bridge . once ( 'initialize' , ( { template , ... initOptions } ) => {
82+ bridge . once ( 'initialize' , async ( initOptions ) => {
5483 initializationOptions = initOptions || { } ;
5584
56- // If no custom template is provided, go ahead with the default steps
57- if ( ! template ) {
85+ const { template, templates, chaiPlugin } = initializationOptions ,
86+ availableTemplates = templates || ( template ? { _default : template } : { } ) ,
87+ templateNames = Object . keys ( availableTemplates ) ,
88+ promises = templateNames . map ( ( name ) => {
89+ return execTemplate ( availableTemplates [ name ] ) ;
90+ } ) ;
91+
92+ // If no custom template or chai plugin is provided, go ahead with the default steps
93+ if ( ! templates && ! template && ! chaiPlugin ) {
5894 chai . use ( require ( 'chai-postman' ) ( sdk , _ , Ajv ) ) ;
5995
6096 return bridge . dispatch ( 'initialize' ) ;
6197 }
6298
63- const _module = { exports : { } } ,
64- scope = Scope . create ( {
65- eval : true ,
66- ignore : [ 'require' ] ,
67- block : [ 'bridge' ]
68- } ) ;
99+ // Templates can pass a chai plugin as a string or a function
100+ // or a chaiPlugin arg is supported that works with all templates and receives the registry as an argument
101+ promises . push ( chaiPlugin ? execTemplate ( chaiPlugin ) : Promise . resolve ( null ) ) ;
69102
70- scope . import ( {
71- Buffer : require ( 'buffer' ) . Buffer ,
72- module : _module
73- } ) ;
103+ try {
104+ const results = await Promise . all ( promises ) ,
105+ templateChaiPlugins = [ ] ,
106+ chaiPluginResult = results . pop ( ) ;
74107
75- scope . exec ( template , ( err ) => {
76- if ( err ) {
77- return bridge . dispatch ( 'initialize' , err ) ;
78- }
108+ for ( let i = 0 ; i < results . length ; i ++ ) {
109+ const result = results [ i ] ;
79110
80- const { chaiPlugin , initializeExecution : setupExecution } = ( _module && _module . exports ) || { } ;
111+ templatesRegistry [ templateNames [ i ] ] = result ;
81112
82- if ( _ . isFunction ( chaiPlugin ) ) {
83- chai . use ( chaiPlugin ) ;
113+ if ( _ . isFunction ( result . chaiPlugin ) ) {
114+ templateChaiPlugins . push ( result . chaiPlugin ) ;
115+ }
84116 }
85117
86- if ( _ . isFunction ( setupExecution ) ) {
87- initializeExecution = setupExecution ;
118+ if ( _ . isFunction ( chaiPluginResult ) ) {
119+ chai . use ( chaiPluginResult ( templatesRegistry ) ) ;
120+ }
121+ else if ( templateChaiPlugins . length === 1 ) {
122+ chai . use ( templateChaiPlugins [ 0 ] ) ;
123+ }
124+ else if ( templateChaiPlugins . length > 1 ) {
125+ throw new Error ( 'sandbox: multiple chai plugins are not supported' ) ;
88126 }
89127
90128 bridge . dispatch ( 'initialize' ) ;
91- } ) ;
129+ }
130+ catch ( error ) {
131+ return bridge . dispatch ( 'initialize' , error ) ;
132+ }
92133 } ) ;
93134
94135 /**
@@ -108,6 +149,10 @@ module.exports = function (bridge, glob) {
108149 return bridge . dispatch ( 'error' , new Error ( 'sandbox: execution identifier parameter(s) missing' ) ) ;
109150 }
110151
152+ if ( initializationOptions . templates && ! ( options . templateName && _ . isString ( options . templateName ) ) ) {
153+ return bridge . dispatch ( 'error' , new Error ( 'sandbox: template name parameter is missing from options' ) ) ;
154+ }
155+
111156 ! options && ( options = { } ) ;
112157 ! context && ( context = { } ) ;
113158 event = ( new PostmanEvent ( event ) ) ;
@@ -137,8 +182,10 @@ module.exports = function (bridge, glob) {
137182 return isNonLegacySandbox ( code ) ? `${ getNonLegacyCodeMarker ( ) } ${ asyncCode } ` : asyncCode ;
138183 } ) ( event . script ?. toSource ( ) ) ,
139184
185+ protocolMetadata = getProtocolMetadata ( options . templateName ) ,
186+
140187 // create the execution object
141- execution = new Execution ( id , event , context , { ...options , initializeExecution } ) ,
188+ execution = new Execution ( id , event , context , { ...options , protocolMetadata } ) ,
142189
143190 disabledAPIs = [
144191 ...( initializationOptions . disabledAPIs || [ ] ) ,
@@ -242,7 +289,7 @@ module.exports = function (bridge, glob) {
242289 bridge . on ( runRequestResponseEventName , function ( id , err , response , results ) {
243290 // Apply variable mutations from the scripts executed via pm.execution.runRequest
244291 // to the current execution scope
245- const { variableMutations = { } , skipResponseCasting = false } = results || { } ;
292+ const { variableMutations = { } , responseType } = results || { } ;
246293
247294 CONTEXT_VARIABLE_SCOPES . forEach ( function ( variableType ) {
248295 let mutableVariableScope = execution [ variableType ] ,
@@ -264,7 +311,7 @@ module.exports = function (bridge, glob) {
264311 } ) ;
265312 } ) ;
266313
267- timers . clearEvent ( id , err , response , { skipResponseCasting } ) ;
314+ timers . clearEvent ( id , err , response , { responseType } ) ;
268315 } ) ;
269316
270317 // handle cookies event from outside sandbox
@@ -331,7 +378,7 @@ module.exports = function (bridge, glob) {
331378 context ) ;
332379 } ,
333380 createPostmanRequire ( options . resolvedPackages , scope ) ,
334- { disabledAPIs } )
381+ { disabledAPIs, getProtocolMetadata } )
335382 ) ,
336383 dispatchAssertions ,
337384 { disableLegacyAPIs : initializationOptions . disableLegacyAPIs } ) ;
0 commit comments