insomnia/packages/insomnia-smoke-test/tests/smoke/pre-request-script-features.test.ts

478 lines
18 KiB
TypeScript

import path from 'node:path';
import { expect } from '@playwright/test';
import { Buffer } from 'buffer';
import { getFixturePath, loadFixture } from '../../playwright/paths';
import { test } from '../../playwright/test';;
test.describe('pre-request features tests', async () => {
test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms');
test.beforeEach(async ({ app, page }) => {
const text = await loadFixture('pre-request-collection.yaml');
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
await page.getByRole('button', { name: 'Create in project' }).click();
await page.getByRole('menuitemradio', { name: 'Import' }).click();
await page.locator('[data-test-id="import-from-clipboard"]').click();
await page.getByRole('button', { name: 'Scan' }).click();
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
await page.getByLabel('Pre-request Scripts').click();
});
const testCases = [
{
name: 'environments setting and overriding',
expectedBody: {
fromBaseEnv: 'baseEnv',
scriptValue: 'fromEnv',
preDefinedValue: 'fromScript',
customValue: 'fromFolder',
},
},
{
name: 'variables / manipulate variables and set them to environment',
expectedBody: {
varStr: 'varStr',
varNum: 777,
varBool: true,
},
},
{
name: 'require / require classes from insomnia-collection module and init them',
expectedBody: {
propJson: {
disabled: false,
id: 'pid',
name: 'pname',
},
headerJson: {
key: 'headerKey',
value: 'headerValue',
id: '',
name: '',
type: '',
},
},
},
{
name: 'insomnia.request manipulation',
customVerify: (bodyJson: any) => {
expect(bodyJson.method).toEqual('GET');
expect(bodyJson.headers['x-hello']).toEqual('hello');
expect(bodyJson.data).toEqual('rawContent');
},
},
{
name: 'insomnia.request auth manipulation (bearer)',
customVerify: (bodyJson: any) => {
const authzHeader = bodyJson.headers['authorization'];
expect(authzHeader != null).toBeTruthy();
expect(bodyJson.headers['authorization']).toEqual('CustomTokenPrefix tokenValue');
},
},
{
name: 'insomnia.request auth manipulation (basic)',
customVerify: (bodyJson: any) => {
const authzHeader = bodyJson.headers['authorization'];
expect(authzHeader != null).toBeTruthy();
const expectedEncCred = Buffer.from('myName:myPwd', 'utf-8').toString('base64');
expect(bodyJson.headers['authorization']).toEqual(`Basic ${expectedEncCred}`);
},
},
{
name: 'eval() works in script',
expectedBody: {
evalResult: 16,
},
},
{
name: 'require the url module',
customVerify: (bodyJson: any) => {
const reqBodyJsons = JSON.parse(bodyJson.data);
expect(reqBodyJsons).toEqual({
hash: '#hashcontent',
host: 'insomnia.com:6666',
hostname: 'insomnia.com',
href: 'https://user:pwd@insomnia.com:6666/p1?q1=a&q2=b#hashcontent',
origin: 'https://insomnia.com:6666',
password: 'pwd',
pathname: '/p1',
port: '6666',
protocol: 'https:',
search: '?q1=a&q2=b',
username: 'user',
seachParam: 'q1=a&q2=b',
});
},
},
{
name: 'require node.js modules',
expectedBody: {
path: true,
assert: true,
buffer: true,
util: true,
url: true,
punycode: true,
querystring: true,
stringDecoder: true,
stream: true,
timers: true,
events: true,
},
},
{
name: 'get sendRequest response through await or callback',
customVerify: (bodyJson: any) => {
const requestBody = JSON.parse(bodyJson.data);
expect(requestBody.bodyFromAwait.method).toEqual('GET');
expect(requestBody.bodyFromCallback.method).toEqual('GET');
},
},
{
name: 'require the uuid module',
expectedBody: {
uuid: '00000000-0000-0000-0000-000000000000',
},
},
{
name: 'require external modules and built-in lodash',
expectedBody: {
atob: true,
btoa: true,
chai: true,
cheerio: true,
crypto: true,
csv: true,
lodash: true,
moment: true,
tv4: true,
uuid: true,
xml2js: true,
builtInLodash: true,
},
},
];
for (let i = 0; i < testCases.length; i++) {
const tc = testCases[i];
test(tc.name, async ({ page }) => {
const statusTag = page.locator('[data-testid="response-status-tag"]:visible');
const responseBody = page.getByTestId('response-pane').getByTestId('CodeEditor').locator('.CodeMirror-line');
await page.getByLabel('Request Collection').getByTestId(tc.name).press('Enter');
// send
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
// verify
await page.waitForSelector('[data-testid="response-status-tag"]:visible');
await expect(statusTag).toContainText('200 OK');
const rows = await responseBody.allInnerTexts();
expect(rows.length).toBeGreaterThan(0);
const bodyJson = JSON.parse(rows.join(' '));
if (tc.expectedBody) {
expect(JSON.parse(bodyJson.data)).toEqual(tc.expectedBody);
}
if (tc.customVerify) {
tc.customVerify(bodyJson);
}
});
}
test('send request with content type', async ({ page }) => {
const statusTag = page.locator('[data-testid="response-status-tag"]:visible');
const responseBody = page.getByTestId('response-pane').getByTestId('CodeEditor').locator('.CodeMirror-line');
await page.getByLabel('Request Collection').getByTestId('echo pre-request script result').press('Enter');
// set request body
await page.getByRole('tab', { name: 'Body' }).click();
await page.getByRole('button', { name: 'Body' }).click();
await page.getByRole('menuitem', { name: 'JSON' }).click();
const bodyEditor = page.getByTestId('CodeEditor').getByRole('textbox');
await bodyEditor.fill('{ "rawBody": {{ _.rawBody }}, "urlencodedBody": {{ _.urlencodedBody }}, "gqlBody": {{ _.gqlBody }}, "fileBody": {{ _.fileBody }}, "formdataBody": {{ _.formdataBody }} }');
// enter script
await page.getByTestId('pre-request-script-tab').click();
const preRequestScriptEditor = page.getByTestId('CodeEditor').getByRole('textbox');
await preRequestScriptEditor.fill(`
const rawReq = {
url: 'http://127.0.0.1:4010/echo',
method: 'POST',
header: {
'Content-Type': 'text/plain',
},
body: {
mode: 'raw',
raw: 'rawContent',
},
};
const urlencodedReq = {
url: 'http://127.0.0.1:4010/echo',
method: 'POST',
header: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
mode: 'urlencoded',
urlencoded: [
{ key: 'k1', value: 'v1' },
{ key: 'k2', value: 'v2' },
],
},
};
const gqlReq = {
url: 'http://127.0.0.1:4010/echo',
method: 'POST',
header: {
'Content-Type': 'application/graphql',
},
body: {
mode: 'graphql',
graphql: {
query: 'query',
operationName: 'operation',
variables: 'var',
},
},
};
const fileReq = {
url: 'http://127.0.0.1:4010/echo',
method: 'POST',
header: {
'Content-Type': 'application/octet-stream',
},
body: {
mode: 'file',
file: "${getFixturePath('files/rawfile.txt')}",
},
};
const formdataReq = {
url: 'http://127.0.0.1:4010/echo',
method: 'POST',
header: {
// TODO: try to understand why this breaks the test
// 'Content-Type': 'multipart/form-data',
},
body: {
mode: 'formdata',
formdata: [
{ key: 'k1', type: 'text', value: 'v1' },
{ key: 'k2', type: 'file', value: "${getFixturePath('files/rawfile.txt')}" },
],
},
};
const promises = [rawReq, urlencodedReq, gqlReq, fileReq, formdataReq].map(req => {
return new Promise((resolve, reject) => {
insomnia.sendRequest(
req,
(err, resp) => {
if (err != null) {
reject(err);
} else {
resolve(resp);
}
}
);
});
});
// send request
const resps = await Promise.all(promises);
// set envs
insomnia.environment.set('rawBody', resps[0].body);
insomnia.environment.set('urlencodedBody', resps[1].body);
insomnia.environment.set('gqlBody', resps[2].body);
insomnia.environment.set('fileBody', resps[3].body);
insomnia.environment.set('formdataBody', resps[4].body);
`);
// TODO: wait for body and pre - request script are persisted to the disk
// should improve this part, we should avoid sync this state through db as it introduces race condition
await page.waitForTimeout(500);
// send
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
// verify
await page.waitForSelector('[data-testid="response-status-tag"]:visible');
await expect(statusTag).toContainText('200 OK');
const rows = await responseBody.allInnerTexts();
expect(rows.length).toBeGreaterThan(0);
const bodyJson = JSON.parse(rows.join(' '));
const reqBodyJsons = JSON.parse(bodyJson.data);
expect(reqBodyJsons.rawBody.data).toEqual('rawContent');
expect(reqBodyJsons.urlencodedBody.data).toEqual('k1=v1&k2=v2');
expect(JSON.parse(reqBodyJsons.gqlBody.data)).toEqual({
query: 'query',
operationName: 'operation',
variables: 'var',
});
expect(reqBodyJsons.fileBody.data).toEqual('raw file content');
expect(reqBodyJsons.formdataBody.data).toEqual('--X-INSOMNIA-BOUNDARY\r\nContent-Disposition: form-data; name=\"k1\"\r\n\r\nv1\r\n--X-INSOMNIA-BOUNDARY\r\nContent-Disposition: form-data; name=\"k2\"; filename=\"rawfile.txt\"\r\nContent-Type: text/plain\r\n\r\nraw file content\r\n--X-INSOMNIA-BOUNDARY--\r\n');
});
test('insomnia.request / update proxy configuration', async ({ page }) => {
const responsePane = page.getByTestId('response-pane');
// update proxy configuration
await page.locator('[data-testid="settings-button"]').click();
await page.locator('text=Insomnia Preferences').first().click();
await page.locator('text=Enable proxy').click();
await page.locator('[name="httpProxy"]').fill('localhost:1111');
await page.locator('[name="httpsProxy"]').fill('localhost:2222');
await page.locator('[name="noProxy"]').fill('http://a.com,https://b.com');
await page.locator('.app').press('Escape');
await page.getByLabel('Request Collection').getByTestId('test proxies manipulation').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('localhost:2222'); // original proxy
await expect(responsePane).toContainText('Trying 127.0.0.1:8888'); // updated proxy
});
test('insomnia.request / update clientCertificate', async ({ page }) => {
const responsePane = page.getByTestId('response-pane');
const fixturePath = getFixturePath('certificates');
// update proxy configuration
await page.locator('text=Add Certificates').click();
await page.locator('text=Add client certificate').click();
await page.locator('[name="host"]').fill('a.com');
await page.locator('[data-key="pfx"]').click();
const fileChooserPromise = page.waitForEvent('filechooser');
await page.locator('text=Add PFX or PKCS12 file').click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(path.join(fixturePath, 'fake.pfx'));
await page.getByRole('button', { name: 'Add certificate' }).click();
await page.getByRole('button', { name: 'Done' }).click();
await page.getByLabel('Request Collection').getByTestId('test certificate manipulation').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('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 () => {
test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms');
test.beforeEach(async ({ app, page }) => {
const text = await loadFixture('pre-request-collection.yaml');
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
await page.getByRole('button', { name: 'Create in project' }).click();
await page.getByRole('menuitemradio', { name: 'Import' }).click();
await page.locator('[data-test-id="import-from-clipboard"]').click();
await page.getByRole('button', { name: 'Scan' }).click();
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
await page.getByLabel('Pre-request Scripts').click();
});
const testCases = [
{
name: 'invalid result is returned',
preReqScript: `
return;
`,
context: {
insomnia: {},
},
expectedResult: {
message: 'insomnia object is invalid or script returns earlier than expected.',
},
},
{
name: 'custom error is returned',
preReqScript: `
throw Error('my custom error');
`,
context: {
insomnia: {},
},
expectedResult: {
message: 'my custom error',
},
},
{
name: 'syntax error',
preReqScript: `
insomnia.INVALID_FIELD.set('', '')
`,
context: {
insomnia: {},
},
expectedResult: {
message: "Cannot read properties of undefined (reading 'set')",
},
},
];
for (let i = 0; i < testCases.length; i++) {
const tc = testCases[i];
test(tc.name, async ({ page }) => {
const responsePane = page.getByTestId('response-pane');
await page.getByLabel('Request Collection').getByTestId('echo pre-request script result').press('Enter');
// set request body
await page.getByRole('tab', { name: 'Body' }).click();
await page.getByRole('button', { name: 'Body' }).click();
await page.getByRole('menuitem', { name: 'JSON' }).click();
// enter script
await page.getByTestId('pre-request-script-tab').click();
const preRequestScriptEditor = page.getByTestId('CodeEditor').getByRole('textbox');
await preRequestScriptEditor.fill(tc.preReqScript);
// TODO: wait for body and pre-request script are persisted to the disk
// should improve this part, we should avoid sync this state through db as it introduces race condition
await page.waitForTimeout(500);
// send
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
// verify
await page.waitForSelector('[data-testid="response-status-tag"]:visible');
await expect(responsePane).toContainText(tc.expectedResult.message);
});
}
});