Skip to content

Commit 3620c54

Browse files
authored
feat: support to import native data (#539)
* feat: support to import native data * add unit tests --------- Co-authored-by: rick <[email protected]>
1 parent 834a28f commit 3620c54

File tree

13 files changed

+709
-380
lines changed

13 files changed

+709
-380
lines changed

console/atest-ui/src/views/TestSuite.vue

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const suite = ref({
3434
}
3535
}
3636
} as Suite)
37+
const shareLink = ref('')
3738
function load() {
3839
const store = Cache.GetCurrentStore()
3940
if (!props.name || store.name === '') return
@@ -48,6 +49,8 @@ function load() {
4849
value: ''
4950
} as Pair)
5051
}
52+
53+
shareLink.value = `${window.location.href}api/v1/suites/${e.name}/yaml?store=${store.name}`
5154
},
5255
(e) => {
5356
ElMessage.error('Oops, ' + e)
@@ -225,7 +228,11 @@ const yamlDialogVisible = ref(false)
225228
function viewYaml() {
226229
yamlDialogVisible.value = true
227230
API.GetTestSuiteYaml(props.name, (d) => {
228-
yamlFormat.value = yaml.dump(yaml.load(atob(d.data)))
231+
try {
232+
yamlFormat.value = yaml.dump(yaml.load(atob(d.data)))
233+
} catch (e) {
234+
ElMessage.error('Oops, ' + e)
235+
}
229236
})
230237
}
231238
@@ -330,6 +337,9 @@ const targetSuiteDuplicateName = ref('')
330337
<el-divider />
331338
</div>
332339

340+
<div class="button-container">
341+
Share link: <el-input readonly v-model="shareLink" style="width: 80%" />
342+
</div>
333343
<div class="button-container">
334344
<el-button type="primary" @click="save" v-if="!Cache.GetCurrentStore().readOnly">{{
335345
t('button.save')

console/atest-ui/src/views/TestingPanel.vue

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,8 @@ const testSuiteForm = reactive({
242242
const importSuiteFormRef = ref<FormInstance>()
243243
const importSuiteForm = reactive({
244244
url: '',
245-
store: ''
245+
store: '',
246+
kind: ''
246247
})
247248
248249
function openTestSuiteCreateDialog() {
@@ -290,7 +291,8 @@ const importSuiteFormRules = reactive<FormRules<Suite>>({
290291
{ required: true, message: 'URL is required', trigger: 'blur' },
291292
{ type: 'url', message: 'Should be a valid URL value', trigger: 'blur' }
292293
],
293-
store: [{ required: true, message: 'Location is required', trigger: 'blur' }]
294+
store: [{ required: true, message: 'Location is required', trigger: 'blur' }],
295+
kind: [{ required: true, message: 'Kind is required', trigger: 'blur' }]
294296
})
295297
const importSuiteFormSubmit = async (formEl: FormInstance | undefined) => {
296298
if (!formEl) return
@@ -302,6 +304,8 @@ const importSuiteFormSubmit = async (formEl: FormInstance | undefined) => {
302304
loadStores()
303305
importDialogVisible.value = false
304306
formEl.resetFields()
307+
}, (e) => {
308+
ElMessage.error(e)
305309
})
306310
}
307311
})
@@ -348,6 +352,14 @@ const suiteKinds = [{
348352
"name": "tRPC",
349353
}]
350354
355+
const importSourceKinds = [{
356+
"name": "Postman",
357+
"value": "postman"
358+
}, {
359+
"name": "Native",
360+
"value": "native"
361+
}]
362+
351363
</script>
352364

353365
<template>
@@ -470,7 +482,6 @@ const suiteKinds = [{
470482
</el-dialog>
471483

472484
<el-dialog v-model="importDialogVisible" title="Import Test Suite" width="30%" draggable>
473-
<span>Supported source URL: Postman collection share link</span>
474485
<template #footer>
475486
<span class="dialog-footer">
476487
<el-form
@@ -492,6 +503,20 @@ const suiteKinds = [{
492503
/>
493504
</el-select>
494505
</el-form-item>
506+
<el-form-item label="Kind" prop="kind">
507+
<el-select v-model="importSuiteForm.kind" class="m-2"
508+
filterable=true
509+
test-id="suite-import-form-kind"
510+
default-first-option=true
511+
placeholder="Kind" size="middle">
512+
<el-option
513+
v-for="item in importSourceKinds"
514+
:key="item.name"
515+
:label="item.name"
516+
:value="item.value"
517+
/>
518+
</el-select>
519+
</el-form-item>
495520
<el-form-item label="URL" prop="url">
496521
<el-input v-model="importSuiteForm.url" test-id="suite-import-form-api" placeholder="https://api.postman.com/collections/xxx" />
497522
</el-form-item>

console/atest-ui/src/views/net.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ function CreateTestSuite(suite: TestSuite,
8181
interface ImportSource {
8282
store: string
8383
url: string
84+
kind: string
8485
}
8586

8687
function UpdateTestSuite(suite: any,
@@ -145,7 +146,7 @@ function ConvertTestSuite(suiteName: string, genertor: string,
145146
}
146147

147148
function DuplicateTestSuite(sourceSuiteName: string, targetSuiteName: string,
148-
callback: (d: any) => void, errHandle?: ((reason: any) => PromiseLike<never>) | undefined | null ) {
149+
callback: (d: any) => void, errHandle?: ((reason: any) => PromiseLike<never>) | undefined | null) {
149150
const requestOptions = {
150151
method: 'POST',
151152
headers: {
@@ -162,21 +163,19 @@ function DuplicateTestSuite(sourceSuiteName: string, targetSuiteName: string,
162163
.then(callback).catch(errHandle)
163164
}
164165

165-
function ImportTestSuite(source: ImportSource, callback: (d: any) => void) {
166+
function ImportTestSuite(source: ImportSource, callback: (d: any) => void,
167+
errHandle?: (e: any) => void | null) {
166168
const requestOptions = {
167169
method: 'POST',
168170
headers: {
169171
'X-Store-Name': source.store,
170172
'X-Auth': getToken()
171173
},
172-
body: JSON.stringify({
173-
url: source.url
174-
})
174+
body: JSON.stringify(source)
175175
}
176176

177-
fetch(`/api/v1/suites/import`, requestOptions)
178-
.then(DefaultResponseProcess)
179-
.then(callback)
177+
fetch(`/api/v1/suites/import`, requestOptions).
178+
then(DefaultResponseProcess).then(callback).catch(errHandle)
180179
}
181180

182181
interface TestCase {

pkg/generator/importer.go

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ package generator
1818

1919
import (
2020
"encoding/json"
21-
"io"
22-
"net/http"
23-
"os"
2421

2522
"github.com/linuxsuren/api-testing/pkg/testing"
2623
)
@@ -92,8 +89,12 @@ func (p Paris) ToMap() (result map[string]string) {
9289
return
9390
}
9491

95-
type Importer interface {
92+
type DataImporter interface {
9693
Convert(data []byte) (*testing.TestSuite, error)
94+
}
95+
96+
type Importer interface {
97+
DataImporter
9798
ConvertFromFile(dataFile string) (*testing.TestSuite, error)
9899
ConvertFromURL(dataURL string) (*testing.TestSuite, error)
99100
}
@@ -122,13 +123,18 @@ func (p *postmanImporter) Convert(data []byte) (suite *testing.TestSuite, err er
122123

123124
suite = &testing.TestSuite{}
124125
suite.Name = postman.Info.Name
125-
if err = p.convertItems(postman.Item, "", suite); err != nil {
126-
return
127-
}
128-
126+
err = p.convertItems(postman.Item, "", suite)
129127
return
130128
}
131129

130+
func (p *postmanImporter) ConvertFromFile(dataFile string) (*testing.TestSuite, error) {
131+
return convertFromFile(dataFile, p)
132+
}
133+
134+
func (p *postmanImporter) ConvertFromURL(dataURLStr string) (*testing.TestSuite, error) {
135+
return convertFromURL(dataURLStr, p)
136+
}
137+
132138
func (p *postmanImporter) convertItems(items []PostmanItem, prefix string, suite *testing.TestSuite) (err error) {
133139
for _, item := range items {
134140
itemName := prefix + item.Name
@@ -150,22 +156,3 @@ func (p *postmanImporter) convertItems(items []PostmanItem, prefix string, suite
150156
}
151157
return
152158
}
153-
154-
func (p *postmanImporter) ConvertFromFile(dataFile string) (suite *testing.TestSuite, err error) {
155-
var data []byte
156-
if data, err = os.ReadFile(dataFile); err == nil {
157-
suite, err = p.Convert(data)
158-
}
159-
return
160-
}
161-
162-
func (p *postmanImporter) ConvertFromURL(dataURL string) (suite *testing.TestSuite, err error) {
163-
var resp *http.Response
164-
if resp, err = http.Get(dataURL); err == nil {
165-
var data []byte
166-
if data, err = io.ReadAll(resp.Body); err == nil {
167-
suite, err = p.Convert(data)
168-
}
169-
}
170-
return
171-
}

pkg/generator/importer_native.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
Copyright 2024 API Testing Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package generator
18+
19+
import (
20+
"encoding/base64"
21+
"encoding/json"
22+
"io"
23+
"net/http"
24+
"net/url"
25+
"os"
26+
27+
"github.com/linuxsuren/api-testing/pkg/testing"
28+
)
29+
30+
type nativeImporter struct {
31+
}
32+
33+
type nativeData struct {
34+
Data string `json:"data"`
35+
}
36+
37+
func NewNativeImporter() Importer {
38+
return &nativeImporter{}
39+
}
40+
41+
func (p *nativeImporter) Convert(data []byte) (suite *testing.TestSuite, err error) {
42+
nativeData := nativeData{}
43+
if err = json.Unmarshal(data, &nativeData); err == nil {
44+
var data []byte
45+
if data, err = base64.StdEncoding.DecodeString(nativeData.Data); err == nil {
46+
suite, err = testing.Parse(data)
47+
}
48+
}
49+
return
50+
}
51+
52+
func (p *nativeImporter) ConvertFromFile(dataFile string) (*testing.TestSuite, error) {
53+
return convertFromFile(dataFile, p)
54+
}
55+
56+
func (p *nativeImporter) ConvertFromURL(dataURLStr string) (*testing.TestSuite, error) {
57+
return convertFromURL(dataURLStr, p)
58+
}
59+
60+
func convertFromFile(dataFile string, dataImport DataImporter) (suite *testing.TestSuite, err error) {
61+
var data []byte
62+
if data, err = os.ReadFile(dataFile); err == nil {
63+
suite, err = dataImport.Convert(data)
64+
}
65+
return
66+
}
67+
68+
func convertFromURL(dataURLStr string, dataImport DataImporter) (suite *testing.TestSuite, err error) {
69+
var req *http.Request
70+
var resp *http.Response
71+
var dataURL *url.URL
72+
73+
if dataURL, err = url.Parse(dataURLStr); err == nil {
74+
req, err = http.NewRequest(http.MethodGet, dataURLStr, nil)
75+
}
76+
77+
if err == nil {
78+
// put all query params as headers
79+
for k, v := range dataURL.Query() {
80+
req.Header.Add(k, v[0])
81+
}
82+
83+
if resp, err = http.DefaultClient.Do(req); err == nil {
84+
var data []byte
85+
if data, err = io.ReadAll(resp.Body); err == nil {
86+
suite, err = dataImport.Convert(data)
87+
}
88+
}
89+
}
90+
return
91+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
Copyright 2024 API Testing Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package generator
17+
18+
import (
19+
"bytes"
20+
"net/http"
21+
"testing"
22+
23+
"github.com/h2non/gock"
24+
"github.com/stretchr/testify/assert"
25+
26+
_ "embed"
27+
)
28+
29+
func TestNativeImporter(t *testing.T) {
30+
importer := NewNativeImporter()
31+
32+
t.Run("simple native, from []byte", func(t *testing.T) {
33+
_, err := importer.Convert(simpleNativeData)
34+
assert.NoError(t, err)
35+
})
36+
37+
t.Run("read from file", func(t *testing.T) {
38+
_, err := importer.ConvertFromFile("testdata/native.json")
39+
assert.NoError(t, err)
40+
})
41+
42+
t.Run("read from URL", func(t *testing.T) {
43+
defer gock.Off()
44+
gock.New(urlFoo).Get("/").Reply(http.StatusOK).Body(bytes.NewBuffer(simpleNativeData))
45+
46+
_, err := importer.ConvertFromURL(urlFoo)
47+
assert.NoError(t, err)
48+
})
49+
}
50+
51+
//go:embed testdata/native.json
52+
var simpleNativeData []byte

0 commit comments

Comments
 (0)