From 51acb1e31a4f0d920c3da48461ee65907adf9b8a Mon Sep 17 00:00:00 2001 From: abhiraj75 Date: Mon, 26 Jan 2026 23:32:08 +0530 Subject: [PATCH 1/3] [Remove Vuetify from Studio] Convert collection channels selection unit tests to Vue Testing Library --- .../__tests__/channelSelectionList.spec.js | 194 +++++++++++------- 1 file changed, 125 insertions(+), 69 deletions(-) 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 a7befd7c43..268314f8e7 100644 --- a/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSelectionList.spec.js +++ b/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSelectionList.spec.js @@ -1,9 +1,15 @@ -import { mount } from '@vue/test-utils'; -import { Store } from 'vuex'; -import ChannelSelectionList from '../ChannelSelectionList'; +import { render, screen, within, configure } from '@testing-library/vue'; +import userEvent from '@testing-library/user-event'; +import VueRouter from 'vue-router'; +import Vuex from 'vuex'; import { ChannelListTypes } from 'shared/constants'; +import ChannelSelectionList from '../ChannelSelectionList'; + +// Configure VTL to use 'data-test' instead of the default 'data-testid' +configure({ testIdAttribute: 'data-test' }); const searchWord = 'search test'; + const editChannel = { id: 'editchannel', name: searchWord, @@ -14,7 +20,7 @@ const editChannel = { const editChannel2 = { id: 'editchannel2', - name: '', + name: 'Another Channel', description: '', edit: true, published: true, @@ -22,100 +28,150 @@ const editChannel2 = { const publicChannel = { id: 'publicchannel', + name: 'Public Channel', public: true, published: true, }; -const getters = { - channels: jest.fn(() => [editChannel, editChannel2, publicChannel]), - getChannel: jest.fn(() => () => editChannel), -}; - -const actions = { +const mockActions = { loadChannelList: jest.fn(() => Promise.resolve()), }; -const store = new Store({ - modules: { - channel: { - namespaced: true, - getters, - actions, +const makeStore = () => + new Vuex.Store({ + modules: { + channel: { + namespaced: true, + state: {}, + getters: { + channels: () => [editChannel, editChannel2, publicChannel], + getChannel: () => id => [editChannel, editChannel2, publicChannel].find(c => c.id === id), + }, + actions: mockActions, + mutations: { + ADD_CHANNELS() {}, + }, + }, }, - }, -}); + }); -function makeWrapper() { - const loadChannelList = jest.spyOn(ChannelSelectionList.methods, 'loadChannelList'); - loadChannelList.mockImplementation(() => Promise.resolve()); +const renderComponent = (props = {}) => { + const router = new VueRouter({ + routes: [{ path: '/', name: 'Home' }], + }); - const wrapper = mount(ChannelSelectionList, { - propsData: { + return render(ChannelSelectionList, { + router, + store: makeStore(), + props: { listType: ChannelListTypes.EDITABLE, + value: [], // Default to empty + ...props, // Allow overriding props for specific tests }, - computed: { - channels() { - return [editChannel, editChannel2, publicChannel]; - }, - }, - store, }); +}; - return [wrapper, { loadChannelList }]; -} - -describe('channelSelectionList', () => { - let wrapper, mocks; - +describe('ChannelSelectionList', () => { beforeEach(() => { - [wrapper, mocks] = makeWrapper(); + jest.clearAllMocks(); }); - afterEach(() => { - mocks.loadChannelList.mockRestore(); - }); + it('renders a list of editable channels and hides non-editable ones', async () => { + await renderComponent(); - 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(); + // Specific wait avoids wrapping the whole block in waitFor + expect(await screen.findByLabelText(/search for a channel/i)).toBeInTheDocument(); + + expect(screen.getByText(editChannel.name)).toBeInTheDocument(); + expect(screen.getByText(editChannel2.name)).toBeInTheDocument(); + expect(screen.queryByText(publicChannel.name)).not.toBeInTheDocument(); }); - 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'); + it('filters the channel list when the user types in the search box', async () => { + const user = userEvent.setup(); + await renderComponent(); + + // Wait for data load + expect(await screen.findByText(editChannel.name)).toBeInTheDocument(); + expect(screen.getByText(editChannel2.name)).toBeInTheDocument(); - expect(wrapper.emitted('input')[0][0]).toEqual([editChannel.id]); + const searchInput = screen.getByLabelText(/search for a channel/i); + await user.clear(searchInput); + await user.type(searchInput, editChannel.name); + + // Verify filter happened + expect(await screen.findByText(editChannel.name)).toBeInTheDocument(); + expect(screen.queryByText(editChannel2.name)).not.toBeInTheDocument(); }); - 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 + it('selects a channel when the user clicks the checkbox', async () => { + const user = userEvent.setup(); + const { emitted } = await renderComponent(); + + await screen.findByText(editChannel.name); - 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 + // Using getByTestId because the component doesn't expose unique accessible roles for individual channel checkboxes + const checkboxRow = screen.getByTestId(`checkbox-${editChannel.id}`); + + // Find the checkbox strictly within this row + const checkbox = within(checkboxRow).getByRole('checkbox'); + + await user.click(checkbox); + + expect(emitted()).toHaveProperty('input'); + expect(emitted().input).toHaveLength(1); + expect(emitted().input[0][0]).toEqual([editChannel.id]); }); - 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('deselects a channel when the user clicks the checkbox of an already selected channel', async () => { + const user = userEvent.setup(); + + // Initialize with the channel already selected + const { emitted } = await renderComponent({ value: [editChannel.id] }); + + await screen.findByText(editChannel.name); + + // Using getByTestId because the component doesn't expose unique accessible roles for individual channel checkboxes + const checkboxRow = screen.getByTestId(`checkbox-${editChannel.id}`); + const checkbox = within(checkboxRow).getByRole('checkbox'); + + // Click the checkbox to deselect + await user.click(checkbox); + + expect(emitted()).toHaveProperty('input'); + expect(emitted().input).toHaveLength(1); + expect(emitted().input[0][0]).toEqual([]); }); - 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('selects a channel when the user clicks the channel card', async () => { + const user = userEvent.setup(); + const { emitted } = await renderComponent(); + + await screen.findByText(editChannel.name); + + // Using getByTestId because the component doesn't expose accessible roles for channel cards + const card = screen.getByTestId(`channel-item-${editChannel.id}`); + await user.click(card); + + expect(emitted()).toHaveProperty('input'); + expect(emitted().input).toHaveLength(1); + expect(emitted().input[0][0]).toEqual([editChannel.id]); }); - 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 + it('deselects a channel when the user clicks a selected channel card', async () => { + const user = userEvent.setup(); + + // Initialize with the channel already selected + const { emitted } = await renderComponent({ value: [editChannel.id] }); + + await screen.findByText(editChannel.name); + + // Using getByTestId because the component doesn't expose accessible roles for channel cards + const card = screen.getByTestId(`channel-item-${editChannel.id}`); + await user.click(card); - 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 + expect(emitted()).toHaveProperty('input'); + expect(emitted().input).toHaveLength(1); + expect(emitted().input[0][0]).toEqual([]); }); -}); +}); \ No newline at end of file From 7f18323b5ab7207333ece8b7b429f682d83212e5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:14:23 +0000 Subject: [PATCH 2/3] [pre-commit.ci lite] apply automatic fixes --- .../__tests__/channelSelectionList.spec.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 268314f8e7..65b4652310 100644 --- a/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSelectionList.spec.js +++ b/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSelectionList.spec.js @@ -2,8 +2,8 @@ import { render, screen, within, configure } from '@testing-library/vue'; import userEvent from '@testing-library/user-event'; import VueRouter from 'vue-router'; import Vuex from 'vuex'; -import { ChannelListTypes } from 'shared/constants'; import ChannelSelectionList from '../ChannelSelectionList'; +import { ChannelListTypes } from 'shared/constants'; // Configure VTL to use 'data-test' instead of the default 'data-testid' configure({ testIdAttribute: 'data-test' }); @@ -66,7 +66,7 @@ const renderComponent = (props = {}) => { props: { listType: ChannelListTypes.EDITABLE, value: [], // Default to empty - ...props, // Allow overriding props for specific tests + ...props, // Allow overriding props for specific tests }, }); }; @@ -81,7 +81,7 @@ describe('ChannelSelectionList', () => { // Specific wait avoids wrapping the whole block in waitFor expect(await screen.findByLabelText(/search for a channel/i)).toBeInTheDocument(); - + expect(screen.getByText(editChannel.name)).toBeInTheDocument(); expect(screen.getByText(editChannel2.name)).toBeInTheDocument(); expect(screen.queryByText(publicChannel.name)).not.toBeInTheDocument(); @@ -112,10 +112,10 @@ describe('ChannelSelectionList', () => { // Using getByTestId because the component doesn't expose unique accessible roles for individual channel checkboxes const checkboxRow = screen.getByTestId(`checkbox-${editChannel.id}`); - + // Find the checkbox strictly within this row const checkbox = within(checkboxRow).getByRole('checkbox'); - + await user.click(checkbox); expect(emitted()).toHaveProperty('input'); @@ -125,7 +125,7 @@ describe('ChannelSelectionList', () => { it('deselects a channel when the user clicks the checkbox of an already selected channel', async () => { const user = userEvent.setup(); - + // Initialize with the channel already selected const { emitted } = await renderComponent({ value: [editChannel.id] }); @@ -137,10 +137,10 @@ describe('ChannelSelectionList', () => { // Click the checkbox to deselect await user.click(checkbox); - + expect(emitted()).toHaveProperty('input'); expect(emitted().input).toHaveLength(1); - expect(emitted().input[0][0]).toEqual([]); + expect(emitted().input[0][0]).toEqual([]); }); it('selects a channel when the user clicks the channel card', async () => { @@ -160,7 +160,7 @@ describe('ChannelSelectionList', () => { it('deselects a channel when the user clicks a selected channel card', async () => { const user = userEvent.setup(); - + // Initialize with the channel already selected const { emitted } = await renderComponent({ value: [editChannel.id] }); @@ -174,4 +174,4 @@ describe('ChannelSelectionList', () => { expect(emitted().input).toHaveLength(1); expect(emitted().input[0][0]).toEqual([]); }); -}); \ No newline at end of file +}); From c75da8feb19b26b51bbffe7340df797f9376d799 Mon Sep 17 00:00:00 2001 From: abhiraj75 Date: Tue, 27 Jan 2026 00:06:20 +0530 Subject: [PATCH 3/3] fixed linting --- .../__tests__/channelSelectionList.spec.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) 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 65b4652310..2b98593879 100644 --- a/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSelectionList.spec.js +++ b/contentcuration/contentcuration/frontend/channelList/views/ChannelSet/__tests__/channelSelectionList.spec.js @@ -1,7 +1,7 @@ import { render, screen, within, configure } from '@testing-library/vue'; import userEvent from '@testing-library/user-event'; import VueRouter from 'vue-router'; -import Vuex from 'vuex'; +import { Store } from 'vuex'; import ChannelSelectionList from '../ChannelSelectionList'; import { ChannelListTypes } from 'shared/constants'; @@ -38,7 +38,7 @@ const mockActions = { }; const makeStore = () => - new Vuex.Store({ + new Store({ modules: { channel: { namespaced: true, @@ -110,7 +110,8 @@ describe('ChannelSelectionList', () => { await screen.findByText(editChannel.name); - // Using getByTestId because the component doesn't expose unique accessible roles for individual channel checkboxes + // Using getByTestId because the component doesn't expose unique + // accessible roles for individual channel checkboxes const checkboxRow = screen.getByTestId(`checkbox-${editChannel.id}`); // Find the checkbox strictly within this row @@ -131,7 +132,8 @@ describe('ChannelSelectionList', () => { await screen.findByText(editChannel.name); - // Using getByTestId because the component doesn't expose unique accessible roles for individual channel checkboxes + // Using getByTestId because the component doesn't expose unique + // accessible roles for individual channel checkboxes const checkboxRow = screen.getByTestId(`checkbox-${editChannel.id}`); const checkbox = within(checkboxRow).getByRole('checkbox'); @@ -149,7 +151,8 @@ describe('ChannelSelectionList', () => { await screen.findByText(editChannel.name); - // Using getByTestId because the component doesn't expose accessible roles for channel cards + // Using getByTestId because the component doesn't expose accessible + // roles for channel cards const card = screen.getByTestId(`channel-item-${editChannel.id}`); await user.click(card); @@ -166,7 +169,8 @@ describe('ChannelSelectionList', () => { await screen.findByText(editChannel.name); - // Using getByTestId because the component doesn't expose accessible roles for channel cards + // Using getByTestId because the component doesn't expose accessible + // roles for channel cards const card = screen.getByTestId(`channel-item-${editChannel.id}`); await user.click(card);