Skip to content

Commit 7461990

Browse files
authored
Merge pull request #56 from pactumjs/feat/tools-curl-converter
cURL to PactumsJS converter
2 parents 8e5a04a + ba29d61 commit 7461990

File tree

3 files changed

+317
-1
lines changed

3 files changed

+317
-1
lines changed
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
<template>
2+
<div class="converter-container">
3+
<div class="input-section">
4+
<label for="curlInput">Curl Command</label>
5+
<textarea
6+
id="curlInput"
7+
v-model="curlInput"
8+
placeholder="Enter your curl command here..."
9+
rows="5"
10+
></textarea>
11+
12+
<div class="test-runner-select">
13+
<label for="testRunner">Test Runner</label>
14+
<select id="testRunner" v-model="selectedRunner">
15+
<option value="mocha">Mocha</option>
16+
<option value="jest">Jest</option>
17+
<option value="cucumber">Cucumber.js</option>
18+
</select>
19+
</div>
20+
21+
<button class="convert-button" @click="convertCurl">
22+
Convert
23+
</button>
24+
<button class="convert-button clear" @click="clearOutput">
25+
Clear
26+
</button>
27+
</div>
28+
29+
<div class="output-section" v-if="generatedCode">
30+
<div class="output-header">
31+
<h3>Generated Test Code</h3>
32+
<button class="copy-button" @click="copyToClipboard">
33+
Copy Code
34+
</button>
35+
</div>
36+
<pre><code>{{ generatedCode }}</code></pre>
37+
</div>
38+
</div>
39+
</template>
40+
41+
<script setup>
42+
import { ref } from 'vue'
43+
44+
const curlInput = ref('')
45+
const selectedRunner = ref('mocha')
46+
const generatedCode = ref('')
47+
48+
const clearOutput = () => {
49+
generatedCode.value = ''
50+
}
51+
52+
const parseCurl = (curlCommand) => {
53+
if (!curlCommand) {
54+
clearOutput()
55+
return ""
56+
}
57+
const urlMatch = curlCommand.match(/https?:\/\/[^\s"']+/);
58+
const methodMatch = curlCommand.match(/-X\s+(\w+)/);
59+
const headerMatches = curlCommand.match(/-H\s+['"]([^'"]+)['"]/g);
60+
const dataMatch = curlCommand.match(/--data\s+(['"])(.*?)\1/);
61+
62+
const headers = {};
63+
if (headerMatches) {
64+
headerMatches.forEach(header => {
65+
const [key, value] = header.match(/-H\s+['"]([^'"]+)['"]/)[1].split(': ');
66+
headers[key] = value;
67+
});
68+
}
69+
return {
70+
url: urlMatch?.[0] || '',
71+
method: methodMatch?.[1] || 'GET',
72+
headers,
73+
data: dataMatch?.[2] || null
74+
};
75+
}
76+
77+
const generateMochaTest = (curlData) => {
78+
return `const { spec } = require('pactum');
79+
80+
describe('API Test', () => {
81+
it('should make the API call successfully', async () => {
82+
await spec()
83+
.${curlData.method.toLowerCase()}('${curlData.url}')
84+
${Object.keys(curlData.headers).length > 0 ?
85+
`.withHeaders(${JSON.stringify(curlData.headers, null, 8)})` : ''}
86+
${curlData.data ? `.withJson(${curlData.data})` : ''}
87+
.expectStatus(200);
88+
});
89+
});`
90+
}
91+
92+
const generateJestTest = (curlData) => {
93+
return `const { spec } = require('pactum');
94+
95+
describe('API Test', () => {
96+
test('should make the API call successfully', async () => {
97+
await spec()
98+
.${curlData.method.toLowerCase()}('${curlData.url}')
99+
${Object.keys(curlData.headers).length > 0 ?
100+
`.withHeaders(${JSON.stringify(curlData.headers, null, 8)})` : ''}
101+
${curlData.data ? `.withJson(${curlData.data})` : ''}
102+
.expectStatus(200);
103+
});
104+
});`
105+
}
106+
107+
const generateCucumberTest = (curlData) => {
108+
return `// features/api.feature
109+
Feature: API Testing
110+
Scenario: Make API call
111+
When I make an API call
112+
Then I should receive a successful response
113+
114+
// step_definitions/api_steps.js
115+
const { spec } = require('pactum');
116+
117+
When('I make an API call', async function () {
118+
this.response = await spec()
119+
.${curlData.method.toLowerCase()}('${curlData.url}')
120+
${Object.keys(curlData.headers).length > 0 ?
121+
`.withHeaders(${JSON.stringify(curlData.headers, null, 8)})` : ''}
122+
${curlData.data ? `.withJson(${curlData.data})` : ''}
123+
});
124+
125+
Then('I should receive a successful response', async function () {
126+
await this.response.expectStatus(200);
127+
});`
128+
}
129+
130+
const convertCurl = () => {
131+
try {
132+
const curlData = parseCurl(curlInput.value)
133+
if (!curlData.url) {
134+
return
135+
}
136+
let code = ''
137+
138+
switch (selectedRunner.value) {
139+
case 'mocha':
140+
code = generateMochaTest(curlData)
141+
break
142+
case 'jest':
143+
code = generateJestTest(curlData)
144+
break
145+
case 'cucumber':
146+
code = generateCucumberTest(curlData)
147+
break
148+
}
149+
150+
generatedCode.value = code
151+
} catch (error) {
152+
generatedCode.value = `Error parsing curl command: ${error.message}`
153+
}
154+
}
155+
156+
const copyToClipboard = async () => {
157+
try {
158+
await navigator.clipboard.writeText(generatedCode.value)
159+
alert('Code copied to clipboard!')
160+
} catch (err) {
161+
alert('Failed to copy code to clipboard')
162+
}
163+
}
164+
</script>
165+
166+
<style scoped>
167+
.converter-container {
168+
max-width: 800px;
169+
margin: 0 auto;
170+
padding: 20px;
171+
}
172+
173+
.input-section {
174+
margin-bottom: 20px;
175+
}
176+
177+
label {
178+
display: block;
179+
margin-bottom: 8px;
180+
font-weight: 500;
181+
}
182+
183+
textarea {
184+
width: 100%;
185+
padding: 12px;
186+
border: 1px solid var(--vp-c-divider);
187+
border-radius: 8px;
188+
font-family: monospace;
189+
margin-bottom: 16px;
190+
}
191+
192+
.test-runner-select {
193+
margin-bottom: 16px;
194+
}
195+
196+
select {
197+
width: 200px;
198+
padding: 8px;
199+
border: 1px solid var(--vp-c-divider);
200+
border-radius: 6px;
201+
background-color: var(--vp-c-bg);
202+
}
203+
204+
.convert-button {
205+
background-color: var(--vp-c-brand);
206+
color: white;
207+
padding: 8px 16px;
208+
border: none;
209+
border-radius: 6px;
210+
cursor: pointer;
211+
font-weight: 500;
212+
}
213+
214+
.convert-button:hover {
215+
background-color: var(--vp-c-brand-dark);
216+
}
217+
218+
.convert-button.clear {
219+
background-color: var(--vp-c-bg-mute);
220+
}
221+
222+
223+
.output-section {
224+
background-color: var(--vp-c-bg-soft);
225+
border-radius: 8px;
226+
padding: 16px;
227+
}
228+
229+
.output-header {
230+
display: flex;
231+
justify-content: space-between;
232+
align-items: center;
233+
margin-bottom: 12px;
234+
}
235+
236+
.copy-button {
237+
background-color: var(--vp-c-brand-soft);
238+
color: var(--vp-c-brand);
239+
padding: 4px 12px;
240+
border: none;
241+
border-radius: 4px;
242+
cursor: pointer;
243+
font-size: 14px;
244+
}
245+
246+
.copy-button:hover {
247+
background-color: var(--vp-c-brand-soft-hover);
248+
}
249+
250+
pre {
251+
margin: 0;
252+
padding: 16px;
253+
background-color: var(--vp-c-bg-alt);
254+
border-radius: 6px;
255+
overflow-x: auto;
256+
}
257+
258+
code {
259+
font-family: monospace;
260+
font-size: 14px;
261+
line-height: 1.5;
262+
}
263+
</style>

docs/.vitepress/config.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ const home_sidebar = [
4848
}
4949
];
5050

51+
const tools_sidebar = [
52+
{
53+
text: '🪄 Tools and Utilities',
54+
collapsed: false,
55+
items: [
56+
{ text: 'cURL-To-PactumJS', link: '/tools/converter', },
57+
]
58+
}
59+
]
60+
5161
const api_sidebar = [
5262
{
5363
text: '🪄 Requests',
@@ -286,6 +296,7 @@ const config = defineConfig({
286296
nav: [
287297
{ text: '🏠 Home', link: '/introduction/welcome', activeMatch: '/guides/'},
288298
{ text: '⇌ API', link: '/api/requests/spec', activeMatch: '/api/' },
299+
{ text: '⚒️ Tools', link: '/tools/converter', activeMatch: '/tools/' },
289300
{ text: '📑 v3.x.x',
290301
items: [
291302
{ text: 'Github Releases', link: releases },
@@ -298,7 +309,8 @@ const config = defineConfig({
298309
'/introduction': home_sidebar,
299310
'/guides': home_sidebar,
300311
'/media': home_sidebar,
301-
'/api': api_sidebar
312+
'/api': api_sidebar,
313+
'/tools': tools_sidebar
302314
},
303315
footer: {
304316
message: 'Released under the MIT License.',

docs/tools/converter.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# cURL to PactumJS Converter
2+
3+
::: warning WARNING
4+
cURL to PactumJS converter is still in Beta/experimental mode. Not all features might work as expected. If you see any issues, please start a discussion [here](https://github.com/pactumjs/pactum/discussions/new/choose)
5+
:::
6+
7+
8+
Convert your simple cURL commands to PactumJS tests with support for multiple test runners.
9+
10+
## Usage Instructions
11+
12+
1. Paste your cURL command in the input field
13+
2. Select your preferred test runner (Mocha, Jest, or Cucumber.js)
14+
3. Click "Convert" to generate the test code
15+
4. Copy the generated code using the "Copy Code" button
16+
17+
## cURL Converter
18+
19+
<script setup>
20+
import Converter from '../.vitepress/components/Converter.vue'
21+
</script>
22+
<Converter />
23+
24+
## Supported Features
25+
26+
- HTTP Methods (GET, POST, PUT, DELETE, etc.)
27+
- Headers
28+
- Request Body
29+
- Multiple test runner support:
30+
- Mocha with Chai assertions
31+
- Jest
32+
- Cucumber.js
33+
34+
## Example cURL Command
35+
36+
```bash
37+
curl -X POST "https://api.example.com/data" \
38+
-H "Content-Type: application/json" \
39+
-H "Authorization: Bearer token123" \
40+
--data '{"key": "value"}'
41+
```

0 commit comments

Comments
 (0)