Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support manager store on UI #161

Merged
merged 1 commit into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 53 additions & 40 deletions console/atest-ui/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<script setup lang="ts">
import TestCase from './views/TestCase.vue'
import TestSuite from './views/TestSuite.vue'
import StoreManager from './views/StoreManager.vue'
import TemplateFunctions from './views/TemplateFunctions.vue'
import { reactive, ref, watch } from 'vue'
import { ElTree } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { Edit } from '@element-plus/icons-vue'
import { Edit, Share } from '@element-plus/icons-vue'
import type { Suite } from './types'

interface Tree {
Expand Down Expand Up @@ -61,11 +62,11 @@ const data = ref([] as Tree[])
const treeRef = ref<InstanceType<typeof ElTree>>()
const currentNodekey = ref('')

function loadTestSuites(store: string) {
function loadTestSuites(storeName: string) {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': store
'X-Store-Name': storeName
},
}
fetch('/server.Runner/GetSuites', requestOptions)
Expand All @@ -78,15 +79,15 @@ function loadTestSuites(store: string) {
let suite = {
id: k,
label: k,
store: store,
store: storeName,
children: [] as Tree[]
} as Tree

d.data[k].data.forEach((item: any) => {
suite.children?.push({
id: k + item,
label: item,
store: store,
store: storeName,
parent: k
} as Tree)
})
Expand All @@ -105,6 +106,7 @@ function loadTestSuites(store: string) {

viewName.value = 'testsuite'
testSuite.value = firstItem.label
store.value = firstItem.store
}
})
}
Expand Down Expand Up @@ -196,43 +198,54 @@ const viewName = ref('testcase')

<template>
<div class="common-layout" data-title="Welcome!" data-intro="Welcome to use api-testing! 👋">
<el-container style="height: 100%">
<el-aside width="200px">
<el-button type="primary" @click="openTestSuiteCreateDialog"
data-intro="Click here to create a new test suite"
test-id="open-new-suite-dialog" :icon="Edit">New</el-button>
<el-input v-model="filterText" placeholder="Filter keyword" test-id="search" />

<el-tree
:data=data
highlight-current
:check-on-click-node="true"
:expand-on-click-node="false"
:current-node-key="currentNodekey"
ref="treeRef"
node-key="id"
:filter-node-method="filterTestCases"
@node-click="handleNodeClick"
data-intro="This is the test suite tree. You can click the test suite to edit it."
/>
</el-aside>
<el-container>
<el-header style="height: 30px;justify-content: flex-end;">
<el-button type="primary" :icon="Share" @click="viewName = ''" />
</el-header>

<el-main>
<TestCase
v-if="viewName === 'testcase'"
:store="store"
:suite="testSuite"
:name="testCaseName"
@updated="loadStores"
data-intro="This is the test case editor. You can edit the test case here."
/>
<TestSuite
v-else-if="viewName === 'testsuite'"
:name="testSuite"
:store="store"
@updated="loadStores"
data-intro="This is the test suite editor. You can edit the test suite here."
/>
<el-container style="height: 100%">
<el-aside width="200px">
<el-button type="primary" @click="openTestSuiteCreateDialog"
data-intro="Click here to create a new test suite"
test-id="open-new-suite-dialog" :icon="Edit">New</el-button>
<el-input v-model="filterText" placeholder="Filter keyword" test-id="search" />

<el-tree
:data=data
highlight-current
:check-on-click-node="true"
:expand-on-click-node="false"
:current-node-key="currentNodekey"
ref="treeRef"
node-key="id"
:filter-node-method="filterTestCases"
@node-click="handleNodeClick"
data-intro="This is the test suite tree. You can click the test suite to edit it."
/>
</el-aside>

<el-main>
<TestCase
v-if="viewName === 'testcase'"
:store="store"
:suite="testSuite"
:name="testCaseName"
@updated="loadStores"
data-intro="This is the test case editor. You can edit the test case here."
/>
<TestSuite
v-else-if="viewName === 'testsuite' && testSuite !== ''"
:name="testSuite"
:store="store"
@updated="loadStores"
data-intro="This is the test suite editor. You can edit the test suite here."
/>
<StoreManager
v-else-if="viewName === '' || testSuite === '' || store === ''"
/>
</el-main>
</el-container>
</el-main>
</el-container>
</div>
Expand Down
234 changes: 234 additions & 0 deletions console/atest-ui/src/views/StoreManager.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import { reactive, ref, watch } from 'vue'
import { Edit, Delete } from '@element-plus/icons-vue'
import type { FormInstance, FormRules } from 'element-plus'
import type { Pair } from './types'

const stores = ref([] as Store[])
const dialogVisible = ref(false)
const creatingLoading = ref(false)
const storeFormRef = ref<FormInstance>()
const store = ref({
kind: {},
properties: [{
key: '',
value: ''
}]
} as Store)
const createAction = ref(true)
const storeForm = reactive(store)

interface Store {
name: string
url: string
username: string
password: string
ready: boolean
kind: {
url: string
}
properties: Pair[]
}

function loadStores() {
const requestOptions = {
method: 'POST',
}
fetch('/server.Runner/GetStores', requestOptions)
.then((response) => response.json())
.then((e) => {
stores.value = e.data
})
.catch((e) => {
ElMessage.error('Oops, ' + e)
})
}
loadStores()

function deleteStore(name: string) {
const requestOptions = {
method: 'POST',
body: JSON.stringify({
name: name
})
}
fetch('/server.Runner/DeleteStore', requestOptions)
.then((response) => response.json())
.then((e) => {
ElMessage({
message: 'Deleted.',
type: 'success'
})
loadStores()
})
.catch((e) => {
ElMessage.error('Oops, ' + e)
})
}

function editStore(name: string) {
dialogVisible.value = true
stores.value.forEach((e: Store) => {
if (e.name === name) {
store.value = e
}
})
createAction.value = false
}

function addStore() {
dialogVisible.value = true
createAction.value = true
}

const rules = reactive<FormRules<Store>>({
name: [{ required: true, message: 'Name is required', trigger: 'blur' }]
})
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid: boolean, fields) => {
if (valid) {
creatingLoading.value = true

// remove empty pair
store.value.properties = store.value.properties.filter(
(e) => e.key !== ''
)

const requestOptions = {
method: 'POST',
body: JSON.stringify(store.value)
}

let api = '/server.Runner/CreateStore'
if (!createAction.value) {
api = '/server.Runner/UpdateStore'
}

fetch(api, requestOptions)
.then((response) => response.json())
.then(() => {
creatingLoading.value = false
loadStores()
dialogVisible.value = false
formEl.resetFields()
})
}
})
}

function updateKeys() {
const props = store.value.properties
let lastItem = props[props.length - 1]
if (lastItem.key !== '') {
store.value.properties.push({
key: '',
value: ''
})
}
}
</script>

<template>
<div>Store Manager</div>
<div>
<el-button type="primary" @click="addStore" :icon="Edit">New</el-button>
</div>
<el-table :data="stores" style="width: 100%">
<el-table-column label="Name" width="180">
<template #default="scope">
<el-input v-model="scope.row.name" placeholder="Name"/>
</template>
</el-table-column>
<el-table-column label="URL">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-input v-model="scope.row.url" placeholder="URL" />
</div>
</template>
</el-table-column>
<el-table-column label="Plugin">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-input v-model="scope.row.kind.url" placeholder="Plugin" />
</div>
</template>
</el-table-column>
<el-table-column label="Status" width="100">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-text class="mx-1"
type="success"
v-if="scope.row.ready"
>Ready</el-text>
<el-text class="mx-1"
type="warning"
v-if="!scope.row.ready"
>Not Ready</el-text>
</div>
</template>
</el-table-column>
<el-table-column label="Operations" width="220">
<template #default="scope">
<div style="display: flex; align-items: center" v-if="scope.row.name !== 'local'">
<el-button type="primary" @click="deleteStore(scope.row.name)" :icon="Delete">Delete</el-button>
<el-button type="primary" @click="editStore(scope.row.name)" :icon="Edit">Edit</el-button>
</div>
</template>
</el-table-column>
</el-table>

<el-dialog v-model="dialogVisible" title="Create Store" width="30%" draggable>
<template #footer>
<span class="dialog-footer">
<el-form
:rules="rules"
:model="storeForm"
ref="storeFormRef"
status-icon label-width="120px">
<el-form-item label="Name" prop="name">
<el-input v-model="storeForm.name" test-id="store-form-name" />
</el-form-item>
<el-form-item label="URL" prop="url">
<el-input v-model="storeForm.url" placeholder="http://foo" test-id="store-form-url" />
</el-form-item>
<el-form-item label="Username" prop="username">
<el-input v-model="storeForm.username" test-id="store-form-username" />
</el-form-item>
<el-form-item label="Password" prop="password">
<el-input v-model="storeForm.password" type="password" test-id="store-form-password" />
</el-form-item>
<el-form-item label="Plugin" prop="plugin">
<el-input v-model="storeForm.kind.url" test-id="store-form-plugin" />
</el-form-item>
<el-form-item label="Properties" prop="properties">
<el-table :data="storeForm.properties" style="width: 100%">
<el-table-column label="Key" width="180">
<template #default="scope">
<el-input v-model="scope.row.key" placeholder="Key" @change="updateKeys"/>
</template>
</el-table-column>
<el-table-column label="Value">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-input v-model="scope.row.value" placeholder="Value" />
</div>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="submitForm(storeFormRef)"
:loading="creatingLoading"
test-id="store-form-submit"
>Submit</el-button
>
</el-form-item>
</el-form>
</span>
</template>
</el-dialog>
</template>
Loading
Loading