diff --git a/.eslintrc.js b/.eslintrc.js
index 53f17a9422..1021c7404e 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -17,6 +17,30 @@ module.exports = {
'@vue/standard',
'@vue/typescript/recommended',
'@nextcloud',
+ 'plugin:@typescript-eslint/recommended',
],
ignorePatterns: ['*.d.ts'],
+ rules: {
+ 'no-console': 'warn',
+ '@typescript-eslint/no-var-requires': 'off',
+
+ // TODO: Trouble importing .ts files into .vue files for some reason?
+ 'import/extensions': 'off',
+ 'n/no-missing-import': 'off',
+ },
+ settings: {
+ 'import/resolver': {
+ node: {
+ extensions: ['.ts'],
+ },
+ },
+ },
+ overrides: [
+ {
+ files: ['*spec.ts', 'tests/javascript/unit/setup.ts'],
+ rules: {
+ '@typescript-eslint/no-explicit-any': 'off',
+ },
+ },
+ ],
}
diff --git a/package-lock.json b/package-lock.json
index 48ae5985ee..9279f9e1dd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -46,7 +46,7 @@
"eslint": "^8.6.0",
"eslint-config-standard": "^17.0.0",
"eslint-import-resolver-webpack": "^0.12.2",
- "eslint-plugin-import": "^2.22.0",
+ "eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsdoc": "^39.2.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.0.0",
@@ -82,13 +82,14 @@
"vue-color": "^2.8.1",
"vue-eslint-parser": "^9.0.2",
"vue-jest": "^3.0.7",
- "vue-loader": "^15.9.8",
+ "vue-loader": "^15.10.1",
"vue-material-design-icons": "^5.1.2",
"vue-multiselect": "^2.1.6",
"vue-template-compiler": "^2.6.14",
"vue2-datepicker": "^3.11.0",
"webpack": "^5.72.1",
- "webpack-cli": "^4.9.2"
+ "webpack-cli": "^4.9.2",
+ "webpack-merge": "^5.8.0"
},
"engines": {
"node": "^16.0.0",
@@ -14885,6 +14886,15 @@
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
}
},
+ "node_modules/tapable": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/terser": {
"version": "5.15.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
@@ -15161,14 +15171,6 @@
"node": ">=10.13.0"
}
},
- "node_modules/ts-loader/node_modules/tapable": {
- "version": "2.2.1",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/tsconfig": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz",
@@ -15659,9 +15661,9 @@
}
},
"node_modules/vue-loader": {
- "version": "15.10.0",
- "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.0.tgz",
- "integrity": "sha512-VU6tuO8eKajrFeBzMssFUP9SvakEeeSi1BxdTH5o3+1yUyrldp8IERkSdXlMI2t4kxF2sqYUDsQY+WJBxzBmZg==",
+ "version": "15.10.1",
+ "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.1.tgz",
+ "integrity": "sha512-SaPHK1A01VrNthlix6h1hq4uJu7S/z0kdLUb6klubo738NeQoLbS6V9/d8Pv19tU0XdQKju3D1HSKuI8wJ5wMA==",
"dev": true,
"dependencies": {
"@vue/component-compiler-utils": "^3.1.0",
@@ -16240,15 +16242,6 @@
"node": ">=6.11.5"
}
},
- "node_modules/webpack/node_modules/tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/webpack/node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
@@ -27425,6 +27418,12 @@
}
}
},
+ "tapable": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "dev": true
+ },
"terser": {
"version": "5.15.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
@@ -27607,10 +27606,6 @@
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
}
- },
- "tapable": {
- "version": "2.2.1",
- "dev": true
}
}
},
@@ -28003,9 +27998,9 @@
}
},
"vue-loader": {
- "version": "15.10.0",
- "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.0.tgz",
- "integrity": "sha512-VU6tuO8eKajrFeBzMssFUP9SvakEeeSi1BxdTH5o3+1yUyrldp8IERkSdXlMI2t4kxF2sqYUDsQY+WJBxzBmZg==",
+ "version": "15.10.1",
+ "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.1.tgz",
+ "integrity": "sha512-SaPHK1A01VrNthlix6h1hq4uJu7S/z0kdLUb6klubo738NeQoLbS6V9/d8Pv19tU0XdQKju3D1HSKuI8wJ5wMA==",
"dev": true,
"requires": {
"@vue/component-compiler-utils": "^3.1.0",
@@ -28232,12 +28227,6 @@
"integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
"dev": true
},
- "tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "dev": true
- },
"watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
diff --git a/package.json b/package.json
index 079dead08e..a181d2614b 100644
--- a/package.json
+++ b/package.json
@@ -7,8 +7,8 @@
"build": "NODE_ENV=production webpack --progress --config webpack.js",
"dev": "NODE_ENV=development webpack --progress --config webpack.js",
"watch": "NODE_ENV=development webpack --progress --watch --config webpack.js",
- "lint": "eslint --ext .js,.vue src",
- "lint:fix": "eslint --ext .js,.vue src --fix",
+ "lint": "eslint --ext .js,.vue,.ts src",
+ "lint:fix": "eslint --ext .js,.vue,.ts src --fix",
"stylelint": "stylelint **/*.css **/*.scss **/*.vue",
"stylelint:fix": "stylelint **/*.css **/*.scss **/*.vue --fix",
"test": "jest --verbose",
@@ -82,7 +82,7 @@
"eslint": "^8.6.0",
"eslint-config-standard": "^17.0.0",
"eslint-import-resolver-webpack": "^0.12.2",
- "eslint-plugin-import": "^2.22.0",
+ "eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsdoc": "^39.2.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.0.0",
@@ -118,13 +118,14 @@
"vue-color": "^2.8.1",
"vue-eslint-parser": "^9.0.2",
"vue-jest": "^3.0.7",
- "vue-loader": "^15.9.8",
+ "vue-loader": "^15.10.1",
"vue-material-design-icons": "^5.1.2",
"vue-multiselect": "^2.1.6",
"vue-template-compiler": "^2.6.14",
"vue2-datepicker": "^3.11.0",
"webpack": "^5.72.1",
- "webpack-cli": "^4.9.2"
+ "webpack-cli": "^4.9.2",
+ "webpack-merge": "^5.8.0"
},
"jest": {
"preset": "ts-jest",
diff --git a/src/App.vue b/src/App.vue
index ff6bf6c432..076fed19cb 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -13,6 +13,7 @@ import Vue from 'vue'
import NcContent from '@nextcloud/vue/dist/Components/NcContent.js'
import NcAppContent from '@nextcloud/vue/dist/Components/NcAppContent.js'
import Sidebar from './components/Sidebar.vue'
+import { ACTIONS } from './store'
export default Vue.extend({
components: {
@@ -20,8 +21,9 @@ export default Vue.extend({
Sidebar,
NcAppContent,
},
- created() {
- this.$store.dispatch('loadFolder')
+ async created() {
+ await this.$store.dispatch(ACTIONS.FETCH_FOLDERS)
+ await this.$store.dispatch(ACTIONS.FETCH_FEEDS)
},
})
diff --git a/src/components/AddFeed.vue b/src/components/AddFeed.vue
index 5d50b7b4e1..af5d759374 100644
--- a/src/components/AddFeed.vue
+++ b/src/components/AddFeed.vue
@@ -107,12 +107,15 @@
diff --git a/src/types/Feed.ts b/src/types/Feed.ts
new file mode 100644
index 0000000000..9e06aca3cf
--- /dev/null
+++ b/src/types/Feed.ts
@@ -0,0 +1,8 @@
+export type Feed = {
+ folderId?: number;
+ unreadCount: number;
+ url: string;
+ title?: string;
+ autoDiscover?: boolean;
+ faviconLink?: string;
+}
diff --git a/src/types/Feed.vue b/src/types/Feed.vue
deleted file mode 100644
index eb2e8a0983..0000000000
--- a/src/types/Feed.vue
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/src/types/Folder.ts b/src/types/Folder.ts
new file mode 100644
index 0000000000..533cc93b7f
--- /dev/null
+++ b/src/types/Folder.ts
@@ -0,0 +1,8 @@
+import { Feed } from './Feed'
+
+export type Folder = {
+ feeds: Feed[];
+ feedCount: number;
+ name: string;
+ id: number;
+}
diff --git a/src/types/Folder.vue b/src/types/Folder.vue
deleted file mode 100644
index dd9b5caf38..0000000000
--- a/src/types/Folder.vue
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/tests/javascript/unit/components/AddFeed.spec.ts b/tests/javascript/unit/components/AddFeed.spec.ts
index a549ffb305..66105e3f8a 100644
--- a/tests/javascript/unit/components/AddFeed.spec.ts
+++ b/tests/javascript/unit/components/AddFeed.spec.ts
@@ -1,14 +1,23 @@
-import { shallowMount } from '@vue/test-utils'
-import { store, localVue } from '../setupStore'
+import { shallowMount, createLocalVue } from '@vue/test-utils'
-import AddFeed from 'Components/AddFeed.vue'
+import AddFeed from '../../../../src/components/AddFeed.vue'
describe('AddFeed.vue', () => {
'use strict'
it('should initialize without showing createNewFolder', () => {
- const wrapper = shallowMount(AddFeed, { localVue, store })
+ const localVue = createLocalVue()
+ const wrapper = shallowMount(AddFeed, {
+ localVue,
+ mocks: {
+ $store: {
+ state: {
+ folders: [],
+ },
+ },
+ },
+ })
- expect(wrapper.vm.$data.createNewFolder).toBeFalsy
- });
-});
\ No newline at end of file
+ expect(wrapper.vm.$data.createNewFolder).toBeFalsy()
+ })
+})
diff --git a/tests/javascript/unit/components/AdminSettings.spec.ts b/tests/javascript/unit/components/AdminSettings.spec.ts
index 91f03e62ed..06e88a6ccf 100644
--- a/tests/javascript/unit/components/AdminSettings.spec.ts
+++ b/tests/javascript/unit/components/AdminSettings.spec.ts
@@ -1,65 +1,64 @@
-import axios from '@nextcloud/axios';
-import { shallowMount, Wrapper } from '@vue/test-utils';
-import { store, localVue } from '../setupStore';
-import { showError, showSuccess } from '@nextcloud/dialogs';
-import { loadState } from '@nextcloud/initial-state';
+import axios from '@nextcloud/axios'
+import { createLocalVue, shallowMount, Wrapper } from '@vue/test-utils'
+import { showError, showSuccess } from '@nextcloud/dialogs'
+import { loadState } from '@nextcloud/initial-state'
import 'regenerator-runtime/runtime' // NOTE: Required for testing password-confirmation?
-import AdminSettings from 'Components/AdminSettings.vue';
+import AdminSettings from '../../../../src/components/AdminSettings.vue'
-
-jest.mock('@nextcloud/axios');
-jest.mock('@nextcloud/initial-state');
-jest.mock('@nextcloud/router');
-jest.mock('@nextcloud/dialogs');
+jest.mock('@nextcloud/axios')
+jest.mock('@nextcloud/initial-state')
+jest.mock('@nextcloud/router')
+jest.mock('@nextcloud/dialogs')
describe('AdminSettings.vue', () => {
- 'use strict';
+ 'use strict'
- let wrapper: Wrapper;
+ let wrapper: Wrapper
beforeAll(() => {
- jest.useFakeTimers();
- (loadState as any).mockReturnValue('');
- wrapper = shallowMount(AdminSettings, { localVue, store});
- });
+ jest.useFakeTimers()
+ const localVue = createLocalVue();
+ (loadState as any).mockReturnValue('')
+ wrapper = shallowMount(AdminSettings, { localVue })
+ })
it('should initialize and fetch settings from state', () => {
- expect(loadState).toBeCalledTimes(7);
- });
+ expect(loadState).toBeCalledTimes(7)
+ })
it('should send post with updated settings', async () => {
jest.spyOn(axios, 'post').mockResolvedValue({ data: {} });
(wrapper.vm as any).handleResponse = jest.fn()
- await wrapper.vm.$options?.methods?.update.call(wrapper.vm, 'key', 'val');
+ await wrapper.vm.$options?.methods?.update.call(wrapper.vm, 'key', 'val')
- expect(axios.post).toBeCalledTimes(1);
- });
+ expect(axios.post).toBeCalledTimes(1)
+ })
it('should handle bad response', () => {
- (showError as any).mockClear();
- console.error = jest.fn();
+ (showError as any).mockClear()
+ console.error = jest.fn()
wrapper.vm.$options?.methods?.handleResponse.call(wrapper.vm, {
error: true,
errorMessage: 'FAIL',
- });
+ })
- expect(showError).toBeCalledTimes(1);
- });
+ expect(showError).toBeCalledTimes(1)
+ })
it('should handle success response', () => {
wrapper.vm.$options?.methods?.handleResponse.call(wrapper.vm, {
status: 'ok',
});
- (global as any).t = jest.fn();
- jest.runAllTimers();
+ (global as any).t = jest.fn()
+ jest.runAllTimers()
- expect(showSuccess).toBeCalledTimes(1);
- });
+ expect(showSuccess).toBeCalledTimes(1)
+ })
afterAll(() => {
- jest.clearAllMocks();
- jest.useRealTimers();
- });
-});
+ jest.clearAllMocks()
+ jest.useRealTimers()
+ })
+})
diff --git a/tests/javascript/unit/components/Explore.spec.ts b/tests/javascript/unit/components/Explore.spec.ts
index fe547792e8..b2ab052d55 100644
--- a/tests/javascript/unit/components/Explore.spec.ts
+++ b/tests/javascript/unit/components/Explore.spec.ts
@@ -1,22 +1,32 @@
-import axios from '@nextcloud/axios';
-import { shallowMount } from '@vue/test-utils';
-import { store, localVue } from '../setupStore';
+import axios from '@nextcloud/axios'
+import { shallowMount, createLocalVue } from '@vue/test-utils'
-import * as router from '@nextcloud/router';
+import * as router from '@nextcloud/router'
-import Explore from 'Components/Explore.vue';
+import Explore from '../../../../src/components/Explore.vue'
-jest.mock('@nextcloud/axios');
+jest.mock('@nextcloud/axios')
describe('Explore.vue', () => {
- 'use strict';
+ 'use strict'
+ const localVue = createLocalVue()
it('should initialize without showing AddFeed Component', () => {
- (axios as any).get.mockResolvedValue({ data: {} });
- (router as any).generateUrl = jest.fn().mockReturnValue('');
+ (axios as any).get.mockResolvedValue({ data: { } });
+ (router as any).generateUrl = jest.fn().mockReturnValue('')
- const wrapper = shallowMount(Explore, { localVue, store });
+ const wrapper = shallowMount(Explore, {
+ localVue,
+ mocks: {
+ $store: {
+ state: {
+ feeds: [],
+ folders: [],
+ },
+ },
+ },
+ })
- expect(wrapper.vm.$data.showAddFeed).toBeFalsy;
- });
-});
+ expect(wrapper.vm.$data.showAddFeed).toBeFalsy()
+ })
+})
diff --git a/tests/javascript/unit/components/Sidebar.spec.ts b/tests/javascript/unit/components/Sidebar.spec.ts
index 63b490f0ee..1eee5486a2 100644
--- a/tests/javascript/unit/components/Sidebar.spec.ts
+++ b/tests/javascript/unit/components/Sidebar.spec.ts
@@ -1,46 +1,136 @@
-import { Wrapper, shallowMount } from '@vue/test-utils'
-import { store, localVue } from '../setupStore'
+import { ACTIONS } from '../../../../src/store'
+import { Wrapper, shallowMount, createLocalVue } from '@vue/test-utils'
-import AppSidebar from 'Components/Sidebar.vue'
+import AppSidebar from '../../../../src/components/Sidebar.vue'
describe('Sidebar.vue', () => {
'use strict'
- let wrapper: Wrapper;
+ let wrapper: Wrapper
beforeAll(() => {
- wrapper = shallowMount(AppSidebar, { localVue, store })
- wrapper.vm.$store.dispatch = jest.fn();
+ const localVue = createLocalVue()
+ wrapper = shallowMount(AppSidebar, {
+ localVue,
+ mocks: {
+ $store: {
+ state: {
+ feeds: [],
+ folders: [],
+ },
+ dispatch: jest.fn(),
+ },
+ },
+ })
})
it('should initialize without showing AddFeed Component', () => {
- expect(wrapper.vm.$data.showAddFeed).toBeFalsy
- });
+ expect((wrapper.vm as any).$data.showAddFeed).toBeFalsy()
+ })
- it('should dispatch message to store with folder name to create new folder', () => {
- (wrapper.vm as any).newFolder('abc')
-
- expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith('addFolder', { folder: { name: 'abc'} })
- });
+ describe('User Actions', () => {
+ it('should dispatch message to store with folder name to create new folder', () => {
+ (wrapper.vm as any).newFolder('abc')
- it('should dispatch message to store with folder object on delete folder', () => {
- const folder = {};
- (wrapper.vm as any).deleteFolder(folder)
+ expect((wrapper.vm as any).$store.dispatch).toHaveBeenCalledWith(ACTIONS.ADD_FOLDERS, { folder: { name: 'abc' } })
+ })
- expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith('deleteFolder', { folder })
- })
+ it('should dispatch message to store with folder object on delete folder', () => {
+ const folder = {};
+ (wrapper.vm as any).deleteFolder(folder)
+
+ expect((wrapper.vm as any).$store.dispatch).toHaveBeenCalledWith(ACTIONS.DELETE_FOLDER, { folder })
+ })
+
+ it('should set showAddFeed to true', () => {
+ (wrapper.vm as any).showShowAddFeed()
+ expect(wrapper.vm.$data.showAddFeed).toBeTruthy()
+ })
- it('should set showAddFeed to true', () => {
- (wrapper.vm as any).showShowAddFeed()
- expect(wrapper.vm.$data.showAddFeed).toBeTruthy
+ it('should set showAddFeed to false', () => {
+ (wrapper.vm as any).closeShowAddFeed()
+ expect(wrapper.vm.$data.showAddFeed).toBeFalsy()
+ })
})
- it('should set showAddFeed to false', () => {
- (wrapper.vm as any).closeShowAddFeed()
- expect(wrapper.vm.$data.showAddFeed).toBeFalsy
+ describe('SideBarState', () => {
+ it('should return no top level nav when no folders or feeds', () => {
+ const topLevelNav = (wrapper.vm.$options.computed?.topLevelNav as any).call({
+ $store: {
+ getters: {
+ feeds: [],
+ folders: [],
+ },
+ },
+ })
+
+ expect(topLevelNav).toEqual([])
+ })
+
+ it('should return top level nav with 1 feed', () => {
+ const feeds: any[] = [{ name: 'feed1', id: 1 }]
+ const folders: any[] = []
+ const topLevelNav = (wrapper.vm.$options.computed?.topLevelNav as any).call({
+ $store: {
+ getters: {
+ feeds,
+ folders,
+ },
+ },
+ })
+
+ expect(topLevelNav).toEqual([feeds[0]])
+ })
+
+ it('should return top level nav with 1 folder (with feeds)', () => {
+ const feeds: any[] = [{ name: 'feed2', id: 2, folderId: 123 }]
+ const folders: any[] = [{ name: 'abc', id: 123 }]
+ const topLevelNav = (wrapper.vm.$options.computed?.topLevelNav as any).call({
+ $store: {
+ getters: {
+ feeds,
+ folders,
+ },
+ },
+ })
+
+ expect(topLevelNav).toEqual(folders)
+ })
+
+ it('should return top level nav with 1 folder (without feed)', () => {
+ const feeds: any[] = [{ name: 'feed1', id: 1 }]
+ const folders: any[] = [{ name: 'abc', id: 123 }]
+ const topLevelNav = (wrapper.vm.$options.computed?.topLevelNav as any).call({
+ $store: {
+ getters: {
+ feeds,
+ folders,
+ },
+ },
+ })
+
+ expect(topLevelNav).toEqual([feeds[0], ...folders])
+ })
+
+ it('should return top level nav with feeds and folders', () => {
+ const feeds: any[] = [{ name: 'feed1', id: 1 }, { name: 'feed2', id: 2, folderId: 123 }]
+ const folders: any[] = [{ name: 'abc', id: 123 }, { name: 'xyz', id: 234 }]
+ const topLevelNav = (wrapper.vm.$options.computed?.topLevelNav as any).call({
+ $store: {
+ getters: {
+ feeds,
+ folders,
+ },
+ },
+ })
+
+ expect(topLevelNav).toEqual([feeds[0], ...folders])
+ })
})
+ // TODO: More Template Testing with https://test-utils.vuejs.org/guide/essentials/a-crash-course.html#adding-a-new-todo
+
afterEach(() => {
- jest.clearAllMocks();
- });
+ jest.clearAllMocks()
+ })
})
diff --git a/tests/javascript/unit/setup.ts b/tests/javascript/unit/setup.ts
index f3b7349536..510e53baad 100644
--- a/tests/javascript/unit/setup.ts
+++ b/tests/javascript/unit/setup.ts
@@ -10,11 +10,11 @@ config.mocks.$t = function(_app: any, string: any) {
}
config.mocks.t = config.mocks.$t
-config.mocks.$n = function(app: any, singular: any, plural: any, count: any) {
+config.mocks.$n = function(app: any, singular: any) {
return singular
}
config.mocks.n = config.mocks.$n
afterAll(() => {
-
+ // TODO: afterAll tests?
})
diff --git a/tests/javascript/unit/setupStore.ts b/tests/javascript/unit/setupStore.ts
deleted file mode 100644
index 074d591efa..0000000000
--- a/tests/javascript/unit/setupStore.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-// NOTE: This was copied from nextcloud/tasks repo
-import { createLocalVue } from '@vue/test-utils'
-import Vuex from 'vuex'
-
-const localVue = createLocalVue()
-localVue.use(Vuex)
-
-const store = new Vuex.Store({
- modules: {
- },
-})
-export { store, localVue }
diff --git a/tests/javascript/unit/store/feed.spec.ts b/tests/javascript/unit/store/feed.spec.ts
new file mode 100644
index 0000000000..29df4b1054
--- /dev/null
+++ b/tests/javascript/unit/store/feed.spec.ts
@@ -0,0 +1,55 @@
+import axios from '@nextcloud/axios'
+import { Feed } from '../../../../src/types/Feed'
+import { AppState } from '../../../../src/store'
+import { FEED_ACTION_TYPES, FEED_MUTATION_TYPES, mutations, actions } from '../../../../src/store/feed'
+
+jest.mock('@nextcloud/axios')
+
+describe('feed.ts', () => {
+ 'use strict'
+
+ describe('actions', () => {
+ it('ADD_FEED should call POST and commit feed to state', async () => {
+ (axios as any).post.mockResolvedValue()
+ const commit = jest.fn()
+ await actions[FEED_ACTION_TYPES.ADD_FEED]({ commit }, { feedReq: { url: '' } })
+ expect(axios.post).toBeCalled()
+ expect(commit).toBeCalled()
+ })
+
+ it('FETCH_FEEDS should call GET and commit returned feeds to state', async () => {
+ (axios as any).get.mockResolvedValue({ data: { feeds: [] } })
+ const commit = jest.fn()
+ await (actions[FEED_ACTION_TYPES.FETCH_FEEDS] as any)({ commit })
+ expect(axios.get).toBeCalled()
+ expect(commit).toBeCalled()
+ })
+ })
+
+ describe('mutations', () => {
+ it('SET_FEEDS should add feeds to state', () => {
+ const state = { feeds: [] as Feed[], folders: [] as any[] } as AppState
+ let feeds = [] as Feed[]
+
+ mutations[FEED_MUTATION_TYPES.SET_FEEDS](state, feeds)
+ expect(state.feeds.length).toEqual(0)
+
+ feeds = [{ title: 'test' }] as Feed[]
+
+ mutations[FEED_MUTATION_TYPES.SET_FEEDS](state, feeds)
+ expect(state.feeds.length).toEqual(1)
+ expect(state.feeds[0]).toEqual(feeds[0])
+ })
+
+ it('SET_FEEDS should add feeds and unreadCount to folder if exists and folder set', () => {
+ const state = { feeds: [] as Feed[], folders: [{ id: 1, feedCount: 3, feeds: [] as Feed[] }] } as AppState
+ const feeds = [{ title: 'test', folderId: 1, unreadCount: 2 }] as Feed[]
+
+ mutations[FEED_MUTATION_TYPES.SET_FEEDS](state, feeds)
+ expect(state.feeds.length).toEqual(1)
+ expect(state.feeds[0]).toEqual(feeds[0])
+ expect(state.folders[0].feeds[0]).toEqual(feeds[0])
+ expect(state.folders[0].feedCount).toEqual(5)
+ })
+ })
+})
diff --git a/tests/javascript/unit/store/folder.spec.ts b/tests/javascript/unit/store/folder.spec.ts
new file mode 100644
index 0000000000..68cc04da7a
--- /dev/null
+++ b/tests/javascript/unit/store/folder.spec.ts
@@ -0,0 +1,71 @@
+import axios from '@nextcloud/axios'
+import { generateUrl } from '@nextcloud/router'
+import { Folder } from '../../../../src/types/Folder'
+import { AppState } from '../../../../src/store'
+import { FOLDER_ACTION_TYPES, FOLDER_MUTATION_TYPES, mutations, actions } from '../../../../src/store/folder'
+
+jest.mock('@nextcloud/axios')
+jest.mock('@nextcloud/router')
+
+describe('folder.ts', () => {
+ 'use strict'
+
+ describe('actions', () => {
+ it('FETCH_FOLDERS should send GET and then commit folders returned to state', async () => {
+ (generateUrl as any).mockReturnValue('');
+ (axios.get as any).mockResolvedValue({ data: { folders: [] } })
+
+ const commit = jest.fn()
+
+ await (actions[FOLDER_ACTION_TYPES.FETCH_FOLDERS] as any)({ commit })
+ expect(axios.get).toBeCalled()
+ expect(commit).toBeCalled()
+ })
+
+ it('ADD_FOLDERS should send POST and then commit the folders returned to state', async () => {
+ (axios.post as any).mockResolvedValue({ data: { folders: [] } })
+
+ const folder = {} as Folder
+ const commit = jest.fn()
+
+ await actions[FOLDER_ACTION_TYPES.ADD_FOLDERS]({ commit }, { folder })
+ expect(axios.post).toBeCalled()
+ expect(commit).toBeCalled()
+ })
+
+ it('DELETE_FOLDER should send DELETE and then commit deleted folder to state', async () => {
+ (axios.delete as any).mockResolvedValue()
+
+ const folder = {} as Folder
+ const commit = jest.fn()
+
+ await actions[FOLDER_ACTION_TYPES.DELETE_FOLDER]({ commit }, { folder })
+ expect(axios.delete).toBeCalled()
+ expect(commit).toBeCalled()
+ })
+ })
+
+ describe('mutations', () => {
+ it('SET_FOLDERS should add the passed in folders to the state', () => {
+ const state = { folders: [] as Folder[] } as AppState
+ let folders = [] as Folder[]
+
+ (mutations[FOLDER_MUTATION_TYPES.SET_FOLDERS] as any)(state, folders)
+ expect(state.folders.length).toEqual(0)
+
+ folders = [{ name: 'test' }] as Folder[]
+
+ (mutations[FOLDER_MUTATION_TYPES.SET_FOLDERS] as any)(state, folders)
+ expect(state.folders.length).toEqual(1)
+ expect(state.folders[0]).toEqual(folders[0])
+ })
+
+ it('DELETE_FOLDER should remove the passed in folder from the state', () => {
+ const state = { folders: [{ name: 'test' }] as Folder[] } as AppState
+ const folders = [state.folders[0]] as Folder[]
+
+ (mutations[FOLDER_MUTATION_TYPES.DELETE_FOLDER] as any)(state, folders)
+ expect(state.folders.length).toEqual(0)
+ })
+ })
+})
diff --git a/webpack.js b/webpack.js
index 9b6a70a0bc..25c295596f 100644
--- a/webpack.js
+++ b/webpack.js
@@ -1,4 +1,5 @@
-const webpackConfig = require('@nextcloud/webpack-vue-config')
+const { merge } = require('webpack-merge')
+let webpackConfig = require('@nextcloud/webpack-vue-config')
const path = require('path')
webpackConfig.entry['admin-settings'] = path.join(
@@ -7,6 +8,12 @@ webpackConfig.entry['admin-settings'] = path.join(
'main-admin.js',
)
+webpackConfig = merge(webpackConfig, {
+ resolve: {
+ extensions: ['.ts'],
+ },
+})
+
// Add TS Loader for processing typescript in vue templates
webpackConfig.module.rules.push({
test: /.ts$/,