diff --git a/package.json b/package.json
index dd963b5b..0b675155 100644
--- a/package.json
+++ b/package.json
@@ -53,6 +53,7 @@
"eslint": "^5.14.0",
"eslint-config-gplane": "^5.1.3",
"eslint-formatter-beauty": "^3.0.0",
+ "eslint-plugin-project": "^0.2.2",
"eslint-plugin-vue": "^5.2.2",
"file-loader": "^3.0.1",
"jest": "^24.5.0",
@@ -80,6 +81,14 @@
"not ie 11",
"Chrome > 52"
],
+ "prettier": {
+ "printWidth": 80,
+ "semi": false,
+ "singleQuote": true,
+ "trailingComma": "es5",
+ "arrowParens": "avoid",
+ "tabWidth": 2
+ },
"eslintConfig": {
"root": true,
"extends": [
@@ -100,6 +109,12 @@
"globals": {
"blessing": "readonly"
},
+ "plugins": [
+ "project"
+ ],
+ "settings": {
+ "projectRulesDir": "resources/assets/eslint-rules"
+ },
"rules": {
"camelcase": [
2,
@@ -113,7 +128,8 @@
120
],
"prefer-object-spread": 0,
- "import/no-unresolved": 0
+ "import/no-unresolved": 0,
+ "project/linebreak-between-tests": 2
}
},
"jest": {
diff --git a/resources/assets/eslint-rules/linebreak-between-tests.js b/resources/assets/eslint-rules/linebreak-between-tests.js
new file mode 100644
index 00000000..5945bd46
--- /dev/null
+++ b/resources/assets/eslint-rules/linebreak-between-tests.js
@@ -0,0 +1,27 @@
+module.exports = {
+ create(context) {
+ return {
+ CallExpression(node) {
+ const args = node.arguments
+ if (
+ node.callee.name === 'test' &&
+ args[0].type === 'Literal' &&
+ args[1].type === 'ArrowFunctionExpression'
+ ) {
+ const next = context.getTokenAfter(node)
+ if (
+ next &&
+ next.value === 'test' &&
+ node.loc.end.line + 1 === next.loc.start.line
+ ) {
+ context.report({
+ node: context.getLastToken(node),
+ message: 'Linebreak should be inserted between test blocks.',
+ fix: fixer => fixer.insertTextAfter(node, '\n'),
+ })
+ }
+ }
+ },
+ }
+ },
+}
diff --git a/resources/assets/tests/components/admin/Customization.test.js b/resources/assets/tests/components/admin/Customization.test.js
index eec6e081..97872428 100644
--- a/resources/assets/tests/components/admin/Customization.test.js
+++ b/resources/assets/tests/components/admin/Customization.test.js
@@ -12,6 +12,7 @@ test('preview color', () => {
expect(document.body.classList.contains('skin-blue')).toBeFalse()
expect(document.body.classList.contains('skin-yellow')).toBeTrue()
})
+
test('submit color', () => {
Vue.prototype.$http.post.mockResolvedValue({ errno: 0, msg: '' })
const wrapper = mount(Customization)
diff --git a/resources/assets/tests/components/admin/Market.test.js b/resources/assets/tests/components/admin/Market.test.js
index 93d257f1..95107855 100644
--- a/resources/assets/tests/components/admin/Market.test.js
+++ b/resources/assets/tests/components/admin/Market.test.js
@@ -23,6 +23,7 @@ test('render dependencies', async () => {
expect(wrapper.find('span.label.bg-green').text()).toBe('a: ^1.0.0')
expect(wrapper.find('span.label.bg-red').text()).toBe('c: ^2.0.0')
})
+
test('render operation buttons', async () => {
Vue.prototype.$http.get.mockResolvedValue([
{
@@ -47,6 +48,7 @@ test('render operation buttons', async () => {
expect(tbody.find('tr:nth-child(3)').text()).toContain('admin.enablePlugin')
expect(tbody.find('tr:nth-child(4)').text()).toContain('admin.installPlugin')
})
+
test('install plugin', async () => {
Vue.prototype.$http.get.mockResolvedValue([
{
@@ -70,6 +72,7 @@ test('install plugin', async () => {
await wrapper.vm.$nextTick()
expect(wrapper.text()).toContain('admin.enablePlugin')
})
+
test('update plugin', async () => {
Vue.prototype.$http.get.mockResolvedValue([
{
diff --git a/resources/assets/tests/components/admin/Players.test.js b/resources/assets/tests/components/admin/Players.test.js
index 01e61c05..1819402d 100644
--- a/resources/assets/tests/components/admin/Players.test.js
+++ b/resources/assets/tests/components/admin/Players.test.js
@@ -85,6 +85,7 @@ test('change texture', async () => {
expect(wrapper.html()).toContain('/preview/64/5.png')
expect(window.$).toBeCalledWith('.modal')
})
+
test('change player name', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -117,6 +118,7 @@ test('change player name', async () => {
await flushPromises()
expect(wrapper.text()).toContain('new')
})
+
test('change owner', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -146,6 +148,7 @@ test('change owner', async () => {
await flushPromises()
expect(wrapper.text()).toContain('3')
})
+
test('delete player', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
diff --git a/resources/assets/tests/components/admin/Plugins.test.js b/resources/assets/tests/components/admin/Plugins.test.js
index 38b3b27f..93d2a024 100644
--- a/resources/assets/tests/components/admin/Plugins.test.js
+++ b/resources/assets/tests/components/admin/Plugins.test.js
@@ -24,6 +24,7 @@ test('render dependencies', async () => {
expect(wrapper.find('span.label.bg-green').text()).toBe('a: ^1.0.0')
expect(wrapper.find('span.label.bg-red').text()).toBe('c: ^2.0.0')
})
+
test('render operation buttons', async () => {
Vue.prototype.$http.get.mockResolvedValue([
{
@@ -46,6 +47,7 @@ test('render operation buttons', async () => {
expect(tbody.find('tr:nth-child(3)').text()).toContain('admin.enablePlugin')
expect(tbody.find('tr:nth-child(3)').text()).toContain('admin.deletePlugin')
})
+
test('enable plugin', async () => {
Vue.prototype.$http.get.mockResolvedValue([
{
@@ -95,6 +97,7 @@ test('enable plugin', async () => {
await flushPromises()
expect(wrapper.text()).toContain('admin.disablePlugin')
})
+
test('disable plugin', async () => {
jest.spyOn(toastr, 'success')
Vue.prototype.$http.get.mockResolvedValue([
@@ -120,6 +123,7 @@ test('disable plugin', async () => {
expect(toastr.success).toBeCalledWith('0')
expect(wrapper.text()).toContain('admin.enablePlugin')
})
+
test('delete plugin', async () => {
jest.spyOn(toastr, 'success')
Vue.prototype.$http.get.mockResolvedValue([
diff --git a/resources/assets/tests/components/admin/Update.test.js b/resources/assets/tests/components/admin/Update.test.js
index c01b790b..bfba67d9 100644
--- a/resources/assets/tests/components/admin/Update.test.js
+++ b/resources/assets/tests/components/admin/Update.test.js
@@ -14,6 +14,7 @@ test('button should be disabled if update is unavailable', () => {
const wrapper = mount(Update)
expect(wrapper.find('.btn').attributes('disabled')).toBe('disabled')
})
+
test('perform update', async () => {
window.$ = jest.fn(() => ({
modal() {},
diff --git a/resources/assets/tests/components/admin/Users.test.js b/resources/assets/tests/components/admin/Users.test.js
index 39325d8a..c2c079e9 100644
--- a/resources/assets/tests/components/admin/Users.test.js
+++ b/resources/assets/tests/components/admin/Users.test.js
@@ -75,6 +75,7 @@ test('humanize permission', async () => {
expect(text).toContain('admin.admin')
expect(text).toContain('admin.superAdmin')
})
+
test('generate players page link', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -85,6 +86,7 @@ test('generate players page link', async () => {
await wrapper.vm.$nextTick()
expect(wrapper.find('[data-toggle="tooltip"]').attributes('href')).toBe('/admin/players?uid=1')
})
+
test('admin option should not be displayed for super admins', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -97,6 +99,7 @@ test('admin option should not be displayed for super admins', async () => {
expect(text).not.toContain('admin.setAdmin')
expect(text).not.toContain('admin.unsetAdmin')
})
+
test('banning option should not be displayed for super admins', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -109,6 +112,7 @@ test('banning option should not be displayed for super admins', async () => {
expect(text).not.toContain('admin.ban')
expect(text).not.toContain('admin.unban')
})
+
test('admin option should be displayed for admin as super admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -123,6 +127,7 @@ test('admin option should be displayed for admin as super admin', async () => {
expect(text).toContain('admin.unsetAdmin')
expect(text).not.toContain('admin.setAdmin')
})
+
test('banning option should not be displayed for admin as super admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -137,6 +142,7 @@ test('banning option should not be displayed for admin as super admin', async ()
expect(text).not.toContain('admin.ban')
expect(text).not.toContain('admin.unban')
})
+
test('admin option should be displayed for normal users as super admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -151,6 +157,7 @@ test('admin option should be displayed for normal users as super admin', async (
expect(text).toContain('admin.setAdmin')
expect(text).not.toContain('admin.unsetAdmin')
})
+
test('banning option should be displayed for normal users as super admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -165,6 +172,7 @@ test('banning option should be displayed for normal users as super admin', async
expect(text).toContain('admin.ban')
expect(text).not.toContain('admin.unban')
})
+
test('admin option should not be displayed for banned users as super admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -179,6 +187,7 @@ test('admin option should not be displayed for banned users as super admin', asy
expect(text).not.toContain('admin.setAdmin')
expect(text).not.toContain('admin.unsetAdmin')
})
+
test('banning option should be displayed for banned users as super admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -192,6 +201,7 @@ test('banning option should be displayed for banned users as super admin', async
const text = wrapper.find('.vgt-table').text()
expect(text).toContain('admin.unban')
})
+
test('admin option should not be displayed for other admins as admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -206,6 +216,7 @@ test('admin option should not be displayed for other admins as admin', async ()
expect(text).not.toContain('admin.setAdmin')
expect(text).not.toContain('admin.unsetAdmin')
})
+
test('banning option should not be displayed for other admins as admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -220,6 +231,7 @@ test('banning option should not be displayed for other admins as admin', async (
expect(text).not.toContain('admin.ban')
expect(text).not.toContain('admin.unban')
})
+
test('admin option should not be displayed for normal users as admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -234,6 +246,7 @@ test('admin option should not be displayed for normal users as admin', async ()
expect(text).not.toContain('admin.setAdmin')
expect(text).not.toContain('admin.unsetAdmin')
})
+
test('banning option should be displayed for normal users as admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -248,6 +261,7 @@ test('banning option should be displayed for normal users as admin', async () =>
expect(text).toContain('admin.ban')
expect(text).not.toContain('admin.unban')
})
+
test('admin option should not be displayed for banned users as admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -262,6 +276,7 @@ test('admin option should not be displayed for banned users as admin', async ()
expect(text).not.toContain('admin.setAdmin')
expect(text).not.toContain('admin.unsetAdmin')
})
+
test('banning option should be displayed for banned users as admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -275,6 +290,7 @@ test('banning option should be displayed for banned users as admin', async () =>
const text = wrapper.find('.vgt-table').text()
expect(text).toContain('admin.unban')
})
+
test('deletion button should not be displayed for super admins', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -285,6 +301,7 @@ test('deletion button should not be displayed for super admins', async () => {
await wrapper.vm.$nextTick()
expect(wrapper.find('.btn-danger').attributes('disabled')).toBe('disabled')
})
+
test('deletion button should be displayed for admins as super admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -297,6 +314,7 @@ test('deletion button should be displayed for admins as super admin', async () =
await wrapper.vm.$nextTick()
expect(wrapper.find('.btn-danger').attributes('disabled')).toBeNil()
})
+
test('deletion button should be displayed for normal users as super admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -309,6 +327,7 @@ test('deletion button should be displayed for normal users as super admin', asyn
await wrapper.vm.$nextTick()
expect(wrapper.find('.btn-danger').attributes('disabled')).toBeNil()
})
+
test('deletion button should be displayed for banned users as super admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -321,6 +340,7 @@ test('deletion button should be displayed for banned users as super admin', asyn
await wrapper.vm.$nextTick()
expect(wrapper.find('.btn-danger').attributes('disabled')).toBeNil()
})
+
test('deletion button should not be displayed for other admins as admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -333,6 +353,7 @@ test('deletion button should not be displayed for other admins as admin', async
await wrapper.vm.$nextTick()
expect(wrapper.find('.btn-danger').attributes('disabled')).toBe('disabled')
})
+
test('deletion button should be displayed for normal users as admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -345,6 +366,7 @@ test('deletion button should be displayed for normal users as admin', async () =
await wrapper.vm.$nextTick()
expect(wrapper.find('.btn-danger').attributes('disabled')).toBeNil()
})
+
test('deletion button should be displayed for banned users as admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -357,6 +379,7 @@ test('deletion button should be displayed for banned users as admin', async () =
await wrapper.vm.$nextTick()
expect(wrapper.find('.btn-danger').attributes('disabled')).toBeNil()
})
+
test('change email', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -391,6 +414,7 @@ test('change email', async () => {
await flushPromises()
expect(wrapper.text()).toContain('d@e.f')
})
+
test('toggle verification', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -415,6 +439,7 @@ test('toggle verification', async () => {
await flushPromises()
expect(wrapper.text()).toContain('admin.verified')
})
+
test('change nickname', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -449,6 +474,7 @@ test('change nickname', async () => {
await flushPromises()
expect(wrapper.text()).toContain('new')
})
+
test('change password', async () => {
jest.spyOn(toastr, 'success')
jest.spyOn(toastr, 'warning')
@@ -484,6 +510,7 @@ test('change password', async () => {
await flushPromises()
expect(toastr.warning).toBeCalledWith('1')
})
+
test('change score', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -515,6 +542,7 @@ test('change score', async () => {
await flushPromises()
expect(wrapper.text()).toContain('45')
})
+
test('toggle admin', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -547,6 +575,7 @@ test('toggle admin', async () => {
await flushPromises()
expect(wrapper.text()).toContain('admin.normal')
})
+
test('toggle ban', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
@@ -579,6 +608,7 @@ test('toggle ban', async () => {
await flushPromises()
expect(wrapper.text()).toContain('admin.ban')
})
+
test('delete user', async () => {
Vue.prototype.$http.get.mockResolvedValue({
data: [
diff --git a/resources/assets/tests/components/auth/Forgot.test.js b/resources/assets/tests/components/auth/Forgot.test.js
index 996454d6..235e6626 100644
--- a/resources/assets/tests/components/auth/Forgot.test.js
+++ b/resources/assets/tests/components/auth/Forgot.test.js
@@ -8,6 +8,7 @@ test('click to refresh captcha', () => {
wrapper.find('img').trigger('click')
expect(Date.now).toBeCalledTimes(2)
})
+
test('submit forgot form', async () => {
jest.spyOn(Date, 'now')
Vue.prototype.$http.post
diff --git a/resources/assets/tests/components/auth/Login.test.js b/resources/assets/tests/components/auth/Login.test.js
index 865abfcb..2aa04a8e 100644
--- a/resources/assets/tests/components/auth/Login.test.js
+++ b/resources/assets/tests/components/auth/Login.test.js
@@ -10,6 +10,7 @@ test('show captcha if too many login fails', () => {
const wrapper = mount(Login)
expect(wrapper.find('img').attributes('src')).toMatch(/\/auth\/captcha\?v=\d+/)
})
+
test('click to refresh captcha', () => {
window.blessing.extra = { tooManyFails: true }
jest.spyOn(Date, 'now')
@@ -17,6 +18,7 @@ test('click to refresh captcha', () => {
wrapper.find('img').trigger('click')
expect(Date.now).toBeCalledTimes(2)
})
+
test('login', async () => {
window.blessing.extra = { tooManyFails: false }
Vue.prototype.$http.post
diff --git a/resources/assets/tests/components/auth/Register.test.js b/resources/assets/tests/components/auth/Register.test.js
index 1eefee1e..5af297e0 100644
--- a/resources/assets/tests/components/auth/Register.test.js
+++ b/resources/assets/tests/components/auth/Register.test.js
@@ -13,6 +13,7 @@ test('click to refresh captcha', () => {
wrapper.find('img').trigger('click')
expect(Date.now).toBeCalledTimes(2)
})
+
test('require player name', () => {
window.blessing.extra = { player: true }
@@ -22,6 +23,7 @@ test('require player name', () => {
window.blessing.extra = { player: false }
})
+
test('register', async () => {
jest.spyOn(Date, 'now')
Vue.prototype.$http.post
@@ -97,6 +99,7 @@ test('register', async () => {
jest.runAllTimers()
expect(swal).toBeCalledWith({ type: 'success', text: 'ok' })
})
+
test('register with player name', async () => {
window.blessing.extra = { player: true }
Vue.prototype.$http.post.mockResolvedValue({ errno: 0, msg: 'ok' })
diff --git a/resources/assets/tests/components/common/Previewer.test.js b/resources/assets/tests/components/common/Previewer.test.js
index 3bec1849..d50f062c 100644
--- a/resources/assets/tests/components/common/Previewer.test.js
+++ b/resources/assets/tests/components/common/Previewer.test.js
@@ -12,21 +12,25 @@ test('initialize skinview3d', () => {
expect(wrapper.vm.viewer.camera.position.z).toBe(70)
expect(stub).toBeCalledWith(expect.any(HTMLElement))
})
+
test('dispose viewer before destroy', () => {
const wrapper = mount(Previewer)
wrapper.destroy()
expect(wrapper.vm.viewer.disposed).toBeTrue()
})
+
test('skin URL should be updated', () => {
const wrapper = mount(Previewer)
wrapper.setProps({ skin: 'abc' })
expect(wrapper.vm.viewer.skinUrl).toBe('abc')
})
+
test('cape URL should be updated', () => {
const wrapper = mount(Previewer)
wrapper.setProps({ cape: 'abc' })
expect(wrapper.vm.viewer.capeUrl).toBe('abc')
})
+
test('`footer` slot', () => {
const wrapper = mount(Previewer, {
slots: {
@@ -35,10 +39,12 @@ test('`footer` slot', () => {
})
expect(wrapper.find('#footer').exists()).toBeTrue()
})
+
test('disable closet mode', () => {
const wrapper = mount(Previewer)
expect(wrapper.find('.badge').text()).toBe('')
})
+
test('enable closet mode', () => {
const wrapper = mount(Previewer, {
propsData: {
@@ -56,6 +62,7 @@ test('enable closet mode', () => {
wrapper.setProps({ skin: 'abc', cape: 'abc' })
expect(wrapper.find('.badge').text()).toBe('general.skin & general.cape')
})
+
test('toggle pause', () => {
const wrapper = mount(Previewer)
const pauseButton = wrapper.find('.fa-pause')
@@ -64,17 +71,20 @@ test('toggle pause', () => {
expect(wrapper.find('.fa-play').exists()).toBeTrue()
expect(wrapper.find('.fa-pause').exists()).toBeFalse()
})
+
test('toggle run', () => {
const wrapper = mount(Previewer)
wrapper.find('.fa-forward').trigger('click')
expect(wrapper.vm.handles.run.paused).toBeFalse()
expect(wrapper.vm.handles.walk.paused).toBeTrue()
})
+
test('toggle rotate', () => {
const wrapper = mount(Previewer)
wrapper.find('.fa-redo-alt').trigger('click')
expect(wrapper.vm.handles.rotate.paused).toBeTrue()
})
+
test('reset', () => {
mockedSkinview3d.SkinViewer.prototype.dispose = jest.fn(function () {
this.disposed = true
@@ -83,6 +93,7 @@ test('reset', () => {
wrapper.find('.fa-stop').trigger('click')
expect(mockedSkinview3d.SkinViewer.prototype.dispose).toBeCalled()
})
+
test('custom title', () => {
const wrapper = mount(Previewer, { propsData: { title: 'custom-title' } })
expect(wrapper.text()).toContain('custom-title')
diff --git a/resources/assets/tests/components/skinlib/List.test.js b/resources/assets/tests/components/skinlib/List.test.js
index b6eb4780..ff3c0d61 100644
--- a/resources/assets/tests/components/skinlib/List.test.js
+++ b/resources/assets/tests/components/skinlib/List.test.js
@@ -22,6 +22,7 @@ test('empty skin library', () => {
const wrapper = mount(List)
expect(wrapper.text()).toContain('general.noResult')
})
+
test('toggle texture type', () => {
Vue.prototype.$http.get.mockResolvedValue({
items: [], total_pages: 0, current_uid: 0,
@@ -114,6 +115,7 @@ test('sort items', () => {
)
expect(wrapper.text()).toContain('skinlib.sort.time')
})
+
test('search by keyword', () => {
Vue.prototype.$http.get.mockResolvedValue({
items: [], total_pages: 0, current_uid: 0,
@@ -162,6 +164,7 @@ test('reset all filters', () => {
wrapper.find('.btn-warning').trigger('click')
expect(Vue.prototype.$http.get).toBeCalledTimes(1)
})
+
test('is anonymous', () => {
Vue.prototype.$http.get.mockResolvedValue({
items: [], total_pages: 0, current_uid: 0,
@@ -169,6 +172,7 @@ test('is anonymous', () => {
const wrapper = mount(List)
expect(wrapper.vm.anonymous).toBeTrue()
})
+
test('on page changed', () => {
Vue.prototype.$http.get.mockResolvedValue({
items: [], total_pages: 0, current_uid: 0,
diff --git a/resources/assets/tests/components/skinlib/Show.test.js b/resources/assets/tests/components/skinlib/Show.test.js
index 087dd5a7..ae10c909 100644
--- a/resources/assets/tests/components/skinlib/Show.test.js
+++ b/resources/assets/tests/components/skinlib/Show.test.js
@@ -32,6 +32,7 @@ test('button for adding to closet should be disabled if not auth', () => {
})
expect(wrapper.find('.btn-primary').attributes('disabled')).toBe('disabled')
})
+
test('button for adding to closet should be disabled if auth', () => {
Vue.prototype.$http.get.mockResolvedValue({})
Object.assign(window.blessing.extra, { inCloset: true, currentUid: 1 })
@@ -43,6 +44,7 @@ test('button for adding to closet should be disabled if auth', () => {
})
expect(wrapper.find('.btn-primary').text()).toBe('skinlib.removeFromCloset')
})
+
test('likes count indicator', async () => {
Vue.prototype.$http.get.mockResolvedValue({ likes: 2 })
const wrapper = mount(Show, {
@@ -55,6 +57,7 @@ test('likes count indicator', async () => {
expect(wrapper.find('.likes').attributes('style')).toContain('color: rgb(224, 53, 59)')
expect(wrapper.find('.likes').text()).toContain('2')
})
+
test('render basic information', async () => {
Vue.prototype.$http.get.mockResolvedValue({
name: 'my-texture',
@@ -77,6 +80,7 @@ test('render basic information', async () => {
expect(text).toContain('2018')
expect(text).toContain('author')
})
+
test('render action text of editing texture name', async () => {
Object.assign(window.blessing.extra, { admin: true })
Vue.prototype.$http.get.mockResolvedValue({ uploader: 1, name: 'name' })
@@ -98,6 +102,7 @@ test('render action text of editing texture name', async () => {
await wrapper.vm.$nextTick()
expect(wrapper.contains('small')).toBeFalse()
})
+
test('render nickname of uploader', () => {
Object.assign(window.blessing.extra, { nickname: null })
Vue.prototype.$http.get.mockResolvedValue({})
@@ -108,6 +113,7 @@ test('render nickname of uploader', () => {
})
expect(wrapper.text()).toContain('general.unexistent-user')
})
+
test('operation panel should not be rendered if not auth', () => {
Object.assign(window.blessing.extra, { currentUid: 0 })
Vue.prototype.$http.get.mockResolvedValue({})
@@ -118,6 +124,7 @@ test('operation panel should not be rendered if not auth', () => {
})
expect(wrapper.contains('.box-warning')).toBeFalse()
})
+
test('link to downloading texture', async () => {
Object.assign(window.blessing.extra, { download: false })
Vue.prototype.$http.get.mockResolvedValue({ hash: '123' })
@@ -130,6 +137,7 @@ test('link to downloading texture', async () => {
expect(wrapper.contains('a[title="123"]')).toBeFalse()
expect(wrapper.contains('span[title="123"]')).toBeTrue()
})
+
test('set as avatar', () => {
Object.assign(window.blessing.extra, { currentUid: 1, inCloset: true })
Vue.prototype.$http.get.mockResolvedValueOnce({ type: 'steve' })
@@ -151,6 +159,7 @@ test('set as avatar', () => {
})
expect(noSetAsAvatar.find('button.btn-default').isEmpty()).toBeTrue()
})
+
test('add to closet', async () => {
Object.assign(window.blessing.extra, { currentUid: 1, inCloset: false })
Vue.prototype.$http.get.mockResolvedValue({ name: 'wow', likes: 2 })
@@ -190,6 +199,7 @@ test('add to closet', async () => {
expect(wrapper.vm.likes).toBe(3)
expect(wrapper.vm.liked).toBeTrue()
})
+
test('remove from closet', async () => {
Object.assign(window.blessing.extra, { currentUid: 1, inCloset: true })
Vue.prototype.$http.get.mockResolvedValue({ likes: 2 })
@@ -223,6 +233,7 @@ test('remove from closet', async () => {
expect(wrapper.vm.likes).toBe(1)
expect(wrapper.vm.liked).toBeFalse()
})
+
test('change texture name', async () => {
Object.assign(window.blessing.extra, { admin: true })
Vue.prototype.$http.get.mockResolvedValue({ name: 'old-name' })
@@ -259,6 +270,7 @@ test('change texture name', async () => {
await flushPromises()
expect(wrapper.vm.name).toBe('new-name')
})
+
test('change texture model', async () => {
Vue.prototype.$http.get.mockResolvedValue({ type: 'steve' })
Vue.prototype.$http.post
@@ -291,6 +303,7 @@ test('change texture model', async () => {
await flushPromises()
expect(wrapper.vm.type).toBe('alex')
})
+
test('toggle privacy', async () => {
Vue.prototype.$http.get.mockResolvedValue({ public: true })
Vue.prototype.$http.post
@@ -326,6 +339,7 @@ test('toggle privacy', async () => {
await flushPromises()
expect(wrapper.vm.public).toBeTrue()
})
+
test('delete texture', async () => {
Vue.prototype.$http.get.mockResolvedValue({})
Vue.prototype.$http.post
diff --git a/resources/assets/tests/components/skinlib/SkinLibItem.test.js b/resources/assets/tests/components/skinlib/SkinLibItem.test.js
index 98741205..4b2eaf1e 100644
--- a/resources/assets/tests/components/skinlib/SkinLibItem.test.js
+++ b/resources/assets/tests/components/skinlib/SkinLibItem.test.js
@@ -15,6 +15,7 @@ test('urls', () => {
expect(wrapper.find('a').attributes('href')).toBe('/skinlib/show/1')
expect(wrapper.find('img').attributes('src')).toBe('/preview/1.png')
})
+
test('render basic information', () => {
const wrapper = mount(SkinLibItem, {
propsData: {
@@ -26,6 +27,7 @@ test('render basic information', () => {
expect(wrapper.text()).toContain('test')
expect(wrapper.text()).toContain('skinlib.filter.steve')
})
+
test('anonymous user', () => {
const wrapper = mount(SkinLibItem, {
propsData: { anonymous: true },
@@ -35,6 +37,7 @@ test('anonymous user', () => {
button.trigger('click')
expect(Vue.prototype.$http.post).not.toBeCalled()
})
+
test('private texture', () => {
const wrapper = mount(SkinLibItem, {
propsData: { isPublic: false },
@@ -44,6 +47,7 @@ test('private texture', () => {
wrapper.setProps({ isPublic: true })
expect(wrapper.text()).not.toContain('skinlib.private')
})
+
test('liked state', () => {
const wrapper = mount(SkinLibItem, {
propsData: { liked: true, anonymous: false },
@@ -57,6 +61,7 @@ test('liked state', () => {
expect(button.attributes('title')).toBe('skinlib.addToCloset')
expect(button.classes('liked')).toBeFalse()
})
+
test('remove from closet', async () => {
Vue.prototype.$http.post
.mockResolvedValueOnce({ errno: 1, msg: '1' })
@@ -86,6 +91,7 @@ test('remove from closet', async () => {
await flushPromises()
expect(wrapper.emitted('like-toggled')[0]).toEqual([false])
})
+
test('add to closet', async () => {
Vue.prototype.$http.post
.mockResolvedValueOnce({ errno: 1, msg: '1' })
diff --git a/resources/assets/tests/components/skinlib/Upload.test.js b/resources/assets/tests/components/skinlib/Upload.test.js
index f4c71b77..afc59549 100644
--- a/resources/assets/tests/components/skinlib/Upload.test.js
+++ b/resources/assets/tests/components/skinlib/Upload.test.js
@@ -30,6 +30,7 @@ test('display drap and drop notice', () => {
wrapper.setData({ files: [{}] })
expect(wrapper.contains('img')).toBeTrue()
})
+
test('button for removing texture', () => {
const wrapper = mount(Upload, {
stubs: ['file-upload'],
@@ -46,6 +47,7 @@ test('button for removing texture', () => {
button.trigger('click')
expect(wrapper.vm.texture).toBe('')
})
+
test('notice should be display if texture is private', () => {
const wrapper = mount(Upload, {
stubs: ['file-upload'],
@@ -54,6 +56,7 @@ test('notice should be display if texture is private', () => {
wrapper.find('[type=checkbox]').setChecked()
expect(wrapper.find('.callout').text()).toBe('privacyNotice')
})
+
test('display score cost', () => {
const origin = Vue.prototype.$t
Vue.prototype.$t = (key, args) => `${key}${JSON.stringify(args)}`
@@ -67,6 +70,7 @@ test('display score cost', () => {
Vue.prototype.$t = origin
})
+
test('process input file', () => {
window.URL.createObjectURL = jest.fn().mockReturnValue('file-url')
jest.spyOn(window, 'Image')
@@ -107,6 +111,7 @@ test('process input file', () => {
window.Image.mockRestore()
})
+
test('upload file', async () => {
window.Request = jest.fn()
Vue.prototype.$http.post
diff --git a/resources/assets/tests/components/user/Closet.test.js b/resources/assets/tests/components/user/Closet.test.js
index 21f0e458..0c4c4297 100644
--- a/resources/assets/tests/components/user/Closet.test.js
+++ b/resources/assets/tests/components/user/Closet.test.js
@@ -74,6 +74,7 @@ test('different categories', () => {
.classes('active')).toBeTrue()
expect(wrapper.find('#cape-category').classes('active')).toBeTrue()
})
+
test('search textures', () => {
Vue.prototype.$http.get.mockResolvedValue({})
@@ -100,6 +101,7 @@ test('empty closet', () => {
wrapper.setData({ category: 'cape' })
expect(wrapper.find('#cape-category').text()).toContain('user.emptyClosetMsg')
})
+
test('no matched search result', () => {
Vue.prototype.$http.get.mockResolvedValue({})
const wrapper = mount(Closet)
@@ -108,6 +110,7 @@ test('no matched search result', () => {
wrapper.setData({ category: 'cape' })
expect(wrapper.find('#cape-category').text()).toContain('general.noResult')
})
+
test('render items', async () => {
Vue.prototype.$http.get.mockResolvedValue({
items: [
@@ -121,6 +124,7 @@ test('render items', async () => {
await wrapper.vm.$nextTick()
expect(wrapper.findAll(ClosetItem)).toHaveLength(2)
})
+
test('reload closet when page changed', () => {
Vue.prototype.$http.get.mockResolvedValue({})
const wrapper = mount(Closet)
@@ -128,6 +132,7 @@ test('reload closet when page changed', () => {
jest.runAllTicks()
expect(Vue.prototype.$http.get).toBeCalledTimes(2)
})
+
test('remove skin item', () => {
Vue.prototype.$http.get.mockResolvedValue({})
const wrapper = mount(Closet)
@@ -135,6 +140,7 @@ test('remove skin item', () => {
wrapper.vm.removeSkinItem(0)
expect(wrapper.find('#skin-category').text()).toContain('user.emptyClosetMsg')
})
+
test('remove cape item', () => {
Vue.prototype.$http.get.mockResolvedValue({})
const wrapper = mount(Closet)
@@ -142,12 +148,14 @@ test('remove cape item', () => {
wrapper.vm.removeCapeItem(0)
expect(wrapper.find('#cape-category').text()).toContain('user.emptyClosetMsg')
})
+
test('compute avatar URL', () => {
Vue.prototype.$http.get.mockResolvedValue({})
const wrapper = mount(Closet)
const { avatarUrl } = wrapper.vm
expect(avatarUrl({ tid_skin: 1 })).toBe('/avatar/35/1')
})
+
test('select texture', async () => {
Vue.prototype.$http.get
.mockResolvedValueOnce({})
@@ -169,6 +177,7 @@ test('select texture', async () => {
expect(Vue.prototype.$http.get).toBeCalledWith('/skinlib/info/2')
expect(wrapper.vm.capeUrl).toBe('/textures/b')
})
+
test('apply texture', async () => {
window.$ = jest.fn(() => ({
iCheck: () => ({
@@ -203,6 +212,7 @@ test('apply texture', async () => {
expect(wrapper.find('.modal-body').text()).toContain('name')
jest.runAllTimers()
})
+
test('submit applying texture', async () => {
window.$ = jest.fn(() => ({ modal() {} }))
jest.spyOn(toastr, 'info')
@@ -246,6 +256,7 @@ test('submit applying texture', async () => {
await wrapper.vm.$nextTick()
expect(swal).toBeCalledWith({ type: 'success', text: 'ok' })
})
+
test('reset selected texture', () => {
Vue.prototype.$http.get.mockResolvedValue({})
const wrapper = mount(Closet)
@@ -265,6 +276,7 @@ test('reset selected texture', () => {
capeUrl: '',
}))
})
+
test('select specified texture initially', async () => {
window.history.pushState({}, 'title', `${location.href}?tid=1`)
window.$ = jest.fn(() => ({
diff --git a/resources/assets/tests/components/user/ClosetItem.test.js b/resources/assets/tests/components/user/ClosetItem.test.js
index 0cfc57de..007190b0 100644
--- a/resources/assets/tests/components/user/ClosetItem.test.js
+++ b/resources/assets/tests/components/user/ClosetItem.test.js
@@ -20,10 +20,12 @@ test('computed values', () => {
expect(wrapper.find('img').attributes('src')).toBe('/preview/1.png')
expect(wrapper.find('a.more').attributes('href')).toBe('/skinlib/show/1')
})
+
test('selected item', () => {
const wrapper = mount(ClosetItem, { propsData: factory({ selected: true }) })
expect(wrapper.find('.item').classes('item-selected')).toBeTrue()
})
+
test('click item body', () => {
const wrapper = mount(ClosetItem, { propsData: factory() })
@@ -33,6 +35,7 @@ test('click item body', () => {
wrapper.find('.item-body').trigger('click')
expect(wrapper.emitted().select).toBeTruthy()
})
+
test('rename texture', async () => {
Vue.prototype.$http.post
.mockResolvedValueOnce({ errno: 0 })
diff --git a/resources/assets/tests/components/user/Dashboard.test.js b/resources/assets/tests/components/user/Dashboard.test.js
index c8aaa37f..3f6ec812 100644
--- a/resources/assets/tests/components/user/Dashboard.test.js
+++ b/resources/assets/tests/components/user/Dashboard.test.js
@@ -31,6 +31,7 @@ test('fetch score info', () => {
mount(Dashboard)
expect(Vue.prototype.$http.get).toBeCalledWith('/user/score-info')
})
+
test('players usage', async () => {
Vue.prototype.$http.get.mockResolvedValue(scoreInfo())
const wrapper = mount(Dashboard)
@@ -38,6 +39,7 @@ test('players usage', async () => {
expect(wrapper.text()).toContain('3 / 15')
expect(wrapper.find('.progress-bar-aqua').attributes('style')).toBe('width: 20%;')
})
+
test('storage usage', async () => {
Vue.prototype.$http.get
.mockResolvedValueOnce(scoreInfo())
@@ -61,12 +63,14 @@ test('storage usage', async () => {
expect(wrapper.text()).toContain('2 / 4 MB')
expect(wrapper.find('.progress-bar-yellow').attributes('style')).toBe('width: 50%;')
})
+
test('display score', async () => {
Vue.prototype.$http.get.mockResolvedValue(scoreInfo())
const wrapper = mount(Dashboard)
await wrapper.vm.$nextTick()
expect(wrapper.find('#score').text()).toContain('835')
})
+
test('button `sign` state', async () => {
Vue.prototype.$http.get
.mockResolvedValueOnce(scoreInfo({ signAfterZero: true }))
@@ -93,6 +97,7 @@ test('button `sign` state', async () => {
await wrapper.vm.$nextTick()
expect(wrapper.find('button').attributes('disabled')).toBe('disabled')
})
+
test('remaining time', async () => {
const origin = Vue.prototype.$t
Vue.prototype.$t = (key, args) => key + JSON.stringify(args)
@@ -117,6 +122,7 @@ test('remaining time', async () => {
Vue.prototype.$t = origin
})
+
test('sign', async () => {
jest.spyOn(toastr, 'warning')
swal.mockResolvedValue()
diff --git a/resources/assets/tests/components/user/EmailVerification.test.js b/resources/assets/tests/components/user/EmailVerification.test.js
index 164c1d94..a66e8a53 100644
--- a/resources/assets/tests/components/user/EmailVerification.test.js
+++ b/resources/assets/tests/components/user/EmailVerification.test.js
@@ -10,6 +10,7 @@ test('message box should not be render if verified', () => {
const wrapper = mount(EmailVerification)
expect(wrapper.isEmpty()).toBeTrue()
})
+
test('resend email', async () => {
window.blessing.extra = { unverified: true }
Vue.prototype.$http.post
diff --git a/resources/assets/tests/components/user/Players.test.js b/resources/assets/tests/components/user/Players.test.js
index 3a9f6e40..2b838468 100644
--- a/resources/assets/tests/components/user/Players.test.js
+++ b/resources/assets/tests/components/user/Players.test.js
@@ -18,11 +18,13 @@ test('display player name constraints', () => {
expect(text).toContain('rule')
expect(text).toContain('length')
})
+
test('fetch players data before mount', () => {
Vue.prototype.$http.get.mockResolvedValue([])
mount(Players)
expect(Vue.prototype.$http.get).toBeCalledWith('/user/player/list')
})
+
test('click to preview player', async () => {
Vue.prototype.$http.get
.mockResolvedValueOnce([
@@ -56,6 +58,7 @@ test('click to preview player', async () => {
await flushPromises()
expect(Vue.prototype.$http.get).toBeCalledWith('/skinlib/info/2')
})
+
test('change player name', async () => {
Vue.prototype.$http.get
.mockResolvedValueOnce([
@@ -89,6 +92,7 @@ test('change player name', async () => {
await flushPromises()
expect(wrapper.text()).toContain('new-name')
})
+
test('load iCheck', async () => {
Vue.prototype.$http.get
.mockResolvedValueOnce([
@@ -109,6 +113,7 @@ test('load iCheck', async () => {
wrapper.find('.btn-warning').trigger('click')
expect(window.$).toBeCalled()
})
+
test('delete player', async () => {
Vue.prototype.$http.get
.mockResolvedValueOnce([
@@ -133,12 +138,14 @@ test('delete player', async () => {
await flushPromises()
expect(wrapper.text()).not.toContain('to-be-deleted')
})
+
test('toggle preview mode', () => {
Vue.prototype.$http.get.mockResolvedValueOnce([])
const wrapper = mount(Players)
wrapper.find('[data-test="to2d"]').trigger('click')
expect(wrapper.text()).toContain('user.player.texture-empty')
})
+
test('add player', async () => {
window.$ = jest.fn(() => ({ modal() {} }))
Vue.prototype.$http.get.mockResolvedValueOnce([])
@@ -163,6 +170,7 @@ test('add player', async () => {
await flushPromises()
expect(Vue.prototype.$http.get).toBeCalledTimes(2)
})
+
test('clear texture', async () => {
window.$ = jest.fn(() => ({ modal() {} }))
Vue.prototype.$http.get.mockResolvedValueOnce([
diff --git a/resources/assets/tests/components/user/Profile.test.js b/resources/assets/tests/components/user/Profile.test.js
index a1ca3386..3bc865e4 100644
--- a/resources/assets/tests/components/user/Profile.test.js
+++ b/resources/assets/tests/components/user/Profile.test.js
@@ -17,6 +17,7 @@ test('computed values', () => {
window.blessing.extra = { admin: false }
expect(mount(Profile).vm.isAdmin).toBeFalse()
})
+
test('convert linebreak', () => {
const wrapper = mount(Profile)
expect(wrapper.vm.nl2br('a\nb\nc')).toBe('a
b
c')
@@ -87,6 +88,7 @@ test('change password', async () => {
await wrapper.vm.$nextTick()
expect(swal).toBeCalledWith({ type: 'success', text: 'o' })
})
+
test('change nickname', async () => {
Vue.prototype.$http.post
.mockResolvedValueOnce({ errno: 1, msg: 'w' })
@@ -129,6 +131,7 @@ test('change nickname', async () => {
await flushPromises()
expect(swal).toBeCalledWith({ type: 'success', text: 'o' })
})
+
test('change email', async () => {
Vue.prototype.$http.post
.mockResolvedValueOnce({ errno: 1, msg: 'w' })
@@ -171,6 +174,7 @@ test('change email', async () => {
await flushPromises()
expect(swal).toBeCalledWith({ type: 'success', text: 'o' })
})
+
test('delete account', async () => {
window.blessing.extra = { admin: true }
swal.mockResolvedValue()
diff --git a/resources/assets/tests/js/check-updates.test.js b/resources/assets/tests/js/check-updates.test.js
index 16c85664..f1ef3474 100644
--- a/resources/assets/tests/js/check-updates.test.js
+++ b/resources/assets/tests/js/check-updates.test.js
@@ -30,6 +30,7 @@ test('check for BS updates', async () => {
await checkForUpdates()
expect(document.querySelector('a').innerHTML).toContain('4.0.0')
})
+
test('check for plugins updates', async () => {
window.fetch = jest.fn()
.mockResolvedValueOnce({ ok: false })
diff --git a/resources/assets/tests/js/event.test.js b/resources/assets/tests/js/event.test.js
index 457c9b29..ea1dc67a 100644
--- a/resources/assets/tests/js/event.test.js
+++ b/resources/assets/tests/js/event.test.js
@@ -3,6 +3,7 @@ import * as emitter from '@/js/event'
test('mount variable to global', () => {
expect(window.bsEmitter).toBeFrozen()
})
+
test('add listener and emit event', () => {
const mockA = jest.fn()
const mockB = jest.fn()
diff --git a/resources/assets/tests/js/i18n.test.js b/resources/assets/tests/js/i18n.test.js
index 54807a57..ab19b528 100644
--- a/resources/assets/tests/js/i18n.test.js
+++ b/resources/assets/tests/js/i18n.test.js
@@ -4,6 +4,7 @@ import Vue from 'vue'
test('mount to global', () => {
expect(window.trans).toBe(trans)
})
+
test('translate text', () => {
window.blessing.i18n = { a: { b: { c: 'text', d: 'Hi, :name!' } } }
expect(trans('a.b.c')).toBe('text')
@@ -11,6 +12,7 @@ test('translate text', () => {
expect(trans('a.b.d', { name: 'me' })).toBe('Hi, me!')
expect(trans('d.e')).toBe('d.e')
})
+
test('Vue directive', () => {
const byString = Vue.extend({
render(h) {
diff --git a/resources/assets/tests/js/net.test.js b/resources/assets/tests/js/net.test.js
index f5afd66d..5d2190f1 100644
--- a/resources/assets/tests/js/net.test.js
+++ b/resources/assets/tests/js/net.test.js
@@ -33,6 +33,7 @@ test('the GET method', async () => {
await net.get('/abc')
expect(window.fetch.mock.calls[1][0].url).toBe('/abc')
})
+
test('the POST method', async () => {
window.fetch = jest.fn()
.mockResolvedValue({
@@ -74,6 +75,7 @@ test('the POST method', async () => {
await net.post('/abc')
expect(window.fetch.mock.calls[2][0].body).toBe('{}')
})
+
test('low level fetch', async () => {
const json = jest.fn().mockResolvedValue({})
window.fetch = jest.fn()
diff --git a/resources/assets/tests/js/notify.test.js b/resources/assets/tests/js/notify.test.js
index aee36c40..1b6b5314 100644
--- a/resources/assets/tests/js/notify.test.js
+++ b/resources/assets/tests/js/notify.test.js
@@ -18,6 +18,7 @@ test('show message', () => {
expect(element.hasClass('callout-info')).toBeTrue()
expect(element.html()).toBe('hi')
})
+
test('show AJAX error', () => {
$.fn.modal = function () {
document.body.innerHTML = this.html()
@@ -25,6 +26,7 @@ test('show AJAX error', () => {
notify.showAjaxError(new Error('an-error'))
expect(document.body.innerHTML).toContain('an-error')
})
+
test('show modal', () => {
notify.showModal('message')
expect($('.modal-title').html()).toBe('Message')
@@ -34,6 +36,7 @@ test('show modal', () => {
destroyOnClose: false,
})
})
+
test('show sweetalert', () => {
jest.spyOn(Swal, 'fire')
notify.swal({})
diff --git a/resources/assets/tests/js/utils.test.js b/resources/assets/tests/js/utils.test.js
index bc2c6b9a..1ec18194 100644
--- a/resources/assets/tests/js/utils.test.js
+++ b/resources/assets/tests/js/utils.test.js
@@ -10,12 +10,14 @@ test('debounce', () => {
jest.runAllTimers()
expect(stub).toBeCalledTimes(1)
})
+
test('queryString', () => {
history.pushState({}, 'page', `${location.href}?key=value`)
expect(utils.queryString('key')).toBe('value')
expect(utils.queryString('a')).toBeUndefined()
expect(utils.queryString('a', 'b')).toBe('b')
})
+
test('queryStringify', () => {
expect(utils.queryStringify({ a: 'b', c: 'd' })).toBe('a=b&c=d')
})
diff --git a/yarn.lock b/yarn.lock
index 95bb3885..f0a4f821 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3222,6 +3222,13 @@ eslint-plugin-node@^8.0.1:
resolve "^1.8.1"
semver "^5.5.0"
+eslint-plugin-project@^0.2.2:
+ version "0.2.2"
+ resolved "https://registry.npmjs.org/eslint-plugin-project/-/eslint-plugin-project-0.2.2.tgz#aace7d7d252dc2403a3167cecf59228b80d983f3"
+ integrity sha512-76UJdTXg3+y0ix/xRjG2DjWumxV6oUYe7q/rEPDddGgxUpv08ybn4DNzm3h5MOqIZ9qY+m6CG1x6o/fAvQZusQ==
+ dependencies:
+ js-yaml "^3.12.1"
+
eslint-plugin-vue@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-5.2.2.tgz#86601823b7721b70bc92d54f1728cfc03b36283c"
@@ -5132,6 +5139,14 @@ js-yaml@^3.12.0, js-yaml@^3.5.2, js-yaml@^3.9.0:
argparse "^1.0.7"
esprima "^4.0.0"
+js-yaml@^3.12.1:
+ version "3.12.2"
+ resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.2.tgz#ef1d067c5a9d9cb65bd72f285b5d8105c77f14fc"
+ integrity sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q==
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^4.0.0"
+
js-yaml@~3.10.0:
version "3.10.0"
resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc"