Skip to content

Commit d55372a

Browse files
starry0811claude
authored andcommitted
test: add core workflow smoke tests covering request CRUD lifecycle
Add Playwright E2E smoke tests that cover Insomnia primary end-to-end workflows: GET with env-var URL substitution, POST/201 creation, full CRUD chain using an after-response script to capture and reuse an ID, custom header propagation verified via echo endpoint, basic auth, manual request creation via the UI URL bar, and environment switching. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a161c95 commit d55372a

2 files changed

Lines changed: 346 additions & 0 deletions

File tree

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
type: collection.insomnia.rest/5.0
2+
schema_version: "5.1"
3+
name: Core Workflow Tests
4+
meta:
5+
id: wrk_f0000000000000000000000000000001
6+
created: 1744502400000
7+
modified: 1744502400000
8+
collection:
9+
- url: "{{_.base_url}}/pets/{{_.pet_id}}"
10+
name: get pet by id
11+
meta:
12+
id: req_f0000000000000000000000000000005
13+
created: 1744502400001
14+
modified: 1744502400001
15+
isPrivate: false
16+
sortKey: -1744502400001
17+
method: GET
18+
settings:
19+
renderRequestBody: true
20+
encodeUrl: true
21+
followRedirects: global
22+
cookies:
23+
send: true
24+
store: true
25+
rebuildPath: true
26+
- url: "{{_.base_url}}/simple-crud"
27+
name: create item
28+
meta:
29+
id: req_f0000000000000000000000000000006
30+
created: 1744502400002
31+
modified: 1744502400002
32+
isPrivate: false
33+
sortKey: -1744502400002
34+
method: POST
35+
body:
36+
mimeType: application/json
37+
text: |-
38+
{
39+
"name": "Widget",
40+
"price": 9.99
41+
}
42+
headers:
43+
- name: Content-Type
44+
value: application/json
45+
scripts:
46+
afterResponse: |-
47+
const json = JSON.parse(insomnia.response.text());
48+
insomnia.environment.set('item_id', json.id);
49+
insomnia.test('response has id', () => {
50+
insomnia.expect(typeof json.id).to.eql('string');
51+
});
52+
insomnia.test('response has correct name', () => {
53+
insomnia.expect(json.name).to.eql('Widget');
54+
});
55+
settings:
56+
renderRequestBody: true
57+
encodeUrl: true
58+
followRedirects: global
59+
cookies:
60+
send: true
61+
store: true
62+
rebuildPath: true
63+
- url: "{{_.base_url}}/simple-crud/{{_.item_id}}"
64+
name: get created item
65+
meta:
66+
id: req_f0000000000000000000000000000007
67+
created: 1744502400003
68+
modified: 1744502400003
69+
isPrivate: false
70+
sortKey: -1744502400003
71+
method: GET
72+
settings:
73+
renderRequestBody: true
74+
encodeUrl: true
75+
followRedirects: global
76+
cookies:
77+
send: true
78+
store: true
79+
rebuildPath: true
80+
- url: "{{_.base_url}}/echo"
81+
name: echo with custom header
82+
meta:
83+
id: req_f0000000000000000000000000000008
84+
created: 1744502400004
85+
modified: 1744502400004
86+
isPrivate: false
87+
sortKey: -1744502400004
88+
method: POST
89+
body:
90+
mimeType: application/json
91+
text: '{"workflow":"test"}'
92+
headers:
93+
- name: Content-Type
94+
value: application/json
95+
- name: x-test-header
96+
value: insomnia-rocks
97+
settings:
98+
renderRequestBody: true
99+
encodeUrl: true
100+
followRedirects: global
101+
cookies:
102+
send: true
103+
store: true
104+
rebuildPath: true
105+
- url: "{{_.base_url}}/auth/basic"
106+
name: authenticated request
107+
meta:
108+
id: req_f0000000000000000000000000000009
109+
created: 1744502400005
110+
modified: 1744502400005
111+
isPrivate: false
112+
sortKey: -1744502400005
113+
method: GET
114+
authentication:
115+
type: basic
116+
useISO88591: false
117+
disabled: false
118+
username: user
119+
password: pass
120+
settings:
121+
renderRequestBody: true
122+
encodeUrl: true
123+
followRedirects: global
124+
cookies:
125+
send: true
126+
store: true
127+
rebuildPath: true
128+
cookieJar:
129+
name: Default Jar
130+
meta:
131+
id: jar_f0000000000000000000000000000002
132+
created: 1744502400000
133+
modified: 1744502400000
134+
environments:
135+
name: Base Environment
136+
meta:
137+
id: env_f0000000000000000000000000000003
138+
created: 1744502400000
139+
modified: 1744502400000
140+
isPrivate: false
141+
data:
142+
base_url: http://127.0.0.1:4010
143+
pet_id: "42"
144+
item_id: placeholder
145+
subEnvironments:
146+
- name: Staging
147+
meta:
148+
id: env_f0000000000000000000000000000004
149+
created: 1744502400000
150+
modified: 1744502400000
151+
isPrivate: false
152+
data:
153+
pet_id: "1"
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import { expect } from '@playwright/test';
2+
3+
import { loadFixture } from '../../playwright/paths';
4+
import { test } from '../../playwright/test';
5+
6+
/**
7+
* Core Workflow Tests
8+
*
9+
* Covers the primary end-to-end Insomnia workflows:
10+
* 1. Sending a GET request with environment-variable URL substitution
11+
* 2. Creating a resource via POST and verifying the 201 response
12+
* 3. Full CRUD chain: POST to create → after-response script captures ID → GET retrieves item
13+
* 4. Custom request headers propagated and reflected in an echo response
14+
* 5. Basic authentication applied to a protected endpoint
15+
* 6. Creating a new HTTP request manually from the UI and sending it
16+
*
17+
* Mock server endpoints used (all in packages/insomnia-smoke-test/server/):
18+
* GET /pets/:id → 200 { id }
19+
* POST /simple-crud → 201 { id, ...body }
20+
* GET /simple-crud/:id → 200 { id, ...body } | 404
21+
* POST /echo → 200 { method, headers, data, cookies }
22+
* GET /auth/basic → 200 (requires Basic user:pass)
23+
*/
24+
25+
test.describe('Core Workflow', () => {
26+
test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms');
27+
28+
test.beforeEach(async ({ app, page }) => {
29+
const text = await loadFixture('core-workflow.yaml');
30+
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
31+
32+
await page.getByLabel('Import').click();
33+
await page.locator('[data-test-id="import-from-clipboard"]').click();
34+
await page.getByRole('button', { name: 'Scan' }).click();
35+
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
36+
});
37+
38+
// ─── 1. GET with environment-variable substitution ────────────────────────
39+
40+
test('sends GET request with env var substitution and verifies JSON response', async ({ page }) => {
41+
const statusTag = page.locator('[data-testid="response-status-tag"]:visible');
42+
const responsePane = page.getByTestId('response-pane');
43+
44+
// Select the request and send it
45+
await page.getByLabel('Request Collection').getByTestId('get pet by id').press('Enter');
46+
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
47+
48+
// Status: 200 OK
49+
await expect.soft(statusTag).toContainText('200 OK');
50+
51+
// Body: { "id": "42" } — pet_id env var resolved to "42"
52+
await expect.soft(responsePane).toContainText('"id"');
53+
await expect.soft(responsePane).toContainText('"42"');
54+
55+
// Response Headers: content-type should be application/json
56+
await responsePane.getByRole('tab', { name: 'Headers' }).click();
57+
await expect.soft(responsePane).toContainText('content-type');
58+
});
59+
60+
// ─── 2. POST → 201 Created ────────────────────────────────────────────────
61+
62+
test('creates resource via POST and verifies 201 Created with body', async ({ page }) => {
63+
const statusTag = page.locator('[data-testid="response-status-tag"]:visible');
64+
const responsePane = page.getByTestId('response-pane');
65+
66+
await page.getByLabel('Request Collection').getByTestId('create item').press('Enter');
67+
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
68+
69+
// Status: 201 Created
70+
await expect.soft(statusTag).toContainText('201');
71+
72+
// Body includes the server-assigned id and the payload we sent
73+
await expect.soft(responsePane).toContainText('"id"');
74+
await expect.soft(responsePane).toContainText('"Widget"');
75+
await expect.soft(responsePane).toContainText('9.99');
76+
77+
// After-response script assertions are visible in the Tests tab
78+
await responsePane.getByRole('tab', { name: 'Tests' }).click();
79+
const testRows = page.getByTestId('test-result-row');
80+
await expect.soft(testRows.nth(0)).toContainText('PASS');
81+
await expect.soft(testRows.nth(1)).toContainText('PASS');
82+
});
83+
84+
// ─── 3. Full CRUD chain: POST create → GET retrieve ───────────────────────
85+
86+
test('performs full CRUD workflow: creates then retrieves resource', async ({ page }) => {
87+
const statusTag = page.locator('[data-testid="response-status-tag"]:visible');
88+
const responsePane = page.getByTestId('response-pane');
89+
90+
// Step 1: Create – after-response script sets item_id in the environment
91+
await page.getByLabel('Request Collection').getByTestId('create item').press('Enter');
92+
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
93+
await expect.soft(statusTag).toContainText('201');
94+
95+
// Step 2: Retrieve – URL uses {{_.item_id}} injected by the script above
96+
await page.getByLabel('Request Collection').getByTestId('get created item').press('Enter');
97+
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
98+
99+
// Status: 200 OK
100+
await expect.soft(statusTag).toContainText('200 OK');
101+
102+
// Body must contain the data we originally POSTed
103+
await expect.soft(responsePane).toContainText('"Widget"');
104+
await expect.soft(responsePane).toContainText('9.99');
105+
});
106+
107+
// ─── 4. Custom request headers reflected in echo response ─────────────────
108+
109+
test('sends POST with custom headers and verifies echo response', async ({ page }) => {
110+
const statusTag = page.locator('[data-testid="response-status-tag"]:visible');
111+
const responsePane = page.getByTestId('response-pane');
112+
113+
await page.getByLabel('Request Collection').getByTestId('echo with custom header').press('Enter');
114+
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
115+
116+
// Status: 200 OK
117+
await expect.soft(statusTag).toContainText('200 OK');
118+
119+
// Echo server mirrors the HTTP method back in the body
120+
await expect.soft(responsePane).toContainText('"POST"');
121+
122+
// Our custom header value must appear in the echoed headers object
123+
await expect.soft(responsePane).toContainText('insomnia-rocks');
124+
125+
// The request body is also echoed under the "data" key
126+
await expect.soft(responsePane).toContainText('"workflow"');
127+
});
128+
129+
// ─── 5. Basic authentication ──────────────────────────────────────────────
130+
131+
test('sends authenticated request and verifies 200 OK', async ({ page }) => {
132+
const statusTag = page.locator('[data-testid="response-status-tag"]:visible');
133+
134+
await page.getByLabel('Request Collection').getByTestId('authenticated request').press('Enter');
135+
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
136+
137+
// The basic-auth endpoint returns 200 only when credentials are valid
138+
await expect.soft(statusTag).toContainText('200 OK');
139+
});
140+
141+
// ─── 6. Create new HTTP request from scratch via the UI ───────────────────
142+
143+
test('creates a new HTTP request from the UI, sets a URL, and sends it', async ({ page }) => {
144+
const statusTag = page.locator('[data-testid="response-status-tag"]:visible');
145+
const responsePane = page.getByTestId('response-pane');
146+
147+
// Create a new blank HTTP request inside the collection
148+
await page.getByLabel('Create in collection').click();
149+
await page.getByRole('menuitemradio', { name: 'Http Request' }).click();
150+
151+
// The new request row should be selected and the request pane visible
152+
const newRequest = page.getByLabel('Request Collection').getByRole('row', { name: 'New Request' }).first();
153+
await expect.soft(newRequest.locator('[data-selected="true"]').first()).toBeVisible();
154+
155+
// Enter a URL in the URL bar (OneLineEditor at the top of the request pane)
156+
const urlBar = page.getByTestId('request-pane').getByTestId('OneLineEditor').first().getByRole('textbox');
157+
await urlBar.click();
158+
await page.keyboard.type('http://127.0.0.1:4010/pets/99');
159+
await page.keyboard.press('Tab');
160+
161+
// Send the request
162+
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
163+
164+
// Status: 200 OK
165+
await expect.soft(statusTag).toContainText('200 OK');
166+
167+
// The mock server echoes back the path parameter as the id
168+
await expect.soft(responsePane).toContainText('"id"');
169+
await expect.soft(responsePane).toContainText('"99"');
170+
});
171+
172+
// ─── 7. Environment switching changes the resolved URL ────────────────────
173+
174+
test('switches to Staging environment and sends GET with different pet_id', async ({ page }) => {
175+
const statusTag = page.locator('[data-testid="response-status-tag"]:visible');
176+
const responsePane = page.getByTestId('response-pane');
177+
178+
// Switch the active sub-environment to "Staging" (pet_id = "1")
179+
await page.getByRole('button', { name: 'Manage Environments' }).click();
180+
await page.getByRole('option', { name: 'Staging' }).press('Enter');
181+
await page.getByRole('option', { name: 'Staging' }).press('Escape');
182+
183+
// Send the same GET /pets/{{pet_id}} request
184+
await page.getByLabel('Request Collection').getByTestId('get pet by id').press('Enter');
185+
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
186+
187+
// Status: 200 OK
188+
await expect.soft(statusTag).toContainText('200 OK');
189+
190+
// pet_id from the Staging env is "1", not the base env "42"
191+
await expect.soft(responsePane).toContainText('"1"');
192+
});
193+
});

0 commit comments

Comments
 (0)