|
-
+
-
+
-
+
-
+
{{ showMoreLabel }}
-
+
{{ getSelectedTopicAndResourceCountText(selected) }}
@@ -109,10 +151,18 @@
:text="$tr('deleteConfirmationText')"
>
-
+
{{ $tr('deleteConfirmationCancelButton') }}
-
+
{{ $tr('deleteConfirmationDeleteButton') }}
@@ -128,6 +178,8 @@
+
+
+
+
diff --git a/contentcuration/contentcuration/frontend/channelList/views/Channel/CatalogFilterBar.vue b/contentcuration/contentcuration/frontend/channelList/views/Channel/CatalogFilterBar.vue
index 6fb71d6b64..832d75bce4 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/Channel/CatalogFilterBar.vue
+++ b/contentcuration/contentcuration/frontend/channelList/views/Channel/CatalogFilterBar.vue
@@ -23,19 +23,20 @@
+
-
diff --git a/contentcuration/contentcuration/frontend/channelList/views/Channel/ChannelItem.vue b/contentcuration/contentcuration/frontend/channelList/views/Channel/ChannelItem.vue
index ba98f77283..c4fb7abf95 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/Channel/ChannelItem.vue
+++ b/contentcuration/contentcuration/frontend/channelList/views/Channel/ChannelItem.vue
@@ -11,31 +11,47 @@
:to="linkToChannelTree ? null : channelDetailsLink"
@click="goToChannelRoute"
>
-
-
+
+
-
+
-
- {{ $tr('resourceCount', { 'count': channel.count || 0 }) }}
+ {{ $tr('resourceCount', { count: channel.count || 0 }) }}
{{ language }}
-
+
{{ channel.description }}
@@ -44,23 +60,29 @@
-
+
-
+
- {{ $tr(
- 'lastPublished',
- {
- 'last_published': $formatRelative(
- channel.last_published,
- { now: new Date() }
- )
+ {{
+ $tr('lastPublished', {
+ last_published: $formatRelative(channel.last_published, { now: new Date() }),
})
}}
-
+
{{ $tr('unpublishedText') }}
@@ -69,13 +91,9 @@
placement="bottom"
:refs="$refs"
>
- {{ $tr(
- 'lastUpdated',
- {
- 'updated': $formatRelative(
- channel.modified,
- { now: new Date() }
- )
+ {{
+ $tr('lastUpdated', {
+ updated: $formatRelative(channel.modified, { now: new Date() }),
})
}}
@@ -91,7 +109,6 @@
v-if="!libraryMode"
:to="channelDetailsLink"
>
-
-
-
+
-
- {{ $tr('deletePrompt') }}
+ {{ canEdit ? $tr('deletePrompt') : $tr('removePrompt') }}
+
-
+
diff --git a/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/catalogList.spec.js b/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/catalogList.spec.js
index 31ad15eee5..1d6be7054b 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/catalogList.spec.js
+++ b/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/catalogList.spec.js
@@ -10,8 +10,13 @@ router.push({ name: RouteNames.CATALOG_ITEMS });
const results = ['channel-1', 'channel-2'];
-function makeWrapper(computed = {}, methods = {}) {
- return mount(CatalogList, {
+function makeWrapper(computed = {}) {
+ const loadCatalog = jest.spyOn(CatalogList.methods, 'loadCatalog');
+ loadCatalog.mockImplementation(() => Promise.resolve());
+
+ const downloadCSV = jest.spyOn(CatalogList.methods, 'downloadCSV');
+
+ const wrapper = mount(CatalogList, {
router,
store,
computed: {
@@ -23,24 +28,24 @@ function makeWrapper(computed = {}, methods = {}) {
},
...computed,
},
- methods,
stubs: {
CatalogFilters: true,
},
});
+ return [wrapper, { loadCatalog, downloadCSV }];
}
describe('catalogFilterBar', () => {
- let wrapper;
- beforeEach(() => {
- wrapper = makeWrapper();
- wrapper.setData({ loading: false });
+ let wrapper, mocks;
+
+ beforeEach(async () => {
+ [wrapper, mocks] = makeWrapper();
+ await wrapper.setData({ loading: false });
});
it('should call loadCatalog on mount', () => {
- const loadCatalog = jest.fn();
- wrapper = makeWrapper({}, { loadCatalog });
- expect(loadCatalog).toHaveBeenCalled();
+ [wrapper, mocks] = makeWrapper();
+ expect(mocks.loadCatalog).toHaveBeenCalled();
});
describe('on query change', () => {
@@ -49,29 +54,30 @@ describe('catalogFilterBar', () => {
beforeEach(() => {
router.replace({ query: {} }).catch(() => {});
searchCatalogMock.mockReset();
- wrapper = makeWrapper({
+ [wrapper, mocks] = makeWrapper({
debouncedSearch() {
return searchCatalogMock;
},
});
});
- it('should call debouncedSearch', () => {
+
+ it('should call debouncedSearch', async () => {
const keywords = 'search catalog test';
router.push({ query: { keywords } }).catch(() => {});
- wrapper.vm.$nextTick(() => {
- expect(searchCatalogMock).toHaveBeenCalled();
- });
+ await wrapper.vm.$nextTick();
+ expect(searchCatalogMock).toHaveBeenCalled();
});
- it('should reset excluded if a filter changed', () => {
+
+ it('should reset excluded if a filter changed', async () => {
const keywords = 'search reset test';
- wrapper.setData({ excluded: ['item 1'] });
+ await wrapper.setData({ excluded: ['item 1'] });
router.push({ query: { keywords } }).catch(() => {});
- wrapper.vm.$nextTick(() => {
- expect(wrapper.vm.excluded).toEqual([]);
- });
+ await wrapper.vm.$nextTick();
+ expect(wrapper.vm.excluded).toEqual([]);
});
- it('should keep excluded if page number changed', () => {
- wrapper.setData({ excluded: ['item 1'] });
+
+ it('should keep excluded if page number changed', async () => {
+ await wrapper.setData({ excluded: ['item 1'] });
router
.push({
query: {
@@ -80,29 +86,31 @@ describe('catalogFilterBar', () => {
},
})
.catch(() => {});
- wrapper.vm.$nextTick(() => {
- expect(wrapper.vm.excluded).toEqual(['item 1']);
- });
+ await wrapper.vm.$nextTick();
+ expect(wrapper.vm.excluded).toEqual(['item 1']);
});
});
describe('download workflow', () => {
describe('toggling selection mode', () => {
it('checkboxes and toolbar should be hidden if selecting is false', () => {
- expect(wrapper.find('[data-test="checkbox"]').exists()).toBe(false);
- expect(wrapper.find('[data-test="toolbar"]').exists()).toBe(false);
+ expect(wrapper.findComponent('[data-test="checkbox"]').exists()).toBe(false);
+ expect(wrapper.findComponent('[data-test="toolbar"]').exists()).toBe(false);
});
- it('should activate when select button is clicked', () => {
- wrapper.find('[data-test="select"]').trigger('click');
+
+ it('should activate when select button is clicked', async () => {
+ await wrapper.findComponent('[data-test="select"]').trigger('click');
expect(wrapper.vm.selecting).toBe(true);
});
- it('clicking cancel should exit selection mode', () => {
- wrapper.setData({ selecting: true });
- wrapper.find('[data-test="cancel"]').trigger('click');
+
+ it('clicking cancel should exit selection mode', async () => {
+ await wrapper.setData({ selecting: true });
+ await wrapper.findComponent('[data-test="cancel"]').trigger('click');
expect(wrapper.vm.selecting).toBe(false);
});
- it('excluded should reset when selection mode is exited', () => {
- wrapper.setData({ selecting: true, excluded: ['item-1', 'item-2'] });
+
+ it('excluded should reset when selection mode is exited', async () => {
+ await wrapper.setData({ selecting: true, excluded: ['item-1', 'item-2'] });
wrapper.vm.setSelection(false);
expect(wrapper.vm.excluded).toHaveLength(0);
});
@@ -110,29 +118,34 @@ describe('catalogFilterBar', () => {
describe('selecting channels', () => {
const excluded = ['item-1'];
- beforeEach(() => {
- wrapper.setData({
+
+ beforeEach(async () => {
+ await wrapper.setData({
selecting: true,
excluded,
});
});
- it('selecting all should select all items on the page', () => {
- wrapper.setData({ excluded: excluded.concat(results) });
+
+ it('selecting all should select all items on the page', async () => {
+ await wrapper.setData({ excluded: excluded.concat(results) });
wrapper.vm.selectAll = true;
expect(wrapper.vm.excluded).toEqual(excluded);
expect(wrapper.vm.selected).toEqual(results);
});
+
it('deselecting all should select all items on the page', () => {
wrapper.vm.selectAll = false;
expect(wrapper.vm.excluded).toEqual(excluded.concat(results));
expect(wrapper.vm.selected).toEqual([]);
});
- it('selecting a channel should remove it from excluded', () => {
- wrapper.setData({ excluded: excluded.concat(results) });
+
+ it('selecting a channel should remove it from excluded', async () => {
+ await wrapper.setData({ excluded: excluded.concat(results) });
wrapper.vm.selected = [results[0]];
expect(wrapper.vm.excluded).toEqual(excluded.concat([results[1]]));
expect(wrapper.vm.selected).toEqual([results[0]]);
});
+
it('deselecting a channel should add it to excluded', () => {
wrapper.vm.selected = [results[0]];
expect(wrapper.vm.excluded).toEqual(excluded.concat([results[1]]));
@@ -141,31 +154,35 @@ describe('catalogFilterBar', () => {
});
describe('download csv', () => {
- const downloadChannelsCSV = jest.fn();
+ let downloadChannelsCSV;
const excluded = ['item-1', 'item-2'];
- beforeEach(() => {
- downloadChannelsCSV.mockReset();
- wrapper.setData({ selecting: true, excluded });
- wrapper.setMethods({ downloadChannelsCSV });
+
+ beforeEach(async () => {
+ await wrapper.setData({ selecting: true, excluded });
+ downloadChannelsCSV = jest.spyOn(wrapper.vm, 'downloadChannelsCSV');
+ downloadChannelsCSV.mockImplementation(() => Promise.resolve());
});
- it('clicking download CSV should call downloadCSV', () => {
- const downloadCSV = jest.fn();
- wrapper.setMethods({ downloadCSV });
- wrapper.find('[data-test="download-csv"]').trigger('click');
- expect(downloadCSV).toHaveBeenCalled();
+
+ it('clicking download CSV should call downloadCSV', async () => {
+ mocks.downloadCSV.mockImplementationOnce(() => Promise.resolve());
+ await wrapper.findComponent('[data-test="download-csv"]').trigger('click');
+ expect(mocks.downloadCSV).toHaveBeenCalled();
});
- it('downloadCSV should call downloadChannelsCSV with current parameters', () => {
+
+ it('downloadCSV should call downloadChannelsCSV with current parameters', async () => {
const keywords = 'Download csv keywords test';
router.replace({ query: { keywords } });
- wrapper.vm.downloadCSV();
+ await wrapper.vm.downloadCSV();
expect(downloadChannelsCSV.mock.calls[0][0].keywords).toBe(keywords);
});
- it('downloadCSV should call downloadChannelsCSV with list of excluded items', () => {
- wrapper.vm.downloadCSV();
+
+ it('downloadCSV should call downloadChannelsCSV with list of excluded items', async () => {
+ await wrapper.vm.downloadCSV();
expect(downloadChannelsCSV.mock.calls[0][0].excluded).toEqual(excluded);
});
- it('downloadCSV should exit selection mode', () => {
- wrapper.vm.downloadCSV();
+
+ it('downloadCSV should exit selection mode', async () => {
+ await wrapper.vm.downloadCSV();
expect(wrapper.vm.selecting).toBe(false);
});
});
diff --git a/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/channelInvitation.spec.js b/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/channelInvitation.spec.js
index ee43657298..42a2e9a922 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/channelInvitation.spec.js
+++ b/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/channelInvitation.spec.js
@@ -24,23 +24,27 @@ function makeWrapper() {
describe('channelInvitation', () => {
let wrapper;
+
beforeEach(() => {
wrapper = makeWrapper();
});
- it('clicking on the accept button should accept the invitation', () => {
- const acceptInvitation = jest.fn().mockReturnValue(Promise.resolve());
- wrapper.setMethods({ acceptInvitation });
- wrapper.find('[data-test="accept"]').trigger('click');
+
+ it('clicking on the accept button should accept the invitation', async () => {
+ const acceptInvitation = jest.spyOn(wrapper.vm, 'acceptInvitation');
+ acceptInvitation.mockImplementation(() => Promise.resolve());
+ await wrapper.find('[data-test="accept"]').trigger('click');
expect(acceptInvitation).toHaveBeenCalledWith(invitationID);
});
- it('clicking on the decline button should open confirmation modal', () => {
- wrapper.find('[data-test="decline"]').trigger('click');
+
+ it('clicking on the decline button should open confirmation modal', async () => {
+ await wrapper.find('[data-test="decline"]').trigger('click');
expect(wrapper.vm.dialog).toBe(true);
});
- it('clicking on the decline button should decline the invitation', () => {
- const declineInvitation = jest.fn().mockReturnValue(Promise.resolve());
- wrapper.setMethods({ declineInvitation });
- wrapper.find('[data-test="decline-close"]').trigger('click');
+
+ it('clicking on the decline button should decline the invitation', async () => {
+ const declineInvitation = jest.spyOn(wrapper.vm, 'declineInvitation');
+ declineInvitation.mockImplementation(() => Promise.resolve());
+ await wrapper.find('[data-test="decline-close"]').trigger('click');
expect(declineInvitation).toHaveBeenCalledWith(invitationID);
});
});
diff --git a/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/channelItem.spec.js b/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/channelItem.spec.js
index 79bcea297e..bee20bfd13 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/channelItem.spec.js
+++ b/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/channelItem.spec.js
@@ -1,6 +1,4 @@
-import Vue from 'vue';
import { mount } from '@vue/test-utils';
-import VueRouter from 'vue-router';
import { factory } from '../../../store';
import router from '../../../router';
import { RouteNames } from '../../../constants';
@@ -8,12 +6,13 @@ import ChannelItem from '../ChannelItem.vue';
const store = factory();
-Vue.use(VueRouter);
const channelId = 'testing';
const channel = {
id: channelId,
edit: true,
published: true,
+ last_published: new Date().getTime() - 100000,
+ modified: new Date().getTime() - 100000,
};
store.state.session.currentUser.id = 0;
@@ -38,49 +37,76 @@ function makeWrapper(allowEdit, deleteStub, libraryMode) {
},
},
});
- wrapper.setMethods({
- handleDelete: deleteStub,
- });
+ const deleteSpy = jest.spyOn(wrapper.vm, 'deleteChannel');
+ if (deleteStub) {
+ deleteSpy.mockImplementation(deleteStub);
+ } else {
+ deleteSpy.mockImplementation(() => {});
+ }
return wrapper;
}
describe('channelItem', () => {
let wrapper;
const deleteStub = jest.fn();
+
beforeEach(() => {
deleteStub.mockReset();
wrapper = makeWrapper(true, deleteStub);
});
- afterEach(() => {
+
+ afterEach(async () => {
// Delete dialog and token dialog use primary modal component, so
// leaving either to be true will block subsequent modals from opening
- wrapper.setData({ deleteDialog: false, tokenDialog: false });
+ await wrapper.setData({ deleteDialog: false, tokenDialog: false });
});
+
it('edit options should be hidden if edit mode is off', () => {
wrapper = makeWrapper(false, deleteStub);
- expect(wrapper.find('[data-test="edit-channel"]').exists()).toBe(false);
+ expect(wrapper.findComponent('[data-test="edit-channel"]').exists()).toBe(false);
});
- it('clicking the channel should open the channel', () => {
- wrapper.find('[data-test="channel-card"]').trigger('click');
+
+ it('clicking the channel should open the channel', async () => {
+ await wrapper.findComponent('[data-test="channel-card"]').trigger('click');
expect(wrapper.vm.$route.name).toEqual(RouteNames.CHANNELS_EDITABLE);
});
- it('clicking edit channel should open a channel editor modal', () => {
- wrapper.find('[data-test="edit-channel"]').trigger('click');
+
+ it('clicking edit channel should open a channel editor modal', async () => {
+ await wrapper.findComponent('[data-test="edit-channel"]').trigger('click');
expect(wrapper.vm.$route.name).toEqual(RouteNames.CHANNEL_EDIT);
});
- it('clicking the info icon should open a channel details modal ', () => {
- wrapper.find('[data-test="details-button"]').trigger('click');
+
+ it('clicking the info icon should open a channel details modal ', async () => {
+ await wrapper.findComponent('[data-test="details-button"]').trigger('click');
expect(wrapper.vm.$route.name).toEqual(RouteNames.CHANNEL_DETAILS);
});
- it('clicking the token option should open the token modal', () => {
- wrapper.find('[data-test="token-listitem"]').trigger('click');
+
+ it('clicking the token option should open the token modal', async () => {
+ await wrapper.findComponent('[data-test="token-listitem"]').trigger('click');
expect(wrapper.vm.tokenDialog).toBe(true);
});
- it('clicking delete button in dialog should delete the channel', () => {
- wrapper.setData({ deleteDialog: true });
- wrapper.find('[data-test="delete-modal"]').trigger('submit');
- wrapper.vm.$nextTick(() => {
- expect(deleteStub).toHaveBeenCalled();
+
+ it('when user can edit, clicking delete button in dialog should call deleteChannel', async () => {
+ const removeViewerSpy = jest.spyOn(wrapper.vm, 'removeViewer');
+ removeViewerSpy.mockResolvedValue();
+
+ await wrapper.setData({ deleteDialog: true });
+ await wrapper.findComponent('[data-test="delete-modal"]').trigger('submit');
+ await wrapper.vm.$nextTick(() => {
+ expect(deleteStub).toHaveBeenCalledWith(channelId);
+ expect(removeViewerSpy).not.toHaveBeenCalled();
+ });
+ });
+
+ it('when user cannot edit, clicking delete button in dialog should call removeViewer', async () => {
+ const removeViewerSpy = jest.spyOn(wrapper.vm, 'removeViewer');
+ removeViewerSpy.mockResolvedValue();
+
+ await wrapper.setData({ deleteDialog: true });
+ await wrapper.findComponent('[data-test="delete-modal"]').trigger('submit');
+ await wrapper.vm.$nextTick(() => {
+ expect(removeViewerSpy).toHaveBeenCalledWith({ channelId, userId: 0 });
+ expect(deleteStub).not.toHaveBeenCalled();
});
});
@@ -88,20 +114,23 @@ describe('channelItem', () => {
beforeEach(() => {
wrapper = makeWrapper(false, null, true);
});
- it('clicking channel card should open details modal', () => {
- wrapper.find('[data-test="channel-card"]').trigger('click');
+
+ it('clicking channel card should open details modal', async () => {
+ await wrapper.findComponent('[data-test="channel-card"]').trigger('click');
expect(wrapper.vm.$route.name).toEqual(RouteNames.CHANNEL_DETAILS);
});
- it('clicking the token button should open the token modal', () => {
- wrapper.setData({ tokenDialog: false });
- wrapper.find('[data-test="token-button"]').trigger('click');
+
+ it('clicking the token button should open the token modal', async () => {
+ await wrapper.setData({ tokenDialog: false });
+ await wrapper.findComponent('[data-test="token-button"]').trigger('click');
expect(wrapper.vm.tokenDialog).toBe(true);
});
+
it('certain menu options should be hidden', () => {
- expect(wrapper.find('[data-test="edit-channel"]').exists()).toBe(false);
- expect(wrapper.find('[data-test="delete-channel"]').exists()).toBe(false);
- expect(wrapper.find('[data-test="token-listitem"]').exists()).toBe(false);
- expect(wrapper.find('[data-test="details-button"]').exists()).toBe(false);
+ expect(wrapper.findComponent('[data-test="edit-channel"]').exists()).toBe(false);
+ expect(wrapper.findComponent('[data-test="delete-channel"]').exists()).toBe(false);
+ expect(wrapper.findComponent('[data-test="token-listitem"]').exists()).toBe(false);
+ expect(wrapper.findComponent('[data-test="details-button"]').exists()).toBe(false);
});
});
});
diff --git a/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/channelList.spec.js b/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/channelList.spec.js
index 0f52f62a7a..92ac547cff 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/channelList.spec.js
+++ b/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/channelList.spec.js
@@ -2,6 +2,7 @@ import { mount, createLocalVue } from '@vue/test-utils';
import Vuex, { Store } from 'vuex';
import VueRouter from 'vue-router';
import ChannelList from '../ChannelList.vue';
+import { RouteNames } from '../../../constants';
import { ChannelListTypes } from 'shared/constants';
const localVue = createLocalVue();
@@ -79,9 +80,16 @@ describe('ChannelList', () => {
expect(getNewChannelButton(wrapper).exists()).toBe(true);
});
- it('should create a new channel when new channel button is clicked', () => {
- getNewChannelButton(wrapper).trigger('click');
- expect(createChannelMock).toHaveBeenCalled();
+ it('should open the new channel modal when the new channel button is clicked', async () => {
+ try {
+ getNewChannelButton(wrapper).trigger('click');
+ await wrapper.vm.$nextTick();
+ } catch (err) {
+ if (err.name !== 'NavigationDuplicated') {
+ throw err;
+ }
+ }
+ expect(wrapper.vm.$route.name).toBe(RouteNames.NEW_CHANNEL);
});
});
});
diff --git a/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/mixins.spec.js b/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/mixins.spec.js
index 299d75b435..29c366e4f6 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/mixins.spec.js
+++ b/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/mixins.spec.js
@@ -11,7 +11,7 @@ describe('mixins', () => {
{
mixins: [catalogFilterMixin],
},
- { router }
+ { router },
);
});
diff --git a/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/star.spec.js b/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/star.spec.js
index c1a33b60cf..12c69f8460 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/star.spec.js
+++ b/contentcuration/contentcuration/frontend/channelList/views/Channel/__tests__/star.spec.js
@@ -10,9 +10,7 @@ function makeWrapper(bookmark, toggleStub) {
channelId,
},
});
- wrapper.setMethods({
- toggleStar: toggleStub,
- });
+ jest.spyOn(wrapper.vm, 'toggleStar').mockImplementation(toggleStub);
return wrapper;
}
@@ -20,16 +18,19 @@ describe('star', () => {
let starredWrapper;
let unstarredWrapper;
const toggleStub = jest.fn();
+
beforeEach(() => {
starredWrapper = makeWrapper(true, toggleStub);
unstarredWrapper = makeWrapper(false, toggleStub);
});
+
it('should reflect correct star on load', () => {
expect(starredWrapper.find('[data-test="button"]').vm.icon).toBe('star');
expect(unstarredWrapper.find('[data-test="button"]').vm.icon).toBe('starBorder');
});
- it('toggle the bookmark when clicked', () => {
- starredWrapper.find('[data-test="button"]').trigger('click');
+
+ it('toggle the bookmark when clicked', async () => {
+ await starredWrapper.findComponent('[data-test="button"]').trigger('click');
expect(toggleStub).toHaveBeenCalled();
});
});
diff --git a/contentcuration/contentcuration/frontend/channelList/views/Channel/components/LanguageFilter.vue b/contentcuration/contentcuration/frontend/channelList/views/Channel/components/LanguageFilter.vue
index 47374365b9..40ceee7887 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/Channel/components/LanguageFilter.vue
+++ b/contentcuration/contentcuration/frontend/channelList/views/Channel/components/LanguageFilter.vue
@@ -18,9 +18,15 @@
@blur="resetScroll"
>
-
+
-
+
{{ item.name }}
@@ -38,9 +44,16 @@
class="mb-0 mt-1 scroll-margin"
:labelDir="null"
>
-
+
-
+
{{ item.name }}
@@ -123,16 +136,16 @@
-
+
diff --git a/contentcuration/contentcuration/frontend/channelList/views/ChannelListIndex.vue b/contentcuration/contentcuration/frontend/channelList/views/ChannelListIndex.vue
index 4dee071859..c1d29c75ec 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/ChannelListIndex.vue
+++ b/contentcuration/contentcuration/frontend/channelList/views/ChannelListIndex.vue
@@ -14,7 +14,7 @@
exact
color="appBarDark"
class="ma-0"
- style="border-radius: 8px;"
+ style="border-radius: 8px"
>
-
+
-
+
{{ $formatNumber(invitationsByListCounts[listType]) }}
{{ translateConstant(listType) }}
-
- {{ $tr("catalog") }}
+
+ {{ $tr('catalog') }}
-
- {{ $tr("channelSets") }}
+
+ {{ $tr('channelSets') }}
-
+
-
-
-
-
+
+
+
+
{{ $tr('invitations', { count: invitationList.length }) }}
@@ -74,7 +105,10 @@
-
+
@@ -164,7 +198,7 @@
const invitations = this.invitations;
return (
invitations.filter(
- i => ChannelInvitationMapping[i.share_mode] === this.currentListType
+ i => ChannelInvitationMapping[i.share_mode] === this.currentListType,
) || []
);
},
@@ -172,7 +206,7 @@
const inviteMap = {};
Object.values(ChannelListTypes).forEach(type => {
inviteMap[type] = this.invitations.filter(
- i => ChannelInvitationMapping[i.share_mode] === type
+ i => ChannelInvitationMapping[i.share_mode] === type,
).length;
});
return inviteMap;
@@ -267,7 +301,7 @@
-
+
diff --git a/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSelectionList.spec.js b/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSelectionList.spec.js
index b35725f43a..a7befd7c43 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSelectionList.spec.js
+++ b/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSelectionList.spec.js
@@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils';
-import Vuex from 'vuex';
+import { Store } from 'vuex';
import ChannelSelectionList from '../ChannelSelectionList';
import { ChannelListTypes } from 'shared/constants';
@@ -35,7 +35,7 @@ const actions = {
loadChannelList: jest.fn(() => Promise.resolve()),
};
-const store = new Vuex.Store({
+const store = new Store({
modules: {
channel: {
namespaced: true,
@@ -46,8 +46,10 @@ const store = new Vuex.Store({
});
function makeWrapper() {
- return mount(ChannelSelectionList, {
- sync: false,
+ const loadChannelList = jest.spyOn(ChannelSelectionList.methods, 'loadChannelList');
+ loadChannelList.mockImplementation(() => Promise.resolve());
+
+ const wrapper = mount(ChannelSelectionList, {
propsData: {
listType: ChannelListTypes.EDITABLE,
},
@@ -56,54 +58,62 @@ function makeWrapper() {
return [editChannel, editChannel2, publicChannel];
},
},
- methods: {
- loadChannelList() {
- return Promise.resolve();
- },
- },
store,
});
+
+ return [wrapper, { loadChannelList }];
}
describe('channelSelectionList', () => {
- let wrapper;
+ let wrapper, mocks;
+
beforeEach(() => {
- wrapper = makeWrapper();
+ [wrapper, mocks] = makeWrapper();
+ });
+
+ afterEach(() => {
+ mocks.loadChannelList.mockRestore();
});
- it('should show the correct channels based on listType', () => {
- wrapper.setData({ loading: false });
+
+ it('should show the correct channels based on listType', async () => {
+ await wrapper.setData({ loading: false });
expect(wrapper.vm.listChannels.find(c => c.id === editChannel.id)).toBeTruthy();
expect(wrapper.vm.listChannels.find(c => c.id === editChannel2.id)).toBeTruthy();
expect(wrapper.vm.listChannels.find(c => c.id === publicChannel.id)).toBeFalsy();
});
- it('should select channels when the channel has been checked', () => {
- wrapper.setData({ loading: false });
- wrapper.find(`[data-test="checkbox-${editChannel.id}"]`).element.click();
+
+ it('should select channels when the channel has been checked', async () => {
+ await wrapper.setData({ loading: false });
+ await wrapper.findComponent(`[data-test="checkbox-${editChannel.id}"]`).trigger('click');
expect(wrapper.emitted('input')[0][0]).toEqual([editChannel.id]);
});
- it('should deselect channels when the channel has been unchecked', () => {
- wrapper.setData({ loading: false });
- wrapper.find(`[data-test="checkbox-${editChannel.id}"]`).element.click(); // Check the channel
- wrapper.find(`[data-test="checkbox-${editChannel.id}"]`).element.click(); // Uncheck the channel
+
+ it('should deselect channels when the channel has been unchecked', async () => {
+ await wrapper.setData({ loading: false });
+ await wrapper.findComponent(`[data-test="checkbox-${editChannel.id}"]`).trigger('click'); // Check the channel
+ await wrapper.findComponent(`[data-test="checkbox-${editChannel.id}"]`).trigger('click'); // Uncheck the channel
expect(wrapper.emitted('input')[0].length).toEqual(1); // Only one event should be emitted (corresponding to the initial check)
expect(wrapper.emitted('input')[0][0]).toEqual([editChannel.id]); // The initial check event should be emitted
});
- it('should filter channels based on the search text', () => {
- wrapper.setData({ loading: false, search: searchWord });
+
+ it('should filter channels based on the search text', async () => {
+ await wrapper.setData({ loading: false, search: searchWord });
expect(wrapper.vm.listChannels.find(c => c.id === editChannel.id)).toBeTruthy();
expect(wrapper.vm.listChannels.find(c => c.id === editChannel2.id)).toBeFalsy();
});
- it('should select channels when the channel card has been clicked', () => {
- wrapper.setData({ loading: false });
- wrapper.find(`[data-test="channel-item-${editChannel.id}"]`).trigger('click');
+
+ it('should select channels when the channel card has been clicked', async () => {
+ await wrapper.setData({ loading: false });
+ await wrapper.findComponent(`[data-test="channel-item-${editChannel.id}"]`).trigger('click');
expect(wrapper.emitted('input')[0][0]).toEqual([editChannel.id]);
});
- it('should deselect channels when the channel card has been clicked', () => {
- wrapper.setData({ loading: false });
- wrapper.find(`[data-test="channel-item-${editChannel.id}"]`).element.click(); // Check the channel
- wrapper.find(`[data-test="channel-item-${editChannel.id}"]`).element.click(); // Uncheck the channel
+
+ it('should deselect channels when the channel card has been clicked', async () => {
+ await wrapper.setData({ loading: false });
+ await wrapper.findComponent(`[data-test="channel-item-${editChannel.id}"]`).trigger('click'); // Check the channel
+ await wrapper.findComponent(`[data-test="channel-item-${editChannel.id}"]`).trigger('click'); // Uncheck the channel
expect(wrapper.emitted('input')[0].length).toEqual(1); // Only one event should be emitted (corresponding to the initial check)
expect(wrapper.emitted('input')[0][0]).toEqual([editChannel.id]); // The initial check event should be emitted
diff --git a/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSetItem.spec.js b/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSetItem.spec.js
index bd6f048c68..bbb57080e3 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSetItem.spec.js
+++ b/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSetItem.spec.js
@@ -14,33 +14,33 @@ const channelSet = {
store.commit('channelSet/ADD_CHANNELSET', channelSet);
-function makeWrapper(deleteStub) {
+function makeWrapper() {
const wrapper = mount(ChannelSetItem, {
router,
store,
sync: false,
propsData: { channelSetId: channelSet.id },
});
- wrapper.setMethods({
- deleteChannelSet: deleteStub,
- });
- return wrapper;
+ const deleteChannelSet = jest.spyOn(wrapper.vm, 'deleteChannelSet');
+ deleteChannelSet.mockImplementation(() => Promise.resolve());
+ return [wrapper, { deleteChannelSet }];
}
describe('channelSetItem', () => {
- let wrapper;
- const deleteStub = jest.fn();
+ let wrapper, mocks;
+
beforeEach(() => {
- deleteStub.mockReset();
- wrapper = makeWrapper(deleteStub);
+ [wrapper, mocks] = makeWrapper();
});
+
it('clicking the edit option should open the channel set edit modal', () => {
wrapper.find('[data-test="edit"]').trigger('click');
expect(wrapper.vm.$route.name).toEqual(RouteNames.CHANNEL_SET_DETAILS);
});
+
it('clicking delete button in dialog should delete the channel set', () => {
wrapper.vm.deleteDialog = true;
wrapper.find('[data-test="delete"]').trigger('click');
- expect(deleteStub).toHaveBeenCalled();
+ expect(mocks.deleteChannelSet).toHaveBeenCalled();
});
});
diff --git a/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSetList.spec.js b/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSetList.spec.js
index 813c48ffa3..61fb53847a 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSetList.spec.js
+++ b/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSetList.spec.js
@@ -6,28 +6,27 @@ import ChannelSetList from '../ChannelSetList.vue';
const store = factory();
-const id = '00000000000000000000000000000000';
-
-function makeWrapper(createChannelSetStub) {
+function makeWrapper() {
router.push({
name: RouteNames.CHANNEL_SETS,
});
- const wrapper = mount(ChannelSetList, { store, router });
- wrapper.setMethods({
- createChannelSet: createChannelSetStub,
- });
- return wrapper;
+ return mount(ChannelSetList, { store, router });
}
describe('channelSetList', () => {
let wrapper;
- const createChannelSetStub = jest.fn().mockImplementation(() => Promise.resolve(id));
- beforeEach(() => {
- wrapper = makeWrapper(createChannelSetStub);
+
+ beforeEach(async () => {
+ wrapper = makeWrapper();
+ await wrapper.setData({ loading: false });
});
- it('should create a new channel set when new set button is clicked', () => {
- wrapper.setData({ loading: false });
- wrapper.find('[data-test="add-channelset"]').trigger('click');
- expect(createChannelSetStub).toHaveBeenCalled();
+
+ it('should open a new channel set modal when new set button is clicked', async () => {
+ const push = jest.fn();
+ wrapper.vm.$router.push = push;
+ await wrapper.find('[data-test="add-channelset"]').trigger('click');
+ expect(push).toHaveBeenCalledWith({
+ name: RouteNames.NEW_CHANNEL_SET,
+ });
});
});
diff --git a/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSetModal.spec.js b/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSetModal.spec.js
index 5ea74f9258..959b677c5e 100644
--- a/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSetModal.spec.js
+++ b/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSetModal.spec.js
@@ -10,7 +10,6 @@ import channelSet from '../../../vuex/channelSet';
import ChannelSetModal from '../ChannelSetModal';
import channel from 'shared/vuex/channel';
import storeFactory from 'shared/vuex/baseStore';
-import { NEW_OBJECT } from 'shared/constants';
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -39,19 +38,6 @@ const CHANNEL_SET = {
},
};
-const NEW_CHANNEL_SET = {
- id: 'id-new-channel-set',
- channels: [],
- [NEW_OBJECT]: true,
-};
-
-const loadChannelSetMock = (cs, store) => {
- return jest.fn().mockImplementation(() => {
- store.commit('channelSet/ADD_CHANNELSET', cs);
- return Promise.resolve(cs);
- });
-};
-
const makeWrapper = ({ store, channelSetId }) => {
if (router.currentRoute.name !== RouteNames.CHANNEL_SET_DETAILS) {
router.push({
@@ -62,48 +48,47 @@ const makeWrapper = ({ store, channelSetId }) => {
});
}
- const loadChannelSet = loadChannelSetMock(CHANNEL_SET, store);
- const loadChannelList = jest.fn().mockImplementation(() => Promise.resolve(CHANNEL_SET.channels));
+ const loadChannelSet = jest.spyOn(ChannelSetModal.methods, 'loadChannelSet');
+ loadChannelSet.mockImplementation(() => {
+ store.commit('channelSet/ADD_CHANNELSET', CHANNEL_SET);
+ return Promise.resolve(CHANNEL_SET);
+ });
+ const loadChannelList = jest.spyOn(ChannelSetModal.methods, 'loadChannelList');
+ loadChannelList.mockImplementation(() => Promise.resolve(CHANNEL_SET.channels));
const wrapper = mount(ChannelSetModal, {
propsData: {
channelSetId,
},
- methods: {
- loadChannelSet,
- loadChannelList,
- },
router,
localVue,
store,
});
- wrapper.loadChannelSet = loadChannelSet;
- wrapper.loadChannelList = loadChannelList;
- return wrapper;
+ return [wrapper, { loadChannelSet, loadChannelList }];
};
const getCollectionNameInput = wrapper => {
- return wrapper.find('[data-test="input-name"]');
+ return wrapper.findComponent('[data-test="input-name"]');
};
const getUnsavedDialog = wrapper => {
- return wrapper.find('[data-test="dialog-unsaved"]');
+ return wrapper.findComponent('[data-test="dialog-unsaved"]');
};
const getCloseButton = wrapper => {
- return wrapper.find('[data-test="close"]');
+ return wrapper.findComponent('[data-test="close"]');
};
const getSaveButton = wrapper => {
- return wrapper.find('[data-test="button-save"]');
+ return wrapper.findComponent('[data-test="button-save"]');
};
const getSelectChannelsButton = wrapper => {
- return wrapper.find('[data-test="button-select"]');
+ return wrapper.findComponent('[data-test="button-select"]');
};
const getFinishButton = wrapper => {
- return wrapper.find('[data-test="button-finish"]');
+ return wrapper.findComponent('[data-test="button-finish"]');
};
describe('ChannelSetModal', () => {
@@ -114,51 +99,52 @@ describe('ChannelSetModal', () => {
it('should show collection channels view at first', () => {
const storeConfig = cloneDeep(STORE_CONFIG);
const store = storeFactory(storeConfig);
- const wrapper = makeWrapper({ store, channelSetId: CHANNEL_SET.id });
+ const [wrapper] = makeWrapper({ store, channelSetId: CHANNEL_SET.id });
expect(wrapper.find('[data-test="collection-channels-view"]').isVisible()).toBe(true);
});
describe('if there are no data for a channel set yet', () => {
- let wrapper;
+ let mocks;
+
beforeEach(() => {
const storeConfig = cloneDeep(STORE_CONFIG);
const store = storeFactory(storeConfig);
- wrapper = makeWrapper({ store, channelSetId: CHANNEL_SET.id });
+ mocks = makeWrapper({ store, channelSetId: CHANNEL_SET.id })[1];
});
it('should load the channel set', () => {
- expect(wrapper.loadChannelSet).toHaveBeenCalledTimes(1);
- expect(wrapper.loadChannelSet.mock.calls[0][0]).toBe(CHANNEL_SET.id);
+ expect(mocks.loadChannelSet).toHaveBeenCalledTimes(1);
+ expect(mocks.loadChannelSet.mock.calls[0][0]).toBe(CHANNEL_SET.id);
});
it('should load channels of the channel set', () => {
- expect(wrapper.loadChannelList).toHaveBeenCalledTimes(1);
- expect(wrapper.loadChannelList.mock.calls[0][0]).toEqual({
+ expect(mocks.loadChannelList).toHaveBeenCalledTimes(1);
+ expect(mocks.loadChannelList.mock.calls[0][0]).toEqual({
id__in: [CHANNEL_1.id, CHANNEL_2.id],
});
});
});
describe('if a channel set has been already loaded', () => {
- let store, wrapper;
+ let store, mocks;
beforeEach(() => {
const storeConfig = cloneDeep(STORE_CONFIG);
store = storeFactory(storeConfig);
store.commit('channelSet/ADD_CHANNELSET', CHANNEL_SET);
- wrapper = makeWrapper({ store, channelSetId: CHANNEL_SET.id });
+ mocks = makeWrapper({ store, channelSetId: CHANNEL_SET.id })[1];
});
it("shouldn't load the channel set", () => {
- expect(wrapper.loadChannelSet).not.toHaveBeenCalled();
+ expect(mocks.loadChannelSet).not.toHaveBeenCalled();
});
it('should load channels from the channel set', () => {
- expect(wrapper.loadChannelList).toHaveBeenCalledTimes(1);
- expect(wrapper.loadChannelList.mock.calls[0][0]).toEqual({
+ expect(mocks.loadChannelList).toHaveBeenCalledTimes(1);
+ expect(mocks.loadChannelList.mock.calls[0][0]).toEqual({
id__in: [CHANNEL_1.id, CHANNEL_2.id],
});
});
@@ -176,7 +162,7 @@ describe('ChannelSetModal', () => {
store.commit('channelSet/ADD_CHANNELSET', CHANNEL_SET);
store.commit('channel/ADD_CHANNELS', [CHANNEL_1, CHANNEL_2]);
- wrapper = makeWrapper({ store, channelSetId: CHANNEL_SET.id });
+ [wrapper] = makeWrapper({ store, channelSetId: CHANNEL_SET.id });
});
it('should render a collection name input', () => {
@@ -196,67 +182,45 @@ describe('ChannelSetModal', () => {
});
it('should render a correct channels count', () => {
- expect(wrapper.find('.subheading').html()).toContain('2 channels');
+ expect(wrapper.findComponent('.subheading').html()).toContain('2 channels');
});
it("should render channels' names, descriptions and remove buttons", () => {
- const channelItems = wrapper.findAll({ name: 'ChannelItem' });
+ const channelItems = wrapper.findAllComponents({ name: 'ChannelItem' });
expect(channelItems.length).toBe(2);
expect(channelItems.at(0).html()).toContain('Channel 1');
expect(channelItems.at(0).html()).toContain('First channel description');
- expect(
- channelItems
- .at(0)
- .find('button')
- .text()
- ).toBe('Remove');
+ expect(channelItems.at(0).find('button').text()).toBe('Remove');
expect(channelItems.at(1).html()).toContain('Channel 2');
expect(channelItems.at(1).html()).toContain('Second channel description');
- expect(
- channelItems
- .at(1)
- .find('button')
- .text()
- ).toBe('Remove');
+ expect(channelItems.at(1).find('button').text()).toBe('Remove');
});
it('clicking select channels button should navigate to channels selection view', async () => {
- getSelectChannelsButton(wrapper).trigger('click');
+ await getSelectChannelsButton(wrapper).trigger('click');
await wrapper.vm.$nextTick();
- expect(wrapper.find('[data-test="collection-channels-view"]').isVisible()).toBe(false);
- expect(wrapper.find('[data-test="channels-selection-view"]').isVisible()).toBe(true);
+ expect(wrapper.findComponent('[data-test="collection-channels-view"]').isVisible()).toBe(
+ false,
+ );
+ expect(wrapper.findComponent('[data-test="channels-selection-view"]').isVisible()).toBe(true);
});
describe('clicking close button', () => {
- it('should redirect to channel sets page', () => {
- getCloseButton(wrapper).trigger('click');
+ it('should redirect to channel sets page', async () => {
+ await getCloseButton(wrapper).trigger('click');
expect(wrapper.vm.$route.name).toBe(RouteNames.CHANNEL_SETS);
});
- it('should delete a channel set if it is new', () => {
- const storeConfig = cloneDeep(STORE_CONFIG);
- const deleteChannelSet = jest.fn();
- storeConfig.modules.channelSet.actions.deleteChannelSet = deleteChannelSet;
- const store = storeFactory(storeConfig);
- store.commit('channelSet/ADD_CHANNELSET', NEW_CHANNEL_SET);
-
- wrapper = makeWrapper({ store, channelSetId: NEW_CHANNEL_SET.id });
-
- getCloseButton(wrapper).trigger('click');
- expect(deleteChannelSet).toHaveBeenCalledTimes(1);
- expect(deleteChannelSet.mock.calls[0][1]).toEqual(NEW_CHANNEL_SET);
- });
-
- it('should prompt user if there are unsaved changes', () => {
+ it('should prompt user if there are unsaved changes', async () => {
expect(getUnsavedDialog(wrapper).attributes('data-test-visible')).toBeFalsy();
- getCollectionNameInput(wrapper).setValue('My collection');
- getCloseButton(wrapper).trigger('click');
+ await getCollectionNameInput(wrapper).setValue('My collection');
+ await getCloseButton(wrapper).trigger('click');
expect(getUnsavedDialog(wrapper).attributes('data-test-visible')).toBeTruthy();
});
@@ -264,24 +228,24 @@ describe('ChannelSetModal', () => {
describe('clicking save button', () => {
it("shouldn't update a channel set when a collection name is missing", async () => {
- getCollectionNameInput(wrapper).setValue('');
- getSaveButton(wrapper).trigger('click');
+ await getCollectionNameInput(wrapper).setValue('');
+ await getSaveButton(wrapper).trigger('click');
await flushPromises();
expect(updateChannelSet).not.toHaveBeenCalled();
});
it("shouldn't update a channel set when a collection name is made of empty characters", async () => {
- getCollectionNameInput(wrapper).setValue(' ');
- getSaveButton(wrapper).trigger('click');
+ await getCollectionNameInput(wrapper).setValue(' ');
+ await getSaveButton(wrapper).trigger('click');
await flushPromises();
expect(updateChannelSet).not.toHaveBeenCalled();
});
it('should update a channel set when a collection name is valid', async () => {
- getCollectionNameInput(wrapper).setValue('My collection');
- getSaveButton(wrapper).trigger('click');
+ await getCollectionNameInput(wrapper).setValue('My collection');
+ await getSaveButton(wrapper).trigger('click');
await flushPromises();
expect(updateChannelSet).toHaveBeenCalledTimes(1);
@@ -303,26 +267,31 @@ describe('ChannelSetModal', () => {
store.commit('channelSet/ADD_CHANNELSET', CHANNEL_SET);
store.commit('channel/ADD_CHANNELS', [CHANNEL_1, CHANNEL_2]);
- wrapper = makeWrapper({ store, channelSetId: CHANNEL_SET.id });
+ [wrapper] = makeWrapper({ store, channelSetId: CHANNEL_SET.id });
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
- getSelectChannelsButton(wrapper).trigger('click');
+ await getSelectChannelsButton(wrapper).trigger('click');
await wrapper.vm.$nextTick();
- expect(wrapper.find('[data-test="channels-selection-view"]').isVisible()).toBe(true);
+ expect(wrapper.findComponent('[data-test="channels-selection-view"]').isVisible()).toBe(true);
});
- it('should render finish button', () => {
+ it('should render finish button', async () => {
+ expect(wrapper.vm.step).toBe(2);
expect(getFinishButton(wrapper).isVisible()).toBe(true);
});
it('clicking finish button should navigate back to collection channels view', async () => {
- getFinishButton(wrapper).trigger('click');
+ await getFinishButton(wrapper).trigger('click');
await wrapper.vm.$nextTick();
- expect(wrapper.find('[data-test="channels-selection-view"]').isVisible()).toBe(false);
- expect(wrapper.find('[data-test="collection-channels-view"]').isVisible()).toBe(true);
+ expect(wrapper.findComponent('[data-test="channels-selection-view"]').isVisible()).toBe(
+ false,
+ );
+ expect(wrapper.findComponent('[data-test="collection-channels-view"]').isVisible()).toBe(
+ true,
+ );
});
});
});
diff --git a/contentcuration/contentcuration/frontend/channelList/vuex/channelList/actions.js b/contentcuration/contentcuration/frontend/channelList/vuex/channelList/actions.js
index 93cf0405c4..a6ad8b7802 100644
--- a/contentcuration/contentcuration/frontend/channelList/vuex/channelList/actions.js
+++ b/contentcuration/contentcuration/frontend/channelList/vuex/channelList/actions.js
@@ -48,7 +48,7 @@ export function loadInvitationList(context) {
}).then(invitations => {
context.commit(
'SET_INVITATION_LIST',
- invitations.filter(i => !i.accepted && !i.declined && !i.revoked)
+ invitations.filter(i => !i.accepted && !i.declined && !i.revoked),
);
return invitations;
});
diff --git a/contentcuration/contentcuration/frontend/channelList/vuex/channelList/getters.js b/contentcuration/contentcuration/frontend/channelList/vuex/channelList/getters.js
index 93e35359f7..614dd8ebd2 100644
--- a/contentcuration/contentcuration/frontend/channelList/vuex/channelList/getters.js
+++ b/contentcuration/contentcuration/frontend/channelList/vuex/channelList/getters.js
@@ -3,13 +3,13 @@ export function invitations(state) {
}
export function getInvitation(state) {
- return function(invitationId) {
+ return function (invitationId) {
return state.invitationsMap[invitationId];
};
}
export function getChannelDetails(state) {
- return function(channelId) {
+ return function (channelId) {
return state.channelDetailsMap[channelId];
};
}
diff --git a/contentcuration/contentcuration/frontend/channelList/vuex/channelList/mutations.js b/contentcuration/contentcuration/frontend/channelList/vuex/channelList/mutations.js
index fa019b99be..f5e334d21f 100644
--- a/contentcuration/contentcuration/frontend/channelList/vuex/channelList/mutations.js
+++ b/contentcuration/contentcuration/frontend/channelList/vuex/channelList/mutations.js
@@ -10,7 +10,7 @@ export function SET_PAGE(
count = null,
total_pages = null,
results = [],
- } = {}
+ } = {},
) {
state.page.next = next;
state.page.previous = previous;
diff --git a/contentcuration/contentcuration/frontend/channelList/vuex/channelSet/actions.js b/contentcuration/contentcuration/frontend/channelList/vuex/channelSet/actions.js
index 7d35025152..0a9556f3f0 100644
--- a/contentcuration/contentcuration/frontend/channelList/vuex/channelSet/actions.js
+++ b/contentcuration/contentcuration/frontend/channelList/vuex/channelSet/actions.js
@@ -39,12 +39,16 @@ export function createChannelSet(context) {
export function commitChannelSet(
context,
- { id, name = NOVALUE, description = NOVALUE, channels = [] } = {}
+ { id, name = NOVALUE, description = NOVALUE, channels = [] } = {},
) {
const channelSetData = {};
+
if (!id) {
- throw ReferenceError('id must be defined to commit a channel');
+ channelSetData.isNew = true;
+ } else {
+ channelSetData.id = id;
}
+
if (name !== NOVALUE) {
channelSetData.name = name;
}
@@ -55,10 +59,21 @@ export function commitChannelSet(
for (const channel of channels) {
channelSetData.channels[channel] = true;
}
- return ChannelSet.createModel(channelSetData).then(data => {
- context.commit('SET_CHANNELSET_NOT_NEW', id);
- context.commit('UPDATE_CHANNELSET', data);
- });
+
+ return ChannelSet.createModel(channelSetData)
+ .then(data => {
+ if (!data || !data.id) {
+ throw ReferenceError('id must be defined to commit a channel');
+ }
+
+ context.commit('SET_CHANNELSET_NOT_NEW', data.id);
+ context.commit('UPDATE_CHANNELSET', data);
+
+ return data;
+ })
+ .catch(error => {
+ throw error;
+ });
}
export function updateChannelSet(context, { id, name = NOVALUE, description = NOVALUE } = {}) {
diff --git a/contentcuration/contentcuration/frontend/channelList/vuex/channelSet/getters.js b/contentcuration/contentcuration/frontend/channelList/vuex/channelSet/getters.js
index 15844d468a..a7c0c71348 100644
--- a/contentcuration/contentcuration/frontend/channelList/vuex/channelSet/getters.js
+++ b/contentcuration/contentcuration/frontend/channelList/vuex/channelSet/getters.js
@@ -3,7 +3,7 @@ export function channelSets(state) {
}
export function getChannelSet(state) {
- return function(channelSetId) {
+ return function (channelSetId) {
const channelSet = state.channelSetsMap[channelSetId];
if (channelSet) {
return {
@@ -16,7 +16,7 @@ export function getChannelSet(state) {
}
export function getChannelSetIsValid(state) {
- return function(channelSetId) {
+ return function (channelSetId) {
const set = state.channelSetsMap[channelSetId];
return set && set.name && set.name.length > 0;
};
diff --git a/contentcuration/contentcuration/frontend/channelList/vuex/channelSet/mutations.js b/contentcuration/contentcuration/frontend/channelList/vuex/channelSet/mutations.js
index bbca8457ad..d2371db718 100644
--- a/contentcuration/contentcuration/frontend/channelList/vuex/channelSet/mutations.js
+++ b/contentcuration/contentcuration/frontend/channelList/vuex/channelSet/mutations.js
@@ -1,4 +1,4 @@
-import Vue from 'vue';
+import Vue, { set } from 'vue';
import { NEW_OBJECT } from 'shared/constants';
import { applyMods } from 'shared/data/applyRemoteChanges';
@@ -34,12 +34,12 @@ export function UPDATE_CHANNELSET(state, { id, ...payload }) {
export function UPDATE_CHANNELSET_FROM_INDEXEDDB(state, { id, ...mods }) {
if (id && state.channelSetsMap[id]) {
- Vue.set(state.channelSetsMap, id, { ...applyMods(state.channelSetsMap[id], mods) });
+ set(state.channelSetsMap, id, { ...applyMods(state.channelSetsMap[id], mods) });
}
}
export function ADD_CHANNEL_TO_CHANNELSET(state, { channelSetId, channelId }) {
- Vue.set(state.channelSetsMap[channelSetId].channels, channelId, true);
+ set(state.channelSetsMap[channelSetId].channels, channelId, true);
}
export function REMOVE_CHANNEL_FROM_CHANNELSET(state, { channelSetId, channelId }) {
diff --git a/contentcuration/contentcuration/frontend/editorDev/index.js b/contentcuration/contentcuration/frontend/editorDev/index.js
new file mode 100644
index 0000000000..ef9a10c8db
--- /dev/null
+++ b/contentcuration/contentcuration/frontend/editorDev/index.js
@@ -0,0 +1,24 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import VueRouter from 'vue-router';
+import TipTapEditor from '../shared/views/TipTapEditor/TipTapEditor/TipTapEditor.vue';
+import startApp from 'shared/app';
+import storeFactory from 'shared/vuex/baseStore';
+
+Vue.use(VueRouter);
+Vue.use(Vuex);
+
+// Create a minimal store that has the required methods
+const store = storeFactory();
+
+startApp({
+ store, // Provide the store - this is required Althought not needed
+ router: new VueRouter({
+ routes: [
+ {
+ path: '/',
+ component: TipTapEditor,
+ },
+ ],
+ }),
+});
diff --git a/contentcuration/contentcuration/frontend/editorDev/router.js b/contentcuration/contentcuration/frontend/editorDev/router.js
new file mode 100644
index 0000000000..1bb8ee6838
--- /dev/null
+++ b/contentcuration/contentcuration/frontend/editorDev/router.js
@@ -0,0 +1,10 @@
+import Vue from 'vue';
+import Router from 'vue-router';
+import TipTapEditor from '../shared/views/TipTapEditor/TipTapEditor/TipTapEditor.vue';
+
+Vue.use(Router);
+export default new Router({
+ mode: 'history',
+ base: '/editor-dev/',
+ routes: [{ path: '/', component: TipTapEditor }],
+});
diff --git a/contentcuration/contentcuration/frontend/settings/pages/Account/ChangePasswordForm.vue b/contentcuration/contentcuration/frontend/settings/pages/Account/ChangePasswordForm.vue
index 8394fb4e2c..10269c1f79 100644
--- a/contentcuration/contentcuration/frontend/settings/pages/Account/ChangePasswordForm.vue
+++ b/contentcuration/contentcuration/frontend/settings/pages/Account/ChangePasswordForm.vue
@@ -5,46 +5,62 @@
:title="$tr('changePasswordHeader')"
:submitText="$tr('saveChangesAction')"
:cancelText="$tr('cancelAction')"
- @submit="submitPassword"
+ @submit="submit"
@cancel="dialog = false"
>
-
-
-
+
-
-
+
+
-
+
diff --git a/contentcuration/contentcuration/frontend/settings/pages/Account/DeleteAccountForm.vue b/contentcuration/contentcuration/frontend/settings/pages/Account/DeleteAccountForm.vue
index da2ecc9ec4..449c28ab16 100644
--- a/contentcuration/contentcuration/frontend/settings/pages/Account/DeleteAccountForm.vue
+++ b/contentcuration/contentcuration/frontend/settings/pages/Account/DeleteAccountForm.vue
@@ -20,25 +20,26 @@
@input="deletionEmailInvalidMessage = ''"
/>
-
+
+
+ {{ $tr('deletionFailedText') }}
+
+
+
\ No newline at end of file
+
diff --git a/contentcuration/contentcuration/frontend/settings/pages/SettingsIndex.vue b/contentcuration/contentcuration/frontend/settings/pages/SettingsIndex.vue
index 6bcc512626..add98d3e5b 100644
--- a/contentcuration/contentcuration/frontend/settings/pages/SettingsIndex.vue
+++ b/contentcuration/contentcuration/frontend/settings/pages/SettingsIndex.vue
@@ -17,14 +17,17 @@
-
+
-
+
@@ -43,12 +46,12 @@
import GlobalSnackbar from 'shared/views/GlobalSnackbar';
import AppBar from 'shared/views/AppBar';
import { routerMixin } from 'shared/mixins';
- import OfflineText from 'shared/views/OfflineText';
+ import StudioOfflineAlert from 'shared/views/StudioOfflineAlert';
import PolicyModals from 'shared/views/policies/PolicyModals';
export default {
name: 'SettingsIndex',
- components: { GlobalSnackbar, AppBar, OfflineText, PolicyModals },
+ components: { GlobalSnackbar, AppBar, StudioOfflineAlert, PolicyModals },
mixins: [routerMixin],
computed: {
...mapState({
diff --git a/contentcuration/contentcuration/frontend/settings/pages/Storage/RequestForm.vue b/contentcuration/contentcuration/frontend/settings/pages/Storage/RequestForm.vue
index aaed431916..1f8e44814f 100644
--- a/contentcuration/contentcuration/frontend/settings/pages/Storage/RequestForm.vue
+++ b/contentcuration/contentcuration/frontend/settings/pages/Storage/RequestForm.vue
@@ -1,7 +1,9 @@
- |