feat: enable insomnia.test and insomnia.expect in scripting - INS-3637 (#7202)

* feat: enable insomnia.test and insomnia.expect in scripting

* fix: lint error

* feat: enable replaceIn method
This commit is contained in:
Hexxa 2024-03-26 22:04:26 +08:00 committed by GitHub
parent 5ff9ed909e
commit 38896f4547
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 143 additions and 11 deletions

View File

@ -750,3 +750,49 @@ resources:
"events": {{ _.events }}
}
_type: request
- _id: req_89dade2ee9ee42fbb22d588783a9df1
parentId: fld_01de564274824ecaad272330339ea6b2
modified: 1636707449231
created: 1636141014552
url: http://127.0.0.1:4010/echo
name: insomnia.test
description: ""
method: POST
parameters: []
headers:
- name: 'Content-Type'
value: 'application/json'
authentication: {}
metaSortKey: -1636141014553
isPrivate: false
settingStoreCookies: true
settingSendCookies: true
settingDisableRenderRequestBody: false
settingEncodeUrl: true
settingRebuildPath: true
settingFollowRedirects: global
preRequestScript: |-
insomnia.test('happy tests', () => {
pm.expect(200).to.eql(200);
pm.expect('uname').to.be.a('string');
pm.expect('a').to.have.lengthOf(1);
pm.expect('xxx_customer_id_yyy').to.include("customer_id");
pm.expect(201).to.be.oneOf([201,202]);
pm.expect(199).to.be.below(200);
// test objects
pm.expect({a: 1, b: 2}).to.have.all.keys('a', 'b');
pm.expect({a: 1, b: 2}).to.have.any.keys('a', 'b');
pm.expect({a: 1, b: 2}).to.not.have.any.keys('c', 'd');
pm.expect({a: 1}).to.have.property('a');
pm.expect({a: 1, b: 2}).to.be.a('object')
.that.has.all.keys('a', 'b');
});
insomnia.test('unhappy tests', () => {
pm.expect(199).to.eql(200);
pm.expect(199).to.be.oneOf([201,202]);
});
body:
mimeType: "application/json"
text: |-
{}
_type: request

View File

@ -344,6 +344,21 @@ test.describe('pre-request features tests', async () => {
await page.getByRole('tab', { name: 'Timeline' }).click();
await expect(responsePane).toContainText('fixtures/certificates/fake.pfx'); // original proxy
});
test('insomnia.test and insomnia.expect can work together ', async ({ page }) => {
const responsePane = page.getByTestId('response-pane');
await page.getByLabel('Request Collection').getByTestId('insomnia.test').press('Enter');
// send
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
// verify
await page.getByRole('tab', { name: 'Timeline' }).click();
await expect(responsePane).toContainText('✓ happy tests'); // original proxy
await expect(responsePane).toContainText('✕ unhappy tests: AssertionError: expected 199 to deeply equal 200'); // updated proxy
});
});
test.describe('unhappy paths', async () => {

View File

@ -36,9 +36,8 @@ const runPreRequestScript = async (
): Promise<RequestContext> => {
console.log(script);
const executionContext = initInsomniaObject(context);
const log: string[] = [];
// TODO: we should at least support info, debug, warn, error
const log: string[] = [];
const consoleInterceptor = {
log: (...args: any[]) => log.push(
JSON.stringify({
@ -49,6 +48,8 @@ const runPreRequestScript = async (
),
};
const executionContext = initInsomniaObject(context, consoleInterceptor.log);
const evalInterceptor = (script: string) => {
invariant(script && typeof script === 'string', 'eval is called with invalid or empty value');
const result = eval(script);

View File

@ -1,3 +1,4 @@
import { getIntepolator } from './interpolator';
export class Environment {
private kvs = new Map<string, boolean | number | string>();
@ -25,10 +26,9 @@ export class Environment {
this.kvs.clear();
};
// TODO: enable this after intepolator is introduced
// replaceIn = (template: string) => {
// return getIntepolator().render(template, this.toObject());
// };
replaceIn = (template: string) => {
return getIntepolator().render(template, this.toObject());
};
toObject = () => {
return Object.fromEntries(this.kvs.entries());
@ -90,10 +90,10 @@ export class Variables {
this.local.set(variableName, variableValue);
};
// replaceIn = (template: string) => {
// const context = this.toObject();
// return getIntepolator().render(template, context);
// };
replaceIn = (template: string) => {
const context = this.toObject();
return getIntepolator().render(template, context);
};
toObject = () => {
return [

View File

@ -1,3 +1,5 @@
import { expect } from 'chai';
import { ClientCertificate } from '../../models/client-certificate';
import { RequestBodyParameter, RequestHeader } from '../../models/request';
import { Settings } from '../../models/settings';
@ -8,6 +10,7 @@ import { unsupportedError } from './properties';
import { Request as ScriptRequest, RequestBodyOptions, RequestOptions } from './request';
import { Response as ScriptResponse } from './response';
import { sendRequest } from './send-request';
import { test } from './test';
export class InsomniaObject {
public environment: Environment;
@ -16,12 +19,16 @@ export class InsomniaObject {
public variables: Variables;
public request: ScriptRequest;
private clientCertificates: ClientCertificate[];
private _expect = expect;
private _test = test;
// TODO: follows will be enabled after Insomnia supports them
private _globals: Environment;
private _iterationData: Environment;
private _settings: Settings;
private _log: (...msgs: any[]) => void;
constructor(
rawObj: {
globals: Environment;
@ -33,6 +40,7 @@ export class InsomniaObject {
settings: Settings;
clientCertificates: ClientCertificate[];
},
log: (...msgs: any[]) => void,
) {
this._globals = rawObj.globals;
this.environment = rawObj.environment;
@ -44,6 +52,8 @@ export class InsomniaObject {
this.request = rawObj.request;
this._settings = rawObj.settings;
this.clientCertificates = rawObj.clientCertificates;
this._log = log;
}
sendRequest(
@ -53,6 +63,14 @@ export class InsomniaObject {
return sendRequest(request, cb, this._settings);
}
test(msg: string, fn: () => void) {
this._test(msg, fn, this._log);
}
expect(exp: boolean | number | string | object) {
return this._expect(exp);
}
// TODO: remove this after enabled globals
get globals() {
throw unsupportedError('globals', 'base environment');
@ -84,6 +102,7 @@ export class InsomniaObject {
export function initInsomniaObject(
rawObj: RequestContext,
log: (...args: any[]) => void,
) {
const globals = new Environment(rawObj.globals);
const environment = new Environment(rawObj.environment);
@ -188,5 +207,6 @@ export function initInsomniaObject(
settings: rawObj.settings,
clientCertificates: rawObj.clientCertificates,
},
log,
);
};

View File

@ -0,0 +1,34 @@
import { configure, type ConfigureOptions, type Environment as NunjuncksEnv } from 'nunjucks';
class Intepolator {
private engine: NunjuncksEnv;
constructor(config: ConfigureOptions) {
this.engine = configure(config);
}
render = (template: string, context: object) => {
// TODO: handle timeout
// TODO: support plugin?
return this.engine.renderString(template, context);
};
}
const intepolator = new Intepolator({
autoescape: false,
// Don't escape HTML
throwOnUndefined: true,
// Strict mode
tags: {
blockStart: '{%',
blockEnd: '%}',
variableStart: '{{',
variableEnd: '}}',
commentStart: '{#',
commentEnd: '#}',
},
});
export function getIntepolator() {
return intepolator;
}

View File

@ -579,6 +579,7 @@ export function mergeRequests(
{},
),
authentication: fromPreRequestAuth(updatedReq.auth),
preRequestScript: '',
};
return {

View File

@ -0,0 +1,12 @@
export function test(
msg: string,
fn: () => void,
log: (message?: any, ...optionalParams: any[]) => void,
) {
try {
fn();
log(`${msg}`);
} catch (e) {
log(`${msg}: ${e}`);
}
}

View File

@ -172,7 +172,10 @@ export const PreRequestScriptEditor: FC<Props> = ({
}),
settings,
clientCertificates: [],
}),
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
(_msg: string, _fn: () => void) => { }
),
'insomnia',
);