From e145602fe768c33be99288ccdb57be2a1bf9e714 Mon Sep 17 00:00:00 2001 From: Devlin Junker Date: Sun, 27 Nov 2022 21:42:21 -1000 Subject: [PATCH 01/16] moved store to separate file and started using constants for action/mutation names Signed-off-by: Devlin Junker --- src/components/AddFeed.vue | 2 +- src/components/Sidebar.vue | 77 +++++++++++------- src/main.js | 98 +--------------------- src/store/index.ts | 162 +++++++++++++++++++++++++++++++++++++ src/types/Feed.vue | 1 + src/types/Folder.vue | 3 + 6 files changed, 219 insertions(+), 124 deletions(-) create mode 100644 src/store/index.ts diff --git a/src/components/AddFeed.vue b/src/components/AddFeed.vue index 5d50b7b4e..cd5e60fe2 100644 --- a/src/components/AddFeed.vue +++ b/src/components/AddFeed.vue @@ -139,7 +139,7 @@ export default Vue.extend({ }, data: (): AddFeedState => { return { - folder: { name: '' }, + folder: { name: '' } as any, autoDiscover: true, createNewFolder: false, withBasicAuth: false, diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue index b9c0c8458..95ace9064 100644 --- a/src/components/Sidebar.vue +++ b/src/components/Sidebar.vue @@ -13,7 +13,7 @@ @@ -23,7 +23,7 @@ @@ -34,13 +34,13 @@ - - @@ -132,6 +130,7 @@ import Vuex from 'vuex' import Vue from 'vue' + import NcAppNavigation from '@nextcloud/vue/dist/Components/NcAppNavigation.js' import NcAppNavigationNew from '@nextcloud/vue/dist/Components/NcAppNavigationNew.js' import NcAppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js' @@ -139,21 +138,23 @@ import NcAppNavigationNewItem from '@nextcloud/vue/dist/Components/NcAppNavigati // import AppNavigationCounter from '@nextcloud/vue/dist/Components/AppNavigationCounter' import NcCounterBubble from '@nextcloud/vue/dist/Components/NcCounterBubble.js' import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js' -import AddFeed from './AddFeed.vue' -import { Folder } from '../types/Folder.vue' -import { Feed } from '../types/Feed.vue' // import { ROUTES } from '../routes.js' -import { ACTIONS, AppState } from '../store/index.ts' + +import AddFeed from './AddFeed.vue' + +import { ACTIONS, AppState } from '../store/index' +import { Folder } from '../types/Folder' +import { Feed } from '../types/Feed' const SideBarState: any = { - topLevelNav(state: AppState) { - const navItems = state.feeds.filter((feed: Feed) => { + topLevelNav(state: AppState): any[] { + const navItems: any[] = state.feeds.filter((feed: Feed) => { return feed.folderId === undefined || feed.folderId === null - }).concat(state.folders) + }) + navItems.concat(state.folders) - console.log(navItems); - return navItems; + return navItems }, } diff --git a/src/store/feed.ts b/src/store/feed.ts new file mode 100644 index 000000000..aaf9a96f0 --- /dev/null +++ b/src/store/feed.ts @@ -0,0 +1,74 @@ +import axios from "@nextcloud/axios"; +import { generateUrl } from "@nextcloud/router"; + +import { ActionParams, AppState } from 'src/store' +import { Feed } from '../types/Feed' + +export const FEED_MUTATION_TYPES = { + SET_FEEDS: 'SET_FEEDS', +} + +export const FEED_ACTION_TYPES = { + ADD_FEED: 'ADD_FEED', + FETCH_FEEDS: 'FETCH_FEEDS', +} + +const feedUrl = generateUrl("/apps/news/feeds") + +export const FEED_ACTIONS = { + async [FEED_ACTION_TYPES.FETCH_FEEDS] ({ commit }: ActionParams) { + const feeds = await axios.get( + generateUrl("/apps/news/feeds") + ); + + commit(FEED_MUTATION_TYPES.SET_FEEDS, feeds.data.feeds); + }, + [FEED_ACTION_TYPES.ADD_FEED] ({ commit }: ActionParams, { feedReq }: { feedReq: { url: string; folder?: { id: number } } }) { + console.log(feedReq) + let url = feedReq.url.trim(); + if (!url.startsWith('http')) { + url = 'https://' + url; + } + + /** + if (title !== undefined) { + title = title.trim(); + } + */ + + let feed: Feed = { + url: url, + folderId: feedReq.folder?.id || 0, + title: undefined, + unreadCount: 0, + autoDiscover: undefined // TODO + }; + + // this.add(feed); + // this.updateFolderCache(); + + axios.post(feedUrl, { + url: feed.url, + parentFolderId: feed.folderId, + title: null, + user: null, + password: null, + fullDiscover: feed.autoDiscover + }).then(() => { + commit('addFeed', feed) + }); + } +} + +export const FEED_MUTATIONS = { + [FEED_MUTATION_TYPES.SET_FEEDS] (state: AppState, feeds: Feed[]) { + feeds.forEach(it => { + state.feeds.push(it) + const folder = state.folders.find(folder => folder.id === it.folderId) + if (folder) { + folder.feeds.push(it) + folder.feedCount += it.unreadCount + } + }) + }, +} \ No newline at end of file diff --git a/src/store/folder.ts b/src/store/folder.ts new file mode 100644 index 000000000..735676ea0 --- /dev/null +++ b/src/store/folder.ts @@ -0,0 +1,70 @@ +import axios from "@nextcloud/axios"; +import { generateUrl } from "@nextcloud/router"; + +import { AppState, ActionParams } from 'src/store' +import { Folder } from '../types/Folder' + +export const FOLDER_MUTATION_TYPES = { + SET_FOLDERS: 'SET_FOLDERS', + DELETE_FOLDER: 'DELETE_FOLDER' +} + +export const FOLDER_ACTION_TYPES = { + FETCH_FOLDERS: 'FETCH_FOLDERS', + ADD_FOLDERS: 'ADD_FOLDER', + DELETE_FOLDER: 'DELETE_FOLDER' +} + +const folderUrl = generateUrl("/apps/news/folders") + +export const FOLDER_ACTIONS = { + + async [FOLDER_ACTION_TYPES.FETCH_FOLDERS] ({ commit }: ActionParams) { + const folders = await axios.get( + generateUrl("/apps/news/folders") + ); + + commit(FOLDER_MUTATION_TYPES.SET_FOLDERS, folders.data.folders); + }, + [FOLDER_ACTION_TYPES.ADD_FOLDERS] ({ commit }: ActionParams, { folder }: { folder: Folder}) { + console.log(folder) + axios.post(folderUrl, {folderName: folder.name}).then( + response => commit(FOLDER_MUTATION_TYPES.SET_FOLDERS, response.data.folders) + ); + }, + [FOLDER_ACTION_TYPES.DELETE_FOLDER] ({ commit }: ActionParams, { folder }: { folder: Folder}) { + /** + this.getByFolderId(folderId).forEach(function (feed) { + promises.push(self.reversiblyDelete(feed.id, false, true)); + }); + this.updateUnreadCache(); + */ + axios.delete(folderUrl + '/' + folder.id).then(() => commit(FOLDER_MUTATION_TYPES.DELETE_FOLDER, folder)) + }, + // loadFolder({commit}) { + // console.log('loading folders') + // axios.get(folderUrl).then( + // response => { + // commit('addFolders', response.data.folders); + // axios.get(feedUrl).then( + // response => commit('addFeeds', response.data.feeds) + // ) + // } + // ) + // }, +} + + +export const FOLDER_MUTATIONS = { + [FOLDER_MUTATION_TYPES.SET_FOLDERS] (state: AppState, folders: Folder[]) { + folders.forEach(it => { + it.feedCount = 0 + it.feeds = [] + state.folders.push(it) + }) + }, + [FOLDER_MUTATION_TYPES.DELETE_FOLDER] (state: AppState, folder: Folder) { + const index = state.folders.indexOf(folder); + state.folders.splice(index, 1); + } +} \ No newline at end of file diff --git a/src/store/index.ts b/src/store/index.ts index 3d1c5948c..177e23a94 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,35 +1,24 @@ -import axios from "@nextcloud/axios"; -import { generateUrl } from "@nextcloud/router"; import { Commit } from "vuex"; +import { Folder } from '../types/Folder' +import { Feed } from '../types/Feed' +import { FEED_MUTATION_TYPES, FEED_ACTION_TYPES, FEED_MUTATIONS, FEED_ACTIONS } from "./feed"; +import { FOLDER_MUTATION_TYPES, FOLDER_ACTION_TYPES, FOLDER_MUTATIONS, FOLDER_ACTIONS } from "./folder"; + export const MUTATIONS = { - SET_FEEDS: 'SET_FEEDS', - SET_FOLDERS: 'SET_FOLDERS' + ... FEED_MUTATION_TYPES, + ... FOLDER_MUTATION_TYPES } export const ACTIONS = { - FETCH_FEEDS: 'FETCH_FEEDS', - FETCH_FOLDERS: 'FETCH_FOLDERS' -} - -type Feed = { - folderId?: number; - unreadCount: number; - url: string; - title?: string; - autoDiscover?: boolean; - faviconLink?: string; + ... FEED_ACTION_TYPES, + ... FOLDER_ACTION_TYPES } -type Folder = { - feeds: any[]; - feedCount: number; - name: string; - id: number; -} +export type ActionParams = { commit: Commit }; export type AppState = { - feeds: any[]; + feeds: Feed[]; folders: Folder[]; items: any[]; } @@ -40,111 +29,6 @@ const state: AppState = { items: [] } as AppState -const mutations = { - [MUTATIONS.SET_FEEDS] (state: AppState, feeds: Feed[]) { - feeds.forEach(it => { - state.feeds.push(it) - const folder = state.folders.find(folder => folder.id === it.folderId) - if (folder) { - folder.feeds.push(it) - folder.feedCount += it.unreadCount - } - }) - }, - [MUTATIONS.SET_FOLDERS] (state: AppState, folders: Folder[]) { - folders.forEach(it => { - it.feedCount = 0 - it.feeds = [] - state.folders.push(it) - }) - }, -} - - -const feedUrl = generateUrl("/apps/news/feeds") -const folderUrl = generateUrl("/apps/news/folders") - - -type ActionParams = { commit: Commit }; - -const actions = { - async [ACTIONS.FETCH_FEEDS] ({ commit }: ActionParams) { - const feeds = await axios.get( - generateUrl("/apps/news/feeds") - ); - - commit(MUTATIONS.SET_FEEDS, feeds.data.feeds); - }, - async [ACTIONS.FETCH_FOLDERS] ({ commit }: ActionParams) { - const folders = await axios.get( - generateUrl("/apps/news/folders") - ); - - commit(MUTATIONS.SET_FOLDERS, folders.data.folders); - }, - addFolder({ commit }: ActionParams, { folder }: { folder: Folder}) { - console.log(folder) - axios.post(folderUrl, {folderName: folder.name}).then( - response => commit(MUTATIONS.SET_FOLDERS, response.data.folders) - ); - }, - deleteFolder({ commit }: ActionParams, { folder }: { folder: Folder}) { - /** - this.getByFolderId(folderId).forEach(function (feed) { - promises.push(self.reversiblyDelete(feed.id, false, true)); - }); - this.updateUnreadCache(); - */ - axios.delete(folderUrl + '/' + folder.id).then() - }, - // loadFolder({commit}) { - // console.log('loading folders') - // axios.get(folderUrl).then( - // response => { - // commit('addFolders', response.data.folders); - // axios.get(feedUrl).then( - // response => commit('addFeeds', response.data.feeds) - // ) - // } - // ) - // }, - addFeed({ commit }: ActionParams, { feedReq }: { feedReq: { url: string; folder?: { id: number } } }) { - console.log(feedReq) - let url = feedReq.url.trim(); - if (!url.startsWith('http')) { - url = 'https://' + url; - } - - /** - if (title !== undefined) { - title = title.trim(); - } - */ - - let feed: Feed = { - url: url, - folderId: feedReq.folder?.id || 0, - title: undefined, - unreadCount: 0, - autoDiscover: undefined // TODO - }; - - // this.add(feed); - // this.updateFolderCache(); - - axios.post(feedUrl, { - url: feed.url, - parentFolderId: feed.folderId, - title: null, - user: null, - password: null, - fullDiscover: feed.autoDiscover - }).then(() => { - commit('addFeed', feed) - }); - } -} - const getters = { feeds (state: AppState) { return state.feeds; @@ -154,6 +38,16 @@ const getters = { }, } +const mutations = { + ... FEED_MUTATIONS, + ... FOLDER_MUTATIONS +} + +const actions = { + ... FEED_ACTIONS, + ... FOLDER_ACTIONS +} + export default { state, mutations, diff --git a/src/types/Feed.ts b/src/types/Feed.ts new file mode 100644 index 000000000..d03ce5e0b --- /dev/null +++ b/src/types/Feed.ts @@ -0,0 +1,9 @@ +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 ece4a569e..000000000 --- a/src/types/Feed.vue +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/src/types/Folder.vue b/src/types/Folder.ts similarity index 76% rename from src/types/Folder.vue rename to src/types/Folder.ts index 254b4a20f..02b25cb8d 100644 --- a/src/types/Folder.vue +++ b/src/types/Folder.ts @@ -1,8 +1,8 @@ - + diff --git a/tsconfig.json b/tsconfig.json index 4548ebe3d..0bc846336 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ "moduleResolution": "node", "experimentalDecorators": true, "skipLibCheck": true, - "esModuleInterop": true, + "esModuleInterop": true, "allowSyntheticDefaultImports": true, "forceConsistentCasingInFileNames": true, "useDefineForClassFields": true, diff --git a/webpack.js b/webpack.js index 9b6a70a0b..25c295596 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$/, From 1d0069ee80ea65f598549b644022b8e656f82632 Mon Sep 17 00:00:00 2001 From: Devlin Junker Date: Mon, 28 Nov 2022 17:06:42 -1000 Subject: [PATCH 03/16] cleanup and renaming Signed-off-by: Devlin Junker --- src/components/AddFeed.vue | 4 ++-- src/components/Explore.vue | 8 +++++--- src/components/Sidebar.vue | 2 +- src/types/{ExploreSite.vue => ExploreSite.ts} | 2 -- 4 files changed, 8 insertions(+), 8 deletions(-) rename src/types/{ExploreSite.vue => ExploreSite.ts} (81%) diff --git a/src/components/AddFeed.vue b/src/components/AddFeed.vue index 2284f7d07..757a3a56a 100644 --- a/src/components/AddFeed.vue +++ b/src/components/AddFeed.vue @@ -107,13 +107,13 @@ From acbb538b4cccd84f1975561bd50f9276bdaa43fd Mon Sep 17 00:00:00 2001 From: Devlin Junker Date: Mon, 28 Nov 2022 17:25:23 -1000 Subject: [PATCH 04/16] clean Signed-off-by: Devlin Junker --- .eslintrc.js | 2 +- src/main.js | 2 +- tsconfig.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 53abb5d1f..38ada6387 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,7 +21,7 @@ module.exports = { ], ignorePatterns: ['*.d.ts'], rules: { - 'no-console': ['warn'], + 'no-console': 'warn', '@typescript-eslint/no-var-requires': 'off', // TODO: Trouble importing .ts files into .vue files for some reason? 'import/extensions': 'off', diff --git a/src/main.js b/src/main.js index 2289c04de..6a3ea3520 100644 --- a/src/main.js +++ b/src/main.js @@ -6,7 +6,7 @@ import Explore from './components/Explore.vue' import { generateUrl } from '@nextcloud/router' import Vuex, { Store } from 'vuex' -import mainStore from './store/index.ts' +import mainStore from './store/index' import { Tooltip } from '@nextcloud/vue' diff --git a/tsconfig.json b/tsconfig.json index 0bc846336..4548ebe3d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ "moduleResolution": "node", "experimentalDecorators": true, "skipLibCheck": true, - "esModuleInterop": true, + "esModuleInterop": true, "allowSyntheticDefaultImports": true, "forceConsistentCasingInFileNames": true, "useDefineForClassFields": true, From 8b21b054360168e4922be96e35abeed0f80e3ede Mon Sep 17 00:00:00 2001 From: Devlin Junker Date: Mon, 28 Nov 2022 23:52:31 -1000 Subject: [PATCH 05/16] modularize store files and fix unit tests Signed-off-by: Devlin Junker --- src/App.vue | 6 +- src/components/AddFeed.vue | 3 +- src/components/Sidebar.vue | 248 +++++++++--------- src/main.js | 2 +- src/store/feed.ts | 21 +- src/store/folder.ts | 23 +- src/store/index.ts | 40 +-- .../unit/components/AddFeed.spec.ts | 15 +- .../unit/components/Explore.spec.ts | 26 +- .../unit/components/Sidebar.spec.ts | 29 +- tests/javascript/unit/setupStore.ts | 12 - 11 files changed, 234 insertions(+), 191 deletions(-) delete mode 100644 tests/javascript/unit/setupStore.ts diff --git a/src/App.vue b/src/App.vue index ff6bf6c43..076fed19c 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 757a3a56a..462ab77d1 100644 --- a/src/components/AddFeed.vue +++ b/src/components/AddFeed.vue @@ -115,6 +115,7 @@ import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect.js' import { Folder } from '../types/Folder' import { Feed } from '../types/Feed' +import { ACTIONS, AppState } from '../store' type AddFeedState = { folder: Folder; @@ -160,7 +161,7 @@ export default Vue.extend({ this.createNewFolder = false }, addFeed() { - this.$store.dispatch('addFeed', { + this.$store.dispatch(ACTIONS.ADD_FEED, { feedReq: { url: this.feed, folder: this.folder, diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue index b2e40cd29..cd88369ee 100644 --- a/src/components/Sidebar.vue +++ b/src/components/Sidebar.vue @@ -5,124 +5,125 @@ button-id="new-feed-button" button-class="icon-add" @click="showShowAddFeed()" /> + @@ -140,7 +141,7 @@ import NcCounterBubble from '@nextcloud/vue/dist/Components/NcCounterBubble.js' import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js' // import { ROUTES } from '../routes.js' -import { ACTIONS, AppState } from '../store/index' +import { ACTIONS, AppState } from '../store' import AddFeed from './AddFeed.vue' @@ -148,11 +149,11 @@ import { Folder } from '../types/Folder' import { Feed } from '../types/Feed' const SideBarState: any = { - topLevelNav(state: AppState): any[] { - const navItems: any[] = state.feeds.filter((feed: Feed) => { + topLevelNav(localState: any, state: AppState): any[] { + let navItems: any[] = state.feeds.filter((feed: Feed) => { return feed.folderId === undefined || feed.folderId === null }) - navItems.concat(state.folders) + navItems = navItems.concat(state.folders) return navItems }, @@ -179,18 +180,17 @@ export default Vue.extend({ ...Vuex.mapState(['feeds', 'folders']), ...Vuex.mapState(SideBarState), }, - async created() { - await this.$store.dispatch(ACTIONS.FETCH_FOLDERS) - await this.$store.dispatch(ACTIONS.FETCH_FEEDS) + created() { + // TODO: ? }, methods: { newFolder(value: string) { const folderName = value.trim() const folder = { name: folderName } - this.$store.dispatch('addFolder', { folder }) + this.$store.dispatch(ACTIONS.ADD_FOLDERS, { folder }) }, deleteFolder(folder: Folder) { - this.$store.dispatch('deleteFolder', { folder }) + this.$store.dispatch(ACTIONS.DELETE_FOLDER, { folder }) // TODO: Reload }, showShowAddFeed() { diff --git a/src/main.js b/src/main.js index 6a3ea3520..6dac8fd5a 100644 --- a/src/main.js +++ b/src/main.js @@ -6,7 +6,7 @@ import Explore from './components/Explore.vue' import { generateUrl } from '@nextcloud/router' import Vuex, { Store } from 'vuex' -import mainStore from './store/index' +import mainStore from './store' import { Tooltip } from '@nextcloud/vue' diff --git a/src/store/feed.ts b/src/store/feed.ts index aaf9a96f0..4269879f9 100644 --- a/src/store/feed.ts +++ b/src/store/feed.ts @@ -13,9 +13,19 @@ export const FEED_ACTION_TYPES = { FETCH_FEEDS: 'FETCH_FEEDS', } +const state = { + feeds: [] +} + +const getters = { + feeds (state: AppState) { + return state.feeds; + }, +} + const feedUrl = generateUrl("/apps/news/feeds") -export const FEED_ACTIONS = { +export const actions = { async [FEED_ACTION_TYPES.FETCH_FEEDS] ({ commit }: ActionParams) { const feeds = await axios.get( generateUrl("/apps/news/feeds") @@ -60,7 +70,7 @@ export const FEED_ACTIONS = { } } -export const FEED_MUTATIONS = { +export const mutations = { [FEED_MUTATION_TYPES.SET_FEEDS] (state: AppState, feeds: Feed[]) { feeds.forEach(it => { state.feeds.push(it) @@ -71,4 +81,11 @@ export const FEED_MUTATIONS = { } }) }, +} + +export default { + state, + getters, + actions, + mutations } \ No newline at end of file diff --git a/src/store/folder.ts b/src/store/folder.ts index 735676ea0..c7ae93341 100644 --- a/src/store/folder.ts +++ b/src/store/folder.ts @@ -15,10 +15,19 @@ export const FOLDER_ACTION_TYPES = { DELETE_FOLDER: 'DELETE_FOLDER' } +const state = { + folders: [] +} + +const getters = { + folders (state: AppState) { + return state.folders; + }, +} + const folderUrl = generateUrl("/apps/news/folders") -export const FOLDER_ACTIONS = { - +export const actions = { async [FOLDER_ACTION_TYPES.FETCH_FOLDERS] ({ commit }: ActionParams) { const folders = await axios.get( generateUrl("/apps/news/folders") @@ -55,8 +64,9 @@ export const FOLDER_ACTIONS = { } -export const FOLDER_MUTATIONS = { +export const mutations = { [FOLDER_MUTATION_TYPES.SET_FOLDERS] (state: AppState, folders: Folder[]) { + console.log(folders); folders.forEach(it => { it.feedCount = 0 it.feeds = [] @@ -67,4 +77,11 @@ export const FOLDER_MUTATIONS = { const index = state.folders.indexOf(folder); state.folders.splice(index, 1); } +} + +export default { + state, + getters, + actions, + mutations } \ No newline at end of file diff --git a/src/store/index.ts b/src/store/index.ts index 177e23a94..5d599449b 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,9 +1,9 @@ -import { Commit } from "vuex"; +import { Commit, Store } from "vuex"; import { Folder } from '../types/Folder' import { Feed } from '../types/Feed' -import { FEED_MUTATION_TYPES, FEED_ACTION_TYPES, FEED_MUTATIONS, FEED_ACTIONS } from "./feed"; -import { FOLDER_MUTATION_TYPES, FOLDER_ACTION_TYPES, FOLDER_MUTATIONS, FOLDER_ACTIONS } from "./folder"; +import feeds, { FEED_MUTATION_TYPES, FEED_ACTION_TYPES } from "./feed"; +import folders, { FOLDER_MUTATION_TYPES, FOLDER_ACTION_TYPES } from "./folder"; export const MUTATIONS = { ... FEED_MUTATION_TYPES, @@ -23,34 +23,10 @@ export type AppState = { items: any[]; } -const state: AppState = { - feeds: [], - folders: [], - items: [] -} as AppState - -const getters = { - feeds (state: AppState) { - return state.feeds; - }, - folders (state: AppState) { - return state.folders; - }, -} - -const mutations = { - ... FEED_MUTATIONS, - ... FOLDER_MUTATIONS -} - -const actions = { - ... FEED_ACTIONS, - ... FOLDER_ACTIONS -} export default { - state, - mutations, - actions, - getters -} \ No newline at end of file + modules: { + feeds, + folders + } +}; \ No newline at end of file diff --git a/tests/javascript/unit/components/AddFeed.spec.ts b/tests/javascript/unit/components/AddFeed.spec.ts index a549ffb30..a426f2785 100644 --- a/tests/javascript/unit/components/AddFeed.spec.ts +++ b/tests/javascript/unit/components/AddFeed.spec.ts @@ -1,5 +1,4 @@ -import { shallowMount } from '@vue/test-utils' -import { store, localVue } from '../setupStore' +import { shallowMount, createLocalVue } from '@vue/test-utils' import AddFeed from 'Components/AddFeed.vue' @@ -7,7 +6,17 @@ 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 }); diff --git a/tests/javascript/unit/components/Explore.spec.ts b/tests/javascript/unit/components/Explore.spec.ts index fe547792e..3e24f2890 100644 --- a/tests/javascript/unit/components/Explore.spec.ts +++ b/tests/javascript/unit/components/Explore.spec.ts @@ -1,6 +1,5 @@ -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'; @@ -9,11 +8,26 @@ import Explore from 'Components/Explore.vue'; 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, + mocks: { + $store: { + state: { + feeds: [], + folders: [] + } + } + } + }) const wrapper = shallowMount(Explore, { localVue, store }); diff --git a/tests/javascript/unit/components/Sidebar.spec.ts b/tests/javascript/unit/components/Sidebar.spec.ts index 63b490f0e..5690aac08 100644 --- a/tests/javascript/unit/components/Sidebar.spec.ts +++ b/tests/javascript/unit/components/Sidebar.spec.ts @@ -1,5 +1,5 @@ -import { Wrapper, shallowMount } from '@vue/test-utils' -import { store, localVue } from '../setupStore' +import { ACTIONS } from '@/store'; +import { Wrapper, shallowMount, createLocalVue } from '@vue/test-utils' import AppSidebar from 'Components/Sidebar.vue' @@ -9,7 +9,18 @@ describe('Sidebar.vue', () => { let wrapper: Wrapper; beforeAll(() => { - wrapper = shallowMount(AppSidebar, { localVue, store }) + const localVue = createLocalVue() + wrapper = shallowMount(AppSidebar, { + localVue, + mocks: { + $store: { + state: { + feeds: [], + folders: [] + } + } + } + }) wrapper.vm.$store.dispatch = jest.fn(); }) @@ -20,14 +31,14 @@ describe('Sidebar.vue', () => { 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'} }) + expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith(ACTIONS.ADD_FOLDERS, { folder: { name: 'abc'} }) }); it('should dispatch message to store with folder object on delete folder', () => { const folder = {}; (wrapper.vm as any).deleteFolder(folder) - expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith('deleteFolder', { folder }) + expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith(ACTIONS.DELETE_FOLDER, { folder }) }) it('should set showAddFeed to true', () => { @@ -43,4 +54,12 @@ describe('Sidebar.vue', () => { afterEach(() => { jest.clearAllMocks(); }); + + describe('SideBar State', () => { + // it('should return top level nav (folders and feeds without folders)', () => { + // const navItems = (wrapper.vm.$options?.computed?.topLevelNav as any)({ feeds: [], folders: [] }); + + // console.log(navItems) + // }) + }) }) diff --git a/tests/javascript/unit/setupStore.ts b/tests/javascript/unit/setupStore.ts deleted file mode 100644 index 074d591ef..000000000 --- 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 } From 68340f6302a094d64f9efca3d19c11160e38a0c2 Mon Sep 17 00:00:00 2001 From: Devlin Junker Date: Tue, 29 Nov 2022 00:53:36 -1000 Subject: [PATCH 06/16] working unit test for local computed state Signed-off-by: Devlin Junker --- .../unit/components/Sidebar.spec.ts | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/tests/javascript/unit/components/Sidebar.spec.ts b/tests/javascript/unit/components/Sidebar.spec.ts index 5690aac08..c85bcd960 100644 --- a/tests/javascript/unit/components/Sidebar.spec.ts +++ b/tests/javascript/unit/components/Sidebar.spec.ts @@ -17,11 +17,12 @@ describe('Sidebar.vue', () => { state: { feeds: [], folders: [] - } + }, + dispatch: jest.fn() } } }) - wrapper.vm.$store.dispatch = jest.fn(); + // wrapper.vm.$store. }) it('should initialize without showing AddFeed Component', () => { @@ -51,15 +52,25 @@ describe('Sidebar.vue', () => { expect(wrapper.vm.$data.showAddFeed).toBeFalsy }) + // TODO: A couple more tests here + it('should return top level nav (folders and feeds without folders)', () => { + let topLevelNav = (wrapper.vm.$options.computed?.topLevelNav as any).call({ $store: { + getters: { + feeds: [], + folders: [] + } + }}) + + expect(topLevelNav).toEqual([]) + }) + + // 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() }); describe('SideBar State', () => { - // it('should return top level nav (folders and feeds without folders)', () => { - // const navItems = (wrapper.vm.$options?.computed?.topLevelNav as any)({ feeds: [], folders: [] }); - - // console.log(navItems) - // }) + }) }) From 496a2d7c3bc2b56d182747bc06f2c10cd653561b Mon Sep 17 00:00:00 2001 From: Devlin Junker Date: Wed, 30 Nov 2022 21:20:03 -1000 Subject: [PATCH 07/16] lint the .ts and spec.ts files also Signed-off-by: Devlin Junker --- .eslintrc.js | 1 + package.json | 4 +- src/store/feed.ts | 120 +++++++++--------- src/store/folder.ts | 117 +++++++++-------- src/store/index.ts | 27 ++-- src/types/Feed.ts | 1 - src/types/Folder.ts | 1 - .../unit/components/AddFeed.spec.ts | 20 +-- .../unit/components/Explore.spec.ts | 37 +++--- .../unit/components/Sidebar.spec.ts | 58 +++++---- 10 files changed, 190 insertions(+), 196 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 38ada6387..6f719bc0e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -23,6 +23,7 @@ module.exports = { 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', diff --git a/package.json b/package.json index 282494d84..85b941019 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", diff --git a/src/store/feed.ts b/src/store/feed.ts index 4269879f9..a74b7c9e4 100644 --- a/src/store/feed.ts +++ b/src/store/feed.ts @@ -1,91 +1,91 @@ -import axios from "@nextcloud/axios"; -import { generateUrl } from "@nextcloud/router"; +import axios from '@nextcloud/axios' +import { generateUrl } from '@nextcloud/router' -import { ActionParams, AppState } from 'src/store' +import { ActionParams, AppState } from '../store' import { Feed } from '../types/Feed' export const FEED_MUTATION_TYPES = { - SET_FEEDS: 'SET_FEEDS', + SET_FEEDS: 'SET_FEEDS', } export const FEED_ACTION_TYPES = { - ADD_FEED: 'ADD_FEED', - FETCH_FEEDS: 'FETCH_FEEDS', + ADD_FEED: 'ADD_FEED', + FETCH_FEEDS: 'FETCH_FEEDS', } const state = { - feeds: [] + feeds: [], } const getters = { - feeds (state: AppState) { - return state.feeds; - }, + feeds(state: AppState) { + return state.feeds + }, } -const feedUrl = generateUrl("/apps/news/feeds") +const feedUrl = generateUrl('/apps/news/feeds') export const actions = { - async [FEED_ACTION_TYPES.FETCH_FEEDS] ({ commit }: ActionParams) { - const feeds = await axios.get( - generateUrl("/apps/news/feeds") - ); + async [FEED_ACTION_TYPES.FETCH_FEEDS]({ commit }: ActionParams) { + const feeds = await axios.get( + generateUrl('/apps/news/feeds'), + ) - commit(FEED_MUTATION_TYPES.SET_FEEDS, feeds.data.feeds); - }, - [FEED_ACTION_TYPES.ADD_FEED] ({ commit }: ActionParams, { feedReq }: { feedReq: { url: string; folder?: { id: number } } }) { - console.log(feedReq) - let url = feedReq.url.trim(); - if (!url.startsWith('http')) { - url = 'https://' + url; - } + commit(FEED_MUTATION_TYPES.SET_FEEDS, feeds.data.feeds) + }, + [FEED_ACTION_TYPES.ADD_FEED]({ commit }: ActionParams, { feedReq }: { feedReq: { url: string; folder?: { id: number } } }) { + console.log(feedReq) + let url = feedReq.url.trim() + if (!url.startsWith('http')) { + url = 'https://' + url + } - /** + /** if (title !== undefined) { title = title.trim(); } - */ + */ - let feed: Feed = { - url: url, - folderId: feedReq.folder?.id || 0, - title: undefined, - unreadCount: 0, - autoDiscover: undefined // TODO - }; + const feed: Feed = { + url, + folderId: feedReq.folder?.id || 0, + title: undefined, + unreadCount: 0, + autoDiscover: undefined, // TODO + } - // this.add(feed); - // this.updateFolderCache(); + // this.add(feed); + // this.updateFolderCache(); - axios.post(feedUrl, { - url: feed.url, - parentFolderId: feed.folderId, - title: null, - user: null, - password: null, - fullDiscover: feed.autoDiscover - }).then(() => { - commit('addFeed', feed) - }); - } + axios.post(feedUrl, { + url: feed.url, + parentFolderId: feed.folderId, + title: null, + user: null, + password: null, + fullDiscover: feed.autoDiscover, + }).then(() => { + commit('addFeed', feed) + }) + }, } export const mutations = { - [FEED_MUTATION_TYPES.SET_FEEDS] (state: AppState, feeds: Feed[]) { - feeds.forEach(it => { - state.feeds.push(it) - const folder = state.folders.find(folder => folder.id === it.folderId) - if (folder) { - folder.feeds.push(it) - folder.feedCount += it.unreadCount - } - }) - }, + [FEED_MUTATION_TYPES.SET_FEEDS](state: AppState, feeds: Feed[]) { + feeds.forEach(it => { + state.feeds.push(it) + const folder = state.folders.find(folder => folder.id === it.folderId) + if (folder) { + folder.feeds.push(it) + folder.feedCount += it.unreadCount + } + }) + }, } export default { - state, - getters, - actions, - mutations -} \ No newline at end of file + state, + getters, + actions, + mutations, +} diff --git a/src/store/folder.ts b/src/store/folder.ts index c7ae93341..3a46e755b 100644 --- a/src/store/folder.ts +++ b/src/store/folder.ts @@ -1,87 +1,86 @@ -import axios from "@nextcloud/axios"; -import { generateUrl } from "@nextcloud/router"; +import axios from '@nextcloud/axios' +import { generateUrl } from '@nextcloud/router' -import { AppState, ActionParams } from 'src/store' +import { AppState, ActionParams } from '../store' import { Folder } from '../types/Folder' export const FOLDER_MUTATION_TYPES = { - SET_FOLDERS: 'SET_FOLDERS', - DELETE_FOLDER: 'DELETE_FOLDER' + SET_FOLDERS: 'SET_FOLDERS', + DELETE_FOLDER: 'DELETE_FOLDER', } export const FOLDER_ACTION_TYPES = { - FETCH_FOLDERS: 'FETCH_FOLDERS', - ADD_FOLDERS: 'ADD_FOLDER', - DELETE_FOLDER: 'DELETE_FOLDER' + FETCH_FOLDERS: 'FETCH_FOLDERS', + ADD_FOLDERS: 'ADD_FOLDER', + DELETE_FOLDER: 'DELETE_FOLDER', } const state = { - folders: [] + folders: [], } const getters = { - folders (state: AppState) { - return state.folders; - }, + folders(state: AppState) { + return state.folders + }, } -const folderUrl = generateUrl("/apps/news/folders") +const folderUrl = generateUrl('/apps/news/folders') export const actions = { - async [FOLDER_ACTION_TYPES.FETCH_FOLDERS] ({ commit }: ActionParams) { - const folders = await axios.get( - generateUrl("/apps/news/folders") - ); + async [FOLDER_ACTION_TYPES.FETCH_FOLDERS]({ commit }: ActionParams) { + const folders = await axios.get( + generateUrl('/apps/news/folders'), + ) - commit(FOLDER_MUTATION_TYPES.SET_FOLDERS, folders.data.folders); - }, - [FOLDER_ACTION_TYPES.ADD_FOLDERS] ({ commit }: ActionParams, { folder }: { folder: Folder}) { - console.log(folder) - axios.post(folderUrl, {folderName: folder.name}).then( - response => commit(FOLDER_MUTATION_TYPES.SET_FOLDERS, response.data.folders) - ); - }, - [FOLDER_ACTION_TYPES.DELETE_FOLDER] ({ commit }: ActionParams, { folder }: { folder: Folder}) { - /** + commit(FOLDER_MUTATION_TYPES.SET_FOLDERS, folders.data.folders) + }, + [FOLDER_ACTION_TYPES.ADD_FOLDERS]({ commit }: ActionParams, { folder }: { folder: Folder}) { + console.log(folder) + axios.post(folderUrl, { folderName: folder.name }).then( + response => commit(FOLDER_MUTATION_TYPES.SET_FOLDERS, response.data.folders), + ) + }, + [FOLDER_ACTION_TYPES.DELETE_FOLDER]({ commit }: ActionParams, { folder }: { folder: Folder}) { + /** this.getByFolderId(folderId).forEach(function (feed) { promises.push(self.reversiblyDelete(feed.id, false, true)); }); this.updateUnreadCache(); - */ - axios.delete(folderUrl + '/' + folder.id).then(() => commit(FOLDER_MUTATION_TYPES.DELETE_FOLDER, folder)) - }, - // loadFolder({commit}) { - // console.log('loading folders') - // axios.get(folderUrl).then( - // response => { - // commit('addFolders', response.data.folders); - // axios.get(feedUrl).then( - // response => commit('addFeeds', response.data.feeds) - // ) - // } - // ) - // }, + */ + axios.delete(folderUrl + '/' + folder.id).then(() => commit(FOLDER_MUTATION_TYPES.DELETE_FOLDER, folder)) + }, + // loadFolder({commit}) { + // console.log('loading folders') + // axios.get(folderUrl).then( + // response => { + // commit('addFolders', response.data.folders); + // axios.get(feedUrl).then( + // response => commit('addFeeds', response.data.feeds) + // ) + // } + // ) + // }, } - export const mutations = { - [FOLDER_MUTATION_TYPES.SET_FOLDERS] (state: AppState, folders: Folder[]) { - console.log(folders); - folders.forEach(it => { - it.feedCount = 0 - it.feeds = [] - state.folders.push(it) - }) - }, - [FOLDER_MUTATION_TYPES.DELETE_FOLDER] (state: AppState, folder: Folder) { - const index = state.folders.indexOf(folder); - state.folders.splice(index, 1); - } + [FOLDER_MUTATION_TYPES.SET_FOLDERS](state: AppState, folders: Folder[]) { + console.log(folders) + folders.forEach(it => { + it.feedCount = 0 + it.feeds = [] + state.folders.push(it) + }) + }, + [FOLDER_MUTATION_TYPES.DELETE_FOLDER](state: AppState, folder: Folder) { + const index = state.folders.indexOf(folder) + state.folders.splice(index, 1) + }, } export default { - state, - getters, - actions, - mutations -} \ No newline at end of file + state, + getters, + actions, + mutations, +} diff --git a/src/store/index.ts b/src/store/index.ts index 5d599449b..277bbfc2a 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,21 +1,19 @@ -import { Commit, Store } from "vuex"; - import { Folder } from '../types/Folder' import { Feed } from '../types/Feed' -import feeds, { FEED_MUTATION_TYPES, FEED_ACTION_TYPES } from "./feed"; -import folders, { FOLDER_MUTATION_TYPES, FOLDER_ACTION_TYPES } from "./folder"; +import feeds, { FEED_MUTATION_TYPES, FEED_ACTION_TYPES } from './feed' +import folders, { FOLDER_MUTATION_TYPES, FOLDER_ACTION_TYPES } from './folder' export const MUTATIONS = { - ... FEED_MUTATION_TYPES, - ... FOLDER_MUTATION_TYPES + ...FEED_MUTATION_TYPES, + ...FOLDER_MUTATION_TYPES, } export const ACTIONS = { - ... FEED_ACTION_TYPES, - ... FOLDER_ACTION_TYPES + ...FEED_ACTION_TYPES, + ...FOLDER_ACTION_TYPES, } -export type ActionParams = { commit: Commit }; +export type ActionParams = { commit: any }; export type AppState = { feeds: Feed[]; @@ -23,10 +21,9 @@ export type AppState = { items: any[]; } - export default { - modules: { - feeds, - folders - } -}; \ No newline at end of file + modules: { + feeds, + folders, + }, +} diff --git a/src/types/Feed.ts b/src/types/Feed.ts index d03ce5e0b..9e06aca3c 100644 --- a/src/types/Feed.ts +++ b/src/types/Feed.ts @@ -6,4 +6,3 @@ export type Feed = { autoDiscover?: boolean; faviconLink?: string; } - diff --git a/src/types/Folder.ts b/src/types/Folder.ts index 02b25cb8d..f09a404d4 100644 --- a/src/types/Folder.ts +++ b/src/types/Folder.ts @@ -5,4 +5,3 @@ export type Folder = { name: string; id: number; } - diff --git a/tests/javascript/unit/components/AddFeed.spec.ts b/tests/javascript/unit/components/AddFeed.spec.ts index a426f2785..66105e3f8 100644 --- a/tests/javascript/unit/components/AddFeed.spec.ts +++ b/tests/javascript/unit/components/AddFeed.spec.ts @@ -1,6 +1,6 @@ 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' @@ -9,15 +9,15 @@ describe('AddFeed.vue', () => { const localVue = createLocalVue() const wrapper = shallowMount(AddFeed, { localVue, - mocks: { - $store: { + mocks: { + $store: { state: { - folders: [] - } - } - } + 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/Explore.spec.ts b/tests/javascript/unit/components/Explore.spec.ts index 3e24f2890..f0a945821 100644 --- a/tests/javascript/unit/components/Explore.spec.ts +++ b/tests/javascript/unit/components/Explore.spec.ts @@ -3,7 +3,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils' import * as router from '@nextcloud/router'; -import Explore from 'Components/Explore.vue'; +import Explore from '../../../../src/components/Explore.vue' jest.mock('@nextcloud/axios'); @@ -11,26 +11,23 @@ describe('Explore.vue', () => { '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(''); - - const wrapper = shallowMount(Explore, { + (axios as any).get.mockResolvedValue({ data: { } }); + (router as any).generateUrl = jest.fn().mockReturnValue('') + + const wrapper = shallowMount(Explore, { localVue, - mocks: { - $store: { - state: { - feeds: [], - folders: [] - } - } - } + mocks: { + $store: { + state: { + feeds: [], + folders: [], + }, + }, + }, }) - const wrapper = shallowMount(Explore, { localVue, store }); - - 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 c85bcd960..42f826eb8 100644 --- a/tests/javascript/unit/components/Sidebar.spec.ts +++ b/tests/javascript/unit/components/Sidebar.spec.ts @@ -1,65 +1,67 @@ -import { ACTIONS } from '@/store'; +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(() => { const localVue = createLocalVue() - wrapper = shallowMount(AppSidebar, { + wrapper = shallowMount(AppSidebar, { localVue, - mocks: { - $store: { - state: { - feeds: [], - folders: [] + mocks: { + $store: { + state: { + feeds: [], + folders: [], }, - dispatch: jest.fn() - } - } + dispatch: jest.fn(), + }, + }, }) // wrapper.vm.$store. }) 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(ACTIONS.ADD_FOLDERS, { folder: { name: 'abc'} }) - }); + + expect((wrapper.vm as any).$store.dispatch).toHaveBeenCalledWith(ACTIONS.ADD_FOLDERS, { folder: { name: 'abc' } }) + }) it('should dispatch message to store with folder object on delete folder', () => { const folder = {}; (wrapper.vm as any).deleteFolder(folder) - expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith(ACTIONS.DELETE_FOLDER, { 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 + expect(wrapper.vm.$data.showAddFeed).toBeTruthy() }) it('should set showAddFeed to false', () => { (wrapper.vm as any).closeShowAddFeed() - expect(wrapper.vm.$data.showAddFeed).toBeFalsy + expect(wrapper.vm.$data.showAddFeed).toBeFalsy() }) // TODO: A couple more tests here it('should return top level nav (folders and feeds without folders)', () => { - let topLevelNav = (wrapper.vm.$options.computed?.topLevelNav as any).call({ $store: { - getters: { - feeds: [], - folders: [] - } - }}) + const topLevelNav = (wrapper.vm.$options.computed?.topLevelNav as any).call({ + $store: { + getters: { + feeds: [], + folders: [], + }, + }, + }) expect(topLevelNav).toEqual([]) }) @@ -68,9 +70,9 @@ describe('Sidebar.vue', () => { afterEach(() => { jest.clearAllMocks() - }); + }) describe('SideBar State', () => { - + // TODO }) }) From c208c01b8576123652d4635e2c6dbc7bed6cd291 Mon Sep 17 00:00:00 2001 From: Devlin Junker Date: Sat, 3 Dec 2022 14:34:26 -0800 Subject: [PATCH 08/16] add more tests Signed-off-by: Devlin Junker --- .../unit/components/Sidebar.spec.ts | 117 ++++++++++++++---- tests/javascript/unit/setup.ts | 2 +- 2 files changed, 91 insertions(+), 28 deletions(-) diff --git a/tests/javascript/unit/components/Sidebar.spec.ts b/tests/javascript/unit/components/Sidebar.spec.ts index 42f826eb8..16affbb3d 100644 --- a/tests/javascript/unit/components/Sidebar.spec.ts +++ b/tests/javascript/unit/components/Sidebar.spec.ts @@ -29,41 +29,104 @@ describe('Sidebar.vue', () => { 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') + describe('User Actions', () => { + it('should dispatch message to store with folder name to create new folder', () => { + (wrapper.vm as any).newFolder('abc') - expect((wrapper.vm as any).$store.dispatch).toHaveBeenCalledWith(ACTIONS.ADD_FOLDERS, { folder: { name: 'abc' } }) - }) + expect((wrapper.vm as any).$store.dispatch).toHaveBeenCalledWith(ACTIONS.ADD_FOLDERS, { folder: { name: 'abc' } }) + }) - it('should dispatch message to store with folder object on delete folder', () => { - const folder = {}; - (wrapper.vm as any).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 }) - }) + 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() + }) }) - // TODO: A couple more tests here - it('should return top level nav (folders and feeds without folders)', () => { - const topLevelNav = (wrapper.vm.$options.computed?.topLevelNav as any).call({ - $store: { - getters: { - feeds: [], - folders: [], + 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]]) }) - expect(topLevelNav).toEqual([]) + 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 @@ -72,7 +135,7 @@ describe('Sidebar.vue', () => { jest.clearAllMocks() }) - describe('SideBar State', () => { - // TODO + afterEach(() => { + jest.clearAllMocks() }) }) diff --git a/tests/javascript/unit/setup.ts b/tests/javascript/unit/setup.ts index f3b734953..380436f8c 100644 --- a/tests/javascript/unit/setup.ts +++ b/tests/javascript/unit/setup.ts @@ -16,5 +16,5 @@ config.mocks.$n = function(app: any, singular: any, plural: any, count: any) { config.mocks.n = config.mocks.$n afterAll(() => { - + // TODO: ? }) From 884664c273d31cefc69a342a56370355134da8d0 Mon Sep 17 00:00:00 2001 From: Devlin Junker Date: Sat, 3 Dec 2022 15:40:06 -0800 Subject: [PATCH 09/16] start on state/store tests Signed-off-by: Devlin Junker --- .eslintrc.js | 8 ++++ package-lock.json | 14 +++--- package.json | 4 +- src/components/AddFeed.vue | 4 +- src/components/Explore.vue | 2 +- src/components/Sidebar.vue | 16 +++---- src/store/feed.ts | 19 ++++---- src/store/folder.ts | 19 ++++---- .../unit/components/Explore.spec.ts | 1 - .../unit/components/Sidebar.spec.ts | 4 -- .../unit/{setup.ts => setup.spec.ts} | 4 +- tests/javascript/unit/store/feed.spec.ts | 28 ++++++++++++ tests/javascript/unit/store/folder.spec.ts | 44 +++++++++++++++++++ 13 files changed, 119 insertions(+), 48 deletions(-) rename tests/javascript/unit/{setup.ts => setup.spec.ts} (80%) create mode 100644 tests/javascript/unit/store/feed.spec.ts create mode 100644 tests/javascript/unit/store/folder.spec.ts diff --git a/.eslintrc.js b/.eslintrc.js index 6f719bc0e..33eef5695 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -35,4 +35,12 @@ module.exports = { }, }, }, + overrides: [ + { + files: ['*spec.ts'], + rules: { + '@typescript-eslint/no-explicit-any': 'off', + }, + }, + ], } diff --git a/package-lock.json b/package-lock.json index 44f4c681c..9279f9e1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,7 +82,7 @@ "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", @@ -15661,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", @@ -27998,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", diff --git a/package.json b/package.json index 85b941019..b4aa4643c 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "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", @@ -151,7 +151,7 @@ "jest-serializer-vue" ], "setupFilesAfterEnv": [ - "./tests/javascript/unit/setup.ts" + "./tests/javascript/unit/setup.spec.ts" ], "coverageDirectory": "./coverage/", "collectCoverage": false, diff --git a/src/components/AddFeed.vue b/src/components/AddFeed.vue index 462ab77d1..af5d75937 100644 --- a/src/components/AddFeed.vue +++ b/src/components/AddFeed.vue @@ -115,7 +115,7 @@ import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect.js' import { Folder } from '../types/Folder' import { Feed } from '../types/Feed' -import { ACTIONS, AppState } from '../store' +import { ACTIONS } from '../store' type AddFeedState = { folder: Folder; @@ -142,7 +142,7 @@ export default Vue.extend({ }, data: (): AddFeedState => { return { - folder: { name: '' } as any, + folder: { name: '' } as Folder, autoDiscover: true, createNewFolder: false, withBasicAuth: false, diff --git a/src/components/Explore.vue b/src/components/Explore.vue index e909101f7..067dd61b3 100644 --- a/src/components/Explore.vue +++ b/src/components/Explore.vue @@ -50,7 +50,7 @@ const ExploreComponent = Vue.extend({ }, data: () => { const exploreSites: ExploreSite[] = [] - const feed: Feed = {} as any + const feed: Feed = {} as Feed const showAddFeed = false return { diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue index cd88369ee..c51ea16ac 100644 --- a/src/components/Sidebar.vue +++ b/src/components/Sidebar.vue @@ -129,7 +129,7 @@