mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 23:00:30 +00:00
478 lines
18 KiB
TypeScript
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);
|
|
});
|
|
}
|
|
});
|