new plugins management page
This commit is contained in:
parent
85f9f8378a
commit
7ecea9e7e6
|
|
@ -21,7 +21,6 @@ class MarketController extends Controller
|
|||
$plugin = $manager->get($item['name']);
|
||||
|
||||
if ($plugin) {
|
||||
$item['enabled'] = $plugin->isEnabled();
|
||||
$item['installed'] = $plugin->version;
|
||||
$item['can_update'] = Comparator::greaterThan($item['version'], $item['installed']);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -88,21 +88,19 @@ class PluginController extends Controller
|
|||
public function getPluginData(PluginManager $plugins)
|
||||
{
|
||||
return $plugins->all()
|
||||
->map(function ($plugin) use ($plugins) {
|
||||
->map(function (Plugin $plugin) {
|
||||
return [
|
||||
'name' => $plugin->name,
|
||||
'title' => trans($plugin->title ?: 'EMPTY'),
|
||||
'author' => $plugin->author,
|
||||
'description' => trans($plugin->description ?: 'EMPTY'),
|
||||
'title' => trans($plugin->title),
|
||||
'description' => trans($plugin->description ?? ''),
|
||||
'version' => $plugin->version,
|
||||
'url' => $plugin->url,
|
||||
'enabled' => $plugin->isEnabled(),
|
||||
'readme' => (bool) $plugin->getReadme(),
|
||||
'config' => $plugin->hasConfig(),
|
||||
'dependencies' => [
|
||||
'all' => $plugin->require,
|
||||
'unsatisfied' => $plugins->getUnsatisfied($plugin),
|
||||
],
|
||||
'icon' => array_merge(
|
||||
['fa' => 'plug', 'faType' => 'fas', 'bg' => 'navy'],
|
||||
$plugin->getManifestAttr('enchants.icon', [])
|
||||
),
|
||||
];
|
||||
})
|
||||
->values();
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class Plugin
|
|||
];
|
||||
|
||||
/**
|
||||
* The full directory of this plugin.
|
||||
* The full path of this plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
|
|
@ -29,11 +29,6 @@ class Plugin
|
|||
*/
|
||||
protected $manifest;
|
||||
|
||||
/**
|
||||
* Whether the plugin is enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $enabled = false;
|
||||
|
||||
public function __construct(string $path, array $manifest)
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
import Vue from 'vue'
|
||||
import { showModal, toast } from '../../scripts/notify'
|
||||
import alertUnresolvedPlugins from './alertUnresolvedPlugins'
|
||||
|
||||
export default Vue.extend({
|
||||
data: () => ({ plugins: [] }),
|
||||
methods: {
|
||||
async enablePlugin({
|
||||
name, dependencies: { all }, originalIndex,
|
||||
}: {
|
||||
name: string
|
||||
dependencies: { all: Record<string, string> }
|
||||
originalIndex: number
|
||||
}) {
|
||||
if (Object.keys(all).length === 0) {
|
||||
try {
|
||||
await showModal({
|
||||
text: this.$t('admin.noDependenciesNotice'),
|
||||
okButtonType: 'warning',
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
code, message, data: { reason } = { reason: [] },
|
||||
} = await this.$http.post(
|
||||
'/admin/plugins/manage',
|
||||
{ action: 'enable', name },
|
||||
) as { code: number, message: string, data: { reason: string[] } }
|
||||
if (code === 0) {
|
||||
toast.success(message)
|
||||
this.$set(this.plugins[originalIndex], 'enabled', true)
|
||||
} else {
|
||||
alertUnresolvedPlugins(message, reason)
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
@ -43,11 +43,8 @@
|
|||
<i class="fas fa-sync-alt" /> {{ $t('admin.updatePlugin') }}
|
||||
</template>
|
||||
</button>
|
||||
<button v-else-if="props.row.enabled" class="btn btn-primary" disabled>
|
||||
<i class="fas fa-check" /> {{ $t('admin.statusEnabled') }}
|
||||
</button>
|
||||
<button v-else class="btn btn-primary" @click="enablePlugin(props.row)">
|
||||
<i class="fas fa-plug" /> {{ $t('admin.enablePlugin') }}
|
||||
<button v-else class="btn btn-default" disabled>
|
||||
<i class="fas fa-download" /> {{ $t('admin.installPlugin') }}
|
||||
</button>
|
||||
</template>
|
||||
<button
|
||||
|
|
@ -74,7 +71,6 @@
|
|||
import { VueGoodTable } from 'vue-good-table'
|
||||
import 'vue-good-table/dist/vue-good-table.min.css'
|
||||
import alertUnresolvedPlugins from '../../components/mixins/alertUnresolvedPlugins'
|
||||
import enablePlugin from '../../components/mixins/enablePlugin'
|
||||
import tableOptions from '../../components/mixins/tableOptions'
|
||||
import emitMounted from '../../components/mixins/emitMounted'
|
||||
import { showModal, toast } from '../../scripts/notify'
|
||||
|
|
@ -86,7 +82,6 @@ export default {
|
|||
},
|
||||
mixins: [
|
||||
emitMounted,
|
||||
enablePlugin,
|
||||
tableOptions,
|
||||
],
|
||||
data() {
|
||||
|
|
|
|||
|
|
@ -1,104 +1,51 @@
|
|||
<template>
|
||||
<div class="container-fluid">
|
||||
<vue-good-table
|
||||
:rows="plugins"
|
||||
:columns="columns"
|
||||
:search-options="tableOptions.search"
|
||||
:pagination-options="tableOptions.pagination"
|
||||
style-class="vgt-table striped"
|
||||
:row-style-class="rowStyleClassFn"
|
||||
>
|
||||
<template #table-row="props">
|
||||
<span v-if="props.column.field === 'title'">
|
||||
<strong>{{ props.formattedRow[props.column.field] }}</strong>
|
||||
<div class="actions">
|
||||
<template v-if="props.row.readme">
|
||||
<a
|
||||
:href="`${baseUrl}/admin/plugins/readme/${props.row.name}`"
|
||||
class="text-primary"
|
||||
>{{ $t('admin.pluginReadme') }}</a> |
|
||||
</template>
|
||||
<template v-if="props.row.enabled" class="actions">
|
||||
<template v-if="props.row.config">
|
||||
<a
|
||||
v-t="'admin.configurePlugin'"
|
||||
class="text-primary"
|
||||
:href="`${baseUrl}/admin/plugins/config/${props.row.name}`"
|
||||
/> |
|
||||
</template>
|
||||
<a
|
||||
v-t="'admin.disablePlugin'"
|
||||
href="#"
|
||||
class="text-primary"
|
||||
@click="disablePlugin(props.row)"
|
||||
/>
|
||||
</template>
|
||||
<template v-else class="actions">
|
||||
<a
|
||||
v-t="'admin.enablePlugin'"
|
||||
href="#"
|
||||
class="text-primary"
|
||||
@click="enablePlugin(props.row)"
|
||||
/> |
|
||||
<a
|
||||
v-t="'admin.deletePlugin'"
|
||||
href="#"
|
||||
class="text-danger"
|
||||
@click="deletePlugin(props.row)"
|
||||
/>
|
||||
</template>
|
||||
<div class="container-fluid d-flex flex-wrap">
|
||||
<div v-for="(plugin, index) in plugins" :key="plugin.name" class="info-box mr-3">
|
||||
<span class="info-box-icon" :class="`bg-${plugin.icon.bg}`">
|
||||
<i :class="`${plugin.icon.faType} fa-${plugin.icon.fa}`" />
|
||||
</span>
|
||||
<div class="info-box-content">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<input :checked="plugin.enabled" type="checkbox" @click.prevent="switchPlugin(plugin, $event)">
|
||||
<strong>{{ plugin.title }}</strong>
|
||||
<span class="text-gray">v{{ plugin.version }}</span>
|
||||
</div>
|
||||
</span>
|
||||
<span v-else-if="props.column.field === 'description'">
|
||||
<div><p>{{ props.formattedRow.description }}</p></div>
|
||||
<div class="plugin-version-author">
|
||||
{{ $t('admin.pluginVersion') }}
|
||||
<span class="text-primary">{{ props.row.version }}</span> |
|
||||
{{ $t('admin.pluginAuthor') }}
|
||||
<a :href="props.row.url">{{ props.row.author }}</a> |
|
||||
{{ $t('admin.pluginName') }}
|
||||
{{ props.row.name }}
|
||||
</div>
|
||||
</span>
|
||||
<span v-else-if="props.column.field === 'dependencies'">
|
||||
<span v-if="Object.keys(props.row.dependencies.all).length === 0">
|
||||
<i v-t="'admin.noDependencies'" />
|
||||
</span>
|
||||
<div v-else>
|
||||
<span
|
||||
v-for="(constraint, name) in props.row.dependencies.all"
|
||||
:key="name"
|
||||
class="badge"
|
||||
:class="`bg-${name in props.row.dependencies.unsatisfied ? 'red' : 'green'}`"
|
||||
<div class="plugin-actions">
|
||||
<a
|
||||
v-if="plugin.readme"
|
||||
:href="`${baseUrl}/admin/plugins/readme/${plugin.name}`"
|
||||
>
|
||||
{{ name }}: {{ constraint }}
|
||||
<br>
|
||||
</span>
|
||||
<i class="fas fa-question" />
|
||||
</a>
|
||||
<a
|
||||
v-if="plugin.enabled && plugin.config"
|
||||
:href="`${baseUrl}/admin/plugins/config/${plugin.name}`"
|
||||
>
|
||||
<i class="fas fa-cog" />
|
||||
</a>
|
||||
<a href="#" @click="deletePlugin(plugin, index)">
|
||||
<i class="fas fa-trash" />
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
<span v-else v-text="props.formattedRow[props.column.field]" />
|
||||
</template>
|
||||
</vue-good-table>
|
||||
</div>
|
||||
<div class="mt-2 plugin-desc" :title="plugin.description">
|
||||
{{ plugin.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { VueGoodTable } from 'vue-good-table'
|
||||
import 'vue-good-table/dist/vue-good-table.min.css'
|
||||
import enablePlugin from '../../components/mixins/enablePlugin'
|
||||
import tableOptions from '../../components/mixins/tableOptions'
|
||||
import alertUnresolvedPlugins from '../../components/mixins/alertUnresolvedPlugins'
|
||||
import emitMounted from '../../components/mixins/emitMounted'
|
||||
import { showModal, toast } from '../../scripts/notify'
|
||||
|
||||
export default {
|
||||
name: 'Plugins',
|
||||
components: {
|
||||
VueGoodTable,
|
||||
},
|
||||
mixins: [
|
||||
emitMounted,
|
||||
enablePlugin,
|
||||
tableOptions,
|
||||
],
|
||||
props: {
|
||||
baseUrl: {
|
||||
|
|
@ -109,23 +56,6 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
plugins: [],
|
||||
columns: [
|
||||
{
|
||||
field: 'title', label: this.$t('admin.pluginTitle'), width: '17%',
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
label: this.$t('admin.pluginDescription'),
|
||||
sortable: false,
|
||||
width: '65%',
|
||||
},
|
||||
{
|
||||
field: 'dependencies',
|
||||
label: this.$t('admin.pluginDependencies'),
|
||||
sortable: false,
|
||||
globalSearchDisabled: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
|
|
@ -135,27 +65,47 @@ export default {
|
|||
async fetchData() {
|
||||
this.plugins = await this.$http.get('/admin/plugins/data')
|
||||
},
|
||||
rowStyleClassFn(row) {
|
||||
return row.enabled ? 'plugin-enabled' : 'plugin'
|
||||
async switchPlugin(plugin, { target }) {
|
||||
if (target.checked) {
|
||||
if (await this.enablePlugin(plugin.name)) {
|
||||
plugin.enabled = true
|
||||
}
|
||||
} else if (await this.disablePlugin(plugin.name)) {
|
||||
plugin.enabled = false
|
||||
}
|
||||
},
|
||||
async disablePlugin({ name, originalIndex }) {
|
||||
async enablePlugin(name) {
|
||||
const {
|
||||
code, message, data: { reason } = { reason: [] },
|
||||
} = await this.$http.post(
|
||||
'/admin/plugins/manage',
|
||||
{ action: 'enable', name },
|
||||
)
|
||||
if (code === 0) {
|
||||
toast.success(message)
|
||||
} else {
|
||||
alertUnresolvedPlugins(message, reason)
|
||||
}
|
||||
|
||||
return code === 0
|
||||
},
|
||||
async disablePlugin(name) {
|
||||
const { code, message } = await this.$http.post(
|
||||
'/admin/plugins/manage',
|
||||
{ action: 'disable', name },
|
||||
)
|
||||
if (code === 0) {
|
||||
toast.success(message)
|
||||
this.plugins[originalIndex].enabled = false
|
||||
} else {
|
||||
toast.error(message)
|
||||
}
|
||||
|
||||
return code === 0
|
||||
},
|
||||
async deletePlugin({
|
||||
name, title, originalIndex,
|
||||
}) {
|
||||
async deletePlugin(plugin, index) {
|
||||
try {
|
||||
await showModal({
|
||||
title,
|
||||
title: plugin.title,
|
||||
text: this.$t('admin.confirmDeletion'),
|
||||
okButtonType: 'danger',
|
||||
})
|
||||
|
|
@ -165,10 +115,10 @@ export default {
|
|||
|
||||
const { code, message } = await this.$http.post(
|
||||
'/admin/plugins/manage',
|
||||
{ action: 'delete', name },
|
||||
{ action: 'delete', name: plugin.name },
|
||||
)
|
||||
if (code === 0) {
|
||||
this.$delete(this.plugins, originalIndex)
|
||||
this.$delete(this.plugins, index)
|
||||
toast.success(message)
|
||||
} else {
|
||||
toast.error(message)
|
||||
|
|
@ -179,25 +129,35 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.actions
|
||||
margin-top 5px
|
||||
color #ddd
|
||||
.info-box
|
||||
cursor default
|
||||
transition-property box-shadow
|
||||
transition-duration 0.3s
|
||||
width 32%
|
||||
@media (max-width: 1280px)
|
||||
width 47%
|
||||
@media (max-width: 768px)
|
||||
width 100%
|
||||
&:hover
|
||||
box-shadow 0 .5rem 1rem rgba(0,0,0,.15)
|
||||
|
||||
.plugin-version-author
|
||||
color #777
|
||||
font-size small
|
||||
a
|
||||
color #337ab7
|
||||
.info-box-content
|
||||
max-width 85%
|
||||
|
||||
.plugin:nth-of-type(2n+1) > td:first-child
|
||||
border-left 5px solid rgba(51, 68, 109, 0.03)
|
||||
.plugin-actions
|
||||
margin-top -7px
|
||||
a
|
||||
transition-property color
|
||||
transition-duration 0.3s
|
||||
color #000
|
||||
&:hover
|
||||
color #999
|
||||
&:not(:last-child)
|
||||
margin-right 7px
|
||||
|
||||
.plugin:nth-of-type(2n) > td:first-child
|
||||
border-left 5px solid #fff
|
||||
|
||||
.plugin-enabled
|
||||
background-color #f7fcfe
|
||||
|
||||
.plugin-enabled > td:first-child
|
||||
border-left 5px solid #3c8dbc
|
||||
.plugin-desc
|
||||
font-size 14px
|
||||
white-space nowrap
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -30,13 +30,10 @@ test('render operation buttons', async () => {
|
|||
name: 'a', dependencies: { all: {}, unsatisfied: {} }, installed: true, can_update: true,
|
||||
},
|
||||
{
|
||||
name: 'b', dependencies: { all: {}, unsatisfied: {} }, installed: true, enabled: true,
|
||||
name: 'b', dependencies: { all: {}, unsatisfied: {} }, installed: true,
|
||||
},
|
||||
{
|
||||
name: 'c', dependencies: { all: {}, unsatisfied: {} }, installed: true,
|
||||
},
|
||||
{
|
||||
name: 'd', dependencies: { all: {}, unsatisfied: {} }, installed: false,
|
||||
name: 'c', dependencies: { all: {}, unsatisfied: {} }, installed: false,
|
||||
},
|
||||
])
|
||||
const wrapper = mount(Market)
|
||||
|
|
@ -44,9 +41,9 @@ test('render operation buttons', async () => {
|
|||
const tbody = wrapper.find('tbody')
|
||||
|
||||
expect(tbody.find('tr:nth-child(1)').text()).toContain('admin.updatePlugin')
|
||||
expect(tbody.find('tr:nth-child(2)').text()).toContain('admin.statusEnabled')
|
||||
expect(tbody.find('tr:nth-child(3)').text()).toContain('admin.enablePlugin')
|
||||
expect(tbody.find('tr:nth-child(4)').text()).toContain('admin.installPlugin')
|
||||
expect(tbody.find('tr:nth-child(2)').text()).toContain('admin.installPlugin')
|
||||
expect(tbody.find('tr:nth-child(2) button').attributes('disabled')).toBeTruthy()
|
||||
expect(tbody.find('tr:nth-child(3)').text()).toContain('admin.installPlugin')
|
||||
})
|
||||
|
||||
test('install plugin', async () => {
|
||||
|
|
@ -80,7 +77,7 @@ test('install plugin', async () => {
|
|||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.text()).toContain('admin.enablePlugin')
|
||||
expect(wrapper.find('.btn-default').attributes('disabled')).toBeTruthy()
|
||||
})
|
||||
|
||||
test('update plugin', async () => {
|
||||
|
|
|
|||
|
|
@ -6,54 +6,30 @@ import Plugins from '@/views/admin/Plugins.vue'
|
|||
|
||||
jest.mock('@/scripts/notify')
|
||||
|
||||
test('render dependencies', async () => {
|
||||
test('render config button', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue([
|
||||
{ name: 'a', dependencies: { all: {}, unsatisfied: {} } },
|
||||
{
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
all: { a: '^1.0.0', c: '^2.0.0' }, unsatisfied: { c: '' },
|
||||
},
|
||||
name: 'a', icon: {}, enabled: true, config: true,
|
||||
},
|
||||
{
|
||||
name: 'b', icon: {}, enabled: false, config: true,
|
||||
},
|
||||
{
|
||||
name: 'c', icon: {}, enabled: false, config: false,
|
||||
},
|
||||
])
|
||||
const wrapper = mount(Plugins)
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.text()).toContain('admin.noDependencies')
|
||||
expect(wrapper.find('span.badge.bg-green').text()).toBe('a: ^1.0.0')
|
||||
expect(wrapper.find('span.badge.bg-red').text()).toBe('c: ^2.0.0')
|
||||
})
|
||||
|
||||
test('render operation buttons', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue([
|
||||
{
|
||||
name: 'a', dependencies: { all: {}, unsatisfied: {} }, enabled: true, config: true,
|
||||
},
|
||||
{
|
||||
name: 'b', dependencies: { all: {}, unsatisfied: {} }, enabled: true, config: false,
|
||||
},
|
||||
{
|
||||
name: 'c', dependencies: { all: {}, unsatisfied: {} }, enabled: false,
|
||||
},
|
||||
])
|
||||
const wrapper = mount(Plugins)
|
||||
await flushPromises()
|
||||
const tbody = wrapper.find('tbody')
|
||||
|
||||
expect(tbody.find('tr:nth-child(1)').text()).toContain('admin.disablePlugin')
|
||||
expect(tbody.find('tr:nth-child(1)').text()).toContain('admin.configurePlugin')
|
||||
expect(tbody.find('tr:nth-child(2)').text()).not.toContain('admin.configurePlugin')
|
||||
expect(tbody.find('tr:nth-child(3)').text()).toContain('admin.enablePlugin')
|
||||
expect(tbody.find('tr:nth-child(3)').text()).toContain('admin.deletePlugin')
|
||||
expect(wrapper.find('.info-box:nth-child(1) .fa-cog').exists()).toBeTrue()
|
||||
expect(wrapper.find('.info-box:nth-child(2) .fa-cog').exists()).toBeFalse()
|
||||
expect(wrapper.find('.info-box:nth-child(3) .fa-cog').exists()).toBeFalse()
|
||||
})
|
||||
|
||||
test('enable plugin', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue([
|
||||
{
|
||||
name: 'a', dependencies: { all: {}, unsatisfied: {} }, enabled: false,
|
||||
},
|
||||
{
|
||||
name: 'b', dependencies: { all: { c: '' }, unsatisfied: {} }, enabled: false,
|
||||
name: 'a', icon: {}, enabled: false,
|
||||
},
|
||||
])
|
||||
Vue.prototype.$http.post
|
||||
|
|
@ -61,29 +37,11 @@ test('enable plugin', async () => {
|
|||
code: 1, message: '1', data: { reason: ['abc'] },
|
||||
})
|
||||
.mockResolvedValue({ code: 0, message: '0' })
|
||||
showModal
|
||||
.mockRejectedValueOnce(null)
|
||||
.mockResolvedValue({ value: '' })
|
||||
const wrapper = mount(Plugins)
|
||||
await flushPromises()
|
||||
const checkbox = wrapper.find('input[type=checkbox]')
|
||||
|
||||
wrapper
|
||||
.findAll('.actions')
|
||||
.at(0)
|
||||
.find('a')
|
||||
.trigger('click')
|
||||
await flushPromises()
|
||||
expect(showModal).toBeCalledWith({
|
||||
text: 'admin.noDependenciesNotice',
|
||||
okButtonType: 'warning',
|
||||
})
|
||||
expect(Vue.prototype.$http.post).not.toBeCalled()
|
||||
|
||||
wrapper
|
||||
.findAll('.actions')
|
||||
.at(0)
|
||||
.find('a')
|
||||
.trigger('click')
|
||||
checkbox.trigger('click')
|
||||
await flushPromises()
|
||||
expect(Vue.prototype.$http.post).toBeCalledWith(
|
||||
'/admin/plugins/manage',
|
||||
|
|
@ -94,19 +52,15 @@ test('enable plugin', async () => {
|
|||
dangerousHTML: expect.stringContaining('<li>abc</li>'),
|
||||
})
|
||||
|
||||
wrapper
|
||||
.findAll('.actions')
|
||||
.at(1)
|
||||
.find('a')
|
||||
.trigger('click')
|
||||
checkbox.trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.text()).toContain('admin.disablePlugin')
|
||||
expect(toast.success).toBeCalled()
|
||||
})
|
||||
|
||||
test('disable plugin', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue([
|
||||
{
|
||||
name: 'a', dependencies: { all: {}, unsatisfied: {} }, enabled: true, config: false,
|
||||
name: 'a', icon: {}, enabled: true,
|
||||
},
|
||||
])
|
||||
Vue.prototype.$http.post
|
||||
|
|
@ -114,18 +68,18 @@ test('disable plugin', async () => {
|
|||
.mockResolvedValue({ code: 0, message: '0' })
|
||||
const wrapper = mount(Plugins)
|
||||
await flushPromises()
|
||||
const button = wrapper.find('.actions').find('a')
|
||||
const checkbox = wrapper.find('input[type="checkbox"]')
|
||||
|
||||
button.trigger('click')
|
||||
checkbox.trigger('click')
|
||||
await flushPromises()
|
||||
expect(Vue.prototype.$http.post).toBeCalledWith(
|
||||
'/admin/plugins/manage',
|
||||
{ action: 'disable', name: 'a' },
|
||||
)
|
||||
button.trigger('click')
|
||||
checkbox.trigger('click')
|
||||
await flushPromises()
|
||||
expect(toast.success).toBeCalledWith('0')
|
||||
expect(wrapper.text()).toContain('admin.enablePlugin')
|
||||
expect(checkbox.attributes('checked')).toBeFalsy()
|
||||
})
|
||||
|
||||
test('delete plugin', async () => {
|
||||
|
|
@ -133,8 +87,7 @@ test('delete plugin', async () => {
|
|||
{
|
||||
name: 'a',
|
||||
title: 'My Plugin',
|
||||
dependencies: { all: {}, unsatisfied: {} },
|
||||
enabled: false,
|
||||
icon: {},
|
||||
},
|
||||
])
|
||||
Vue.prototype.$http.post
|
||||
|
|
@ -145,8 +98,7 @@ test('delete plugin', async () => {
|
|||
.mockResolvedValue({ value: '' })
|
||||
const wrapper = mount(Plugins)
|
||||
await flushPromises()
|
||||
const button = wrapper.find('.actions').findAll('a')
|
||||
.at(1)
|
||||
const button = wrapper.find('.plugin-actions a')
|
||||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
|
|
@ -167,7 +119,7 @@ test('delete plugin', async () => {
|
|||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.text()).toContain('No data')
|
||||
expect(wrapper.text()).not.toContain('My Plugin')
|
||||
})
|
||||
|
||||
test('readme link', async () => {
|
||||
|
|
@ -175,13 +127,12 @@ test('readme link', async () => {
|
|||
{
|
||||
name: 'a',
|
||||
readme: true,
|
||||
dependencies: { all: {}, unsatisfied: {} },
|
||||
icon: {},
|
||||
},
|
||||
])
|
||||
const wrapper = mount(Plugins)
|
||||
await flushPromises()
|
||||
|
||||
const link = wrapper.find('.actions > a:nth-child(1)')
|
||||
expect(link.text()).toContain('admin.pluginReadme')
|
||||
const link = wrapper.find('.plugin-actions > a:nth-child(1)')
|
||||
expect(link.attributes('href')).toBe('/admin/plugins/readme/a')
|
||||
})
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
- Respond with unformatted Profile JSON to reduce bytes.
|
||||
- Switched to a new PHP texture renderer.
|
||||
- Display 3D avatar of player when applying texture to player.
|
||||
- New "Plugins Management" page.
|
||||
|
||||
## Fixed
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
- 响应未格式化过的 Profile JSON 以节省流量
|
||||
- 使用新的 PHP 材质渲染器
|
||||
- 将材质应用到角色时显示角色的 3D 头像
|
||||
- 新的「插件管理」页面
|
||||
|
||||
## 修复
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ class PluginControllerTest extends TestCase
|
|||
|
||||
public function testShowManage()
|
||||
{
|
||||
$this->get('/admin/plugins/manage')
|
||||
->assertSee(trans('general.plugin-manage'));
|
||||
$this->get('/admin/plugins/manage')->assertSee(trans('general.plugin-manage'));
|
||||
}
|
||||
|
||||
public function testConfig()
|
||||
|
|
@ -250,14 +249,6 @@ class PluginControllerTest extends TestCase
|
|||
'version' => '0.0.0',
|
||||
'title' => '',
|
||||
])]));
|
||||
$mock->shouldReceive('getUnsatisfied')
|
||||
->withArgs(function ($plugin) {
|
||||
$this->assertEquals('a', $plugin->name);
|
||||
|
||||
return true;
|
||||
})
|
||||
->once()
|
||||
->andReturn(collect(['b' => null]));
|
||||
});
|
||||
$this->getJson('/admin/plugins/data')
|
||||
->assertJsonStructure([
|
||||
|
|
@ -266,12 +257,9 @@ class PluginControllerTest extends TestCase
|
|||
'version',
|
||||
'title',
|
||||
'description',
|
||||
'author',
|
||||
'url',
|
||||
'enabled',
|
||||
'config',
|
||||
'readme',
|
||||
'dependencies',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,7 @@ const config = {
|
|||
'admin-lte/dist/css/alt/adminlte.components.min.css',
|
||||
'admin-lte/dist/css/alt/adminlte.extra-components.min.css',
|
||||
'admin-lte/dist/css/alt/adminlte.pages.min.css',
|
||||
'@fortawesome/fontawesome-free/css/fontawesome.min.css',
|
||||
'@fortawesome/fontawesome-free/css/regular.min.css',
|
||||
'@fortawesome/fontawesome-free/css/solid.min.css',
|
||||
'@fortawesome/fontawesome-free/css/all.min.css',
|
||||
'./resources/assets/src/styles/common.styl',
|
||||
],
|
||||
setup: './resources/assets/src/styles/setup.styl',
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user