From efb1ac236e0e5f2038886fccf4d01337b486732a Mon Sep 17 00:00:00 2001 From: Devlin Junker Date: Wed, 16 Aug 2023 15:28:17 -0700 Subject: basic unit tests Signed-off-by: Devlin Junker --- tests/javascript/unit/components/AddFeed.spec.ts | 18 +-- tests/javascript/unit/components/FeedItem.spec.ts | 141 ++++++++++++++++++++++ tests/javascript/unit/components/Starred.spec.ts | 75 ++++++++++++ tests/javascript/unit/setup.ts | 1 + tests/javascript/unit/store/item.spec.ts | 96 +++++++++++++++ 5 files changed, 322 insertions(+), 9 deletions(-) create mode 100644 tests/javascript/unit/components/FeedItem.spec.ts create mode 100644 tests/javascript/unit/components/Starred.spec.ts create mode 100644 tests/javascript/unit/store/item.spec.ts (limited to 'tests') diff --git a/tests/javascript/unit/components/AddFeed.spec.ts b/tests/javascript/unit/components/AddFeed.spec.ts index ce1a9581d..daf50329e 100644 --- a/tests/javascript/unit/components/AddFeed.spec.ts +++ b/tests/javascript/unit/components/AddFeed.spec.ts @@ -1,17 +1,17 @@ import { shallowMount, createLocalVue } from '@vue/test-utils' import AddFeed from '../../../../src/components/AddFeed.vue' -import { FEED_ACTION_TYPES } from '@/store/feed' +import { FEED_ACTION_TYPES } from '../../../../src/store/feed' describe('AddFeed.vue', () => { 'use strict' - let mockDispatch = jest.fn(); - let mockStore = { + const mockDispatch = jest.fn() + const mockStore = { state: { folders: { folders: [] }, - feeds: { feeds: [] } + feeds: { feeds: [] }, }, - dispatch: mockDispatch + dispatch: mockDispatch, } let wrapper: any @@ -38,7 +38,7 @@ describe('AddFeed.vue', () => { expect(wrapper.vm.$emit).toBeCalled() expect(mockDispatch).toBeCalled() - expect(mockDispatch.mock.calls[0][0]).toEqual(FEED_ACTION_TYPES.ADD_FEED); + expect(mockDispatch.mock.calls[0][0]).toEqual(FEED_ACTION_TYPES.ADD_FEED) }) it('should check if feed url exists and return true', () => { @@ -53,7 +53,7 @@ describe('AddFeed.vue', () => { expect(response).toBeFalsy() wrapper.vm.$data.feedUrl = 'http://test.com' - wrapper.vm.$store.state.feeds.feeds = [{ url: 'http://test.com'}] + wrapper.vm.$store.state.feeds.feeds = [{ url: 'http://test.com' }] response = wrapper.vm.feedUrlExists() expect(response).toBeTruthy() @@ -71,14 +71,14 @@ describe('AddFeed.vue', () => { expect(response).toBeFalsy() wrapper.vm.$data.newFolderName = 'test' - wrapper.vm.$store.state.folders.folders = [{ name: 'test'}] + wrapper.vm.$store.state.folders.folders = [{ name: 'test' }] response = wrapper.vm.folderNameExists() expect(response).toBeFalsy() wrapper.vm.$data.newFolderName = 'test' wrapper.vm.$data.createNewFolder = 'test' - wrapper.vm.$store.state.folders.folders = [{ name: 'test'}] + wrapper.vm.$store.state.folders.folders = [{ name: 'test' }] response = wrapper.vm.folderNameExists() expect(response).toBeTruthy() diff --git a/tests/javascript/unit/components/FeedItem.spec.ts b/tests/javascript/unit/components/FeedItem.spec.ts new file mode 100644 index 000000000..88991201f --- /dev/null +++ b/tests/javascript/unit/components/FeedItem.spec.ts @@ -0,0 +1,141 @@ +import { shallowMount, createLocalVue, Wrapper } from '@vue/test-utils' + +import FeedItem from '../../../../src/components/FeedItem.vue' +import { ACTIONS } from '../../../../src/store' + +describe('FeedItem.vue', () => { + 'use strict' + const localVue = createLocalVue() + let wrapper: Wrapper + + const mockItem = { + feedId: 1, + title: 'feed item', + pubDate: Date.now() / 1000, + } + const mockFeed = { + id: 1, + } + + const dispatchStub = jest.fn() + beforeAll(() => { + wrapper = shallowMount(FeedItem, { + propsData: { + item: mockItem, + }, + localVue, + mocks: { + $store: { + getters: { + feeds: [mockFeed], + }, + state: { + feeds: [], + folders: [], + }, + dispatch: dispatchStub, + }, + }, + }) + }) + + beforeEach(() => { + dispatchStub.mockReset() + }) + + it('should initialize without expanded and without keepUnread', () => { + expect(wrapper.vm.$data.expanded).toBeFalsy() + expect(wrapper.vm.$data.keepUnread).toBeFalsy() + }) + + it('should expand when clicked', async () => { + await wrapper.find('.feed-item-row').trigger('click') + + expect(wrapper.vm.$data.expanded).toBe(true) + }) + + it('should format date correctly', () => { + const epoch = Date.now() // Provide an epoch timestamp + const formattedDate = (wrapper.vm as any).formatDate(epoch) + + expect(formattedDate).toEqual(new Date(epoch).toLocaleString()) + }) + + it('should format datetime correctly', () => { + const epoch = Date.now() // Provide an epoch timestamp + const formattedDate = (wrapper.vm as any).formatDatetime(epoch) + + expect(formattedDate).toEqual(new Date(epoch).toISOString()) + }) + + it('should calculate relative timestamp correctly', () => { + const currentTimestamp = Date.now() + let pastTimestamp = currentTimestamp - 1000 * 10 // 10 seconds ago + + let relativeTimestamp = (wrapper.vm as any).getRelativeTimestamp(pastTimestamp) + + expect(relativeTimestamp).toEqual('10 seconds') + + pastTimestamp = currentTimestamp - 1000 * 60 * 10 // 10 minutes ago + + relativeTimestamp = (wrapper.vm as any).getRelativeTimestamp(pastTimestamp) + + expect(relativeTimestamp).toEqual('10 minutes ago') + }) + + it('should retrieve feed by ID', () => { + const feed = (wrapper.vm as any).getFeed(mockFeed.id) + + expect(feed).toEqual(mockFeed) + }) + + describe('markRead', () => { + it('should mark item as read when keepUnread is false', () => { + wrapper.vm.$data.keepUnread = false; + (wrapper.vm as any).markRead(wrapper.vm.$props.item) + + expect(dispatchStub).toHaveBeenCalledWith(ACTIONS.MARK_READ, { + item: wrapper.vm.$props.item, + }) + }) + + it('should not mark item as read when keepUnread is true', () => { + wrapper.vm.$data.keepUnread = true; + (wrapper.vm as any).markRead(wrapper.vm.$data.item) + + expect(dispatchStub).not.toHaveBeenCalled() + }) + }) + + it('toggles keepUnread state', () => { + const initialKeepUnread = wrapper.vm.$data.keepUnread; + (wrapper.vm as any).toggleKeepUnread(wrapper.vm.$data.item) + const updatedKeepUnread = wrapper.vm.$data.keepUnread + + expect(updatedKeepUnread).toBe(!initialKeepUnread) + }) + + it('toggles starred state', () => { + wrapper.vm.$props.item.starred = true; + + (wrapper.vm as any).toggleStarred(wrapper.vm.$props.item) + expect(dispatchStub).toHaveBeenCalledWith(ACTIONS.UNSTAR_ITEM, { + item: wrapper.vm.$props.item, + }) + + wrapper.vm.$props.item.starred = false; + + (wrapper.vm as any).toggleStarred(wrapper.vm.$props.item) + expect(dispatchStub).toHaveBeenCalledWith(ACTIONS.STAR_ITEM, { + item: wrapper.vm.$props.item, + }) + }) + + xit('TODO test: getMediaType(mime: any): audio | video | false', () => { + // TODO: finish tests after audio/video playback is supported + }) + + xit('TODO test: play(item: any): void', () => { + // TODO: finish tests after audio/video playback is supported + }) +}) diff --git a/tests/javascript/unit/components/Starred.spec.ts b/tests/javascript/unit/components/Starred.spec.ts new file mode 100644 index 000000000..212c69f6e --- /dev/null +++ b/tests/javascript/unit/components/Starred.spec.ts @@ -0,0 +1,75 @@ +import Vuex, { Store } from 'vuex' +import { shallowMount, createLocalVue, Wrapper } from '@vue/test-utils' + +import Starred from '../../../../src/components/Starred.vue' +import VirtualScroll from '../../../../src/components/VirtualScroll.vue' +import FeedItem from '../../../../src/components/FeedItem.vue' + +jest.mock('@nextcloud/axios') + +describe('Explore.vue', () => { + 'use strict' + const localVue = createLocalVue() + localVue.use(Vuex) + let wrapper: Wrapper + + const mockItem = { + feedId: 1, + title: 'feed item', + pubDate: Date.now() / 1000, + } + + let store: Store + beforeAll(() => { + store = new Vuex.Store({ + state: { + items: { + starredLoaded: false, + }, + }, + actions: { + }, + getters: { + starred: () => [mockItem], + }, + }) + wrapper = shallowMount(Starred, { + propsData: { + item: mockItem, + }, + localVue, + store, + }) + }) + + it('should initialize with mounted flag set', () => { + expect(wrapper.vm.$data.mounted).toBeTruthy() + }) + + it('should get starred items from state', () => { + expect((wrapper.findAllComponents(FeedItem).length)).toEqual(1) + }) + + it('should check starredLoaded and mounted to determine if the virtual scroll has reached end ', () => { + wrapper.vm.$store.state.items.starredLoaded = false + expect((wrapper.findComponent(VirtualScroll)).props().reachedEnd).toEqual(false) + + wrapper.vm.$store.state.items.starredLoaded = true + store.state.items.starredLoaded = true + + wrapper = shallowMount(Starred, { + propsData: { + item: mockItem, + }, + data: () => { + return { + mounted: true, + } + }, + localVue, + store, + }) + + expect((wrapper.findComponent(VirtualScroll)).props().reachedEnd).toEqual(true) + }) +}) diff --git a/tests/javascript/unit/setup.ts b/tests/javascript/unit/setup.ts index 510e53baa..d6f38128b 100644 --- a/tests/javascript/unit/setup.ts +++ b/tests/javascript/unit/setup.ts @@ -9,6 +9,7 @@ config.mocks.$t = function(_app: any, string: any) { return string } config.mocks.t = config.mocks.$t +global.t = config.mocks.$t config.mocks.$n = function(app: any, singular: any) { return singular diff --git a/tests/javascript/unit/store/item.spec.ts b/tests/javascript/unit/store/item.spec.ts new file mode 100644 index 000000000..614e47c1d --- /dev/null +++ b/tests/javascript/unit/store/item.spec.ts @@ -0,0 +1,96 @@ +import axios from '@nextcloud/axios' +import { AppState } from '../../../../src/store' +import { FEED_ITEM_ACTION_TYPES, mutations, actions } from '../../../../src/store/item' + +import { FEED_ITEM_MUTATION_TYPES } from '../../../../src/types/MutationTypes' +import { FeedItem } from '../../../../src/types/FeedItem' + +jest.mock('@nextcloud/axios') + +describe('feed.ts', () => { + 'use strict' + + describe('actions', () => { + describe('FETCH_STARRED', () => { + it('should call GET and commit items and starred count to state', async () => { + (axios as any).get.mockResolvedValue({ data: { items: [], starred: 3 } }) + const commit = jest.fn() + await (actions[FEED_ITEM_ACTION_TYPES.FETCH_STARRED] as any)({ commit }) + expect(axios.get).toBeCalled() + expect(commit).toBeCalledWith(FEED_ITEM_MUTATION_TYPES.SET_ITEMS, []) + expect(commit).toBeCalledWith(FEED_ITEM_MUTATION_TYPES.SET_STARRED_COUNT, 3) + }) + }) + + it('MARK_READ should call GET and commit returned feeds to state', async () => { + const item = { id: 1 } + const commit = jest.fn() + await (actions[FEED_ITEM_ACTION_TYPES.MARK_READ] as any)({ commit }, { item }) + expect(axios.post).toBeCalled() + expect(commit).toBeCalled() + }) + + it('MARK_UNREAD should call GET and commit returned feeds to state', async () => { + const item = { id: 1 } + const commit = jest.fn() + await (actions[FEED_ITEM_ACTION_TYPES.MARK_UNREAD] as any)({ commit }, { item }) + expect(axios.post).toBeCalled() + expect(commit).toBeCalledWith(FEED_ITEM_MUTATION_TYPES.UPDATE_ITEM, { item }) + }) + + it('STAR_ITEM should call GET and commit returned feeds to state', async () => { + const item = { id: 1 }; + (axios as any).get.mockResolvedValue({ data: { feeds: [] } }) + const commit = jest.fn() + await (actions[FEED_ITEM_ACTION_TYPES.STAR_ITEM] as any)({ commit }, { item }) + expect(axios.post).toBeCalled() + expect(commit).toBeCalled() + }) + + it('UNSTAR_ITEM should call GET and commit returned feeds to state', async () => { + const item = { id: 1 }; + (axios as any).get.mockResolvedValue({ data: { feeds: [] } }) + const commit = jest.fn() + await (actions[FEED_ITEM_ACTION_TYPES.UNSTAR_ITEM] as any)({ commit }, { item }) + expect(axios.post).toBeCalled() + expect(commit).toBeCalled() + }) + }) + + describe('mutations', () => { + describe('SET_ITEMS', () => { + it('should add feeds to state', () => { + const state = { allItems: [] as any } as AppState + let items = [] as any + + mutations[FEED_ITEM_MUTATION_TYPES.SET_ITEMS](state, items) + expect(state.allItems.length).toEqual(0) + + items = [{ title: 'test' }] as FeedItem[] + + mutations[FEED_ITEM_MUTATION_TYPES.SET_ITEMS](state, items) + expect(state.allItems.length).toEqual(1) + expect(state.allItems[0]).toEqual(items[0]) + }) + }) + + describe('SET_STARRED_COUNT', () => { + it('should add a single feed to state', () => { + const state = { } as AppState + + (mutations[FEED_ITEM_MUTATION_TYPES.SET_STARRED_COUNT] as any)(state, 13) + expect(state.starredCount).toEqual(13) + }) + }) + + describe('UPDATE_ITEM', () => { + it('should add a single feed to state', () => { + const state = { allItems: [{ id: 1, title: 'abc' }] as any } as AppState + const item = { title: 'test', id: 1 } as any + + (mutations[FEED_ITEM_MUTATION_TYPES.UPDATE_ITEM] as any)(state, { item }) + expect(state.allItems[0]).toEqual(item) + }) + }) + }) +}) -- cgit v1.2.3