Skip to content

test: migrate RearrangeChannelsPage, AvailableChannelsPage, NewChannelVersionPage from VTU to VTL (Part 2-4/4) (#14261) - #15002

Open
soaebhasan12 wants to merge 13 commits into
learningequality:developfrom
soaebhasan12:migrate-device-tests-part2-4
Open

test: migrate RearrangeChannelsPage, AvailableChannelsPage, NewChannelVersionPage from VTU to VTL (Part 2-4/4) (#14261)#15002
soaebhasan12 wants to merge 13 commits into
learningequality:developfrom
soaebhasan12:migrate-device-tests-part2-4

Conversation

@soaebhasan12

Copy link
Copy Markdown
Contributor

Summary

Migrate remaining device plugin tests to Vue Testing Library as part of issue #14261.
This is Part 2-4 of 4 — consolidated into a single PR per reviewer guidance on PR #14883:

Each file has its own commit(s) for easier review.

Status

This is a draft/work-in-progress PR opened early to get feedback on an open question
(see comment below) before continuing with the remaining files.

Open question

I'm stuck on mocking DragContainer for RearrangeChannelsPage — see comment below for details.

AI usage

I used Claude AI as a learning tool and coding assistant during this VTL migration,
consistent with the approach disclosed in PR #14883.

@github-actions github-actions Bot added SIZE: medium APP: Device Re: Device App (content import/export, facility-syncing, user permissions, etc.) DEV: frontend labels Jul 13, 2026
@learning-equality-bot

Copy link
Copy Markdown

👋 Hi @soaebhasan12, thanks for contributing!

For the review process to begin, please verify that the following is satisfied:

  • Contribution is aligned with our contributing guidelines

  • Pull request description has correctly filled AI usage section & follows our AI guidance:

    AI guidance

    State explicitly whether you didn't use or used AI & how.

    If you used it, ensure that the PR is aligned with Using AI as well as our DEEP framework. DEEP asks you:

    • Disclose — Be open about when you've used AI for support.
    • Engage critically — Question what is generated. Review code for correctness and unnecessary complexity.
    • Edit — Review and refine AI output. Remove unnecessary code and verify it still works after your edits.
    • Process sharing — Explain how you used the AI so others can learn.

    Examples of good disclosures:

    "I used Claude Code to implement the component, prompting it to follow the pattern in ComponentX. I reviewed the generated code, removed unnecessary error handling, and verified the tests pass."

    "I brainstormed the approach with Gemini, then had it write failing tests for the feature. After reviewing the tests, I used Claude Code to generate the implementation. I refactored the output to reduce verbosity and ran the full test suite."

Also check that issue requirements are satisfied & you ran pre-commit locally.

Pull requests that don't follow the guidelines will be closed.

Reviewer assignment can take up to 2 weeks.

@soaebhasan12

Copy link
Copy Markdown
Contributor Author

hello @AlexVelezLl @rtibblesbot
I'm migrating RearrangeChannelsPage.spec.js to VTL. Since DragContainer uses
@shopify/draggable (which doesn't work in jsdom), I mocked it with a simple stub
component that just renders its slot plus a button I can click to trigger the
sort event manually.

jest.mock('kolibri-common/components/sortable/DragContainer', () => ({
  default: {
    name: 'DragContainer',
    props: ['items'],
    template: `<div><button data-testid="trigger-sort" @click="...">...</button><slot /></div>`,
  },
}));

The problem: when there are channels to show, nothing renders inside that mocked
component — not even the channel names — and there's no error or warning in the
console either. It just silently shows nothing. The empty-state case (no channels)
works fine though.

I'm guessing it might have something to do with Draggable/DragHandle (the children
inside DragContainer's slot) expecting something from the real DragContainer that
my mock doesn't provide, but I'm not sure.

Any suggestions on how to approach mocking this component correctly?

@AlexVelezLl AlexVelezLl self-assigned this Jul 13, 2026
@AlexVelezLl
AlexVelezLl self-requested a review July 13, 2026 18:01
@AlexVelezLl

Copy link
Copy Markdown
Member

Hey @soaebhasan12! Could you check why the current mock works, and what the difference is? Could you also rebase against the latest develop now that we have made some changes to the DragContainer implementation? Perhaps that could help!

@soaebhasan12

Copy link
Copy Markdown
Contributor Author

Hey @soaebhasan12! Could you check why the current mock works, and what the difference is? Could you also rebase against the latest develop now that we have made some changes to the DragContainer implementation? Perhaps that could help!

Thanks @AlexVelezLl! Rebased against develop and figured it out.

You were right — the develop branch now uses sortablejs instead of @shopify/draggable, and I found the pattern in DragContainer.spec.js (mocking sortablejs itself rather than the DragContainer component). Switched to that approach:

jest.mock(
  'sortablejs',
  () => jest.fn().mockImplementation((el, options) => ({ destroy: jest.fn(), options })),
  { virtual: true },
);

This lets the real DragContainer (and its Draggable/DragHandle children) render normally, and I can grab the captured onEnd callback from Sortable.mock.results to simulate a completed drag. All tests pass now. Thanks for pointing me in the right direction!

@soaebhasan12
soaebhasan12 force-pushed the migrate-device-tests-part2-4 branch from b7e02d7 to 7aa4a4c Compare July 21, 2026 02:05
@soaebhasan12

Copy link
Copy Markdown
Contributor Author

Update: RearrangeChannelsPage.spec.js is complete — all 7 tests passing
(loads data, instructions text, empty state, successful/failed @sort, moveUp, moveDown),
pre-commit clean.

Moving on to AvailableChannelsPage.spec.js next, which I understand is the more
complex one (router, store, filters). Will push that as a separate commit once ready.

@soaebhasan12
soaebhasan12 force-pushed the migrate-device-tests-part2-4 branch from 7aa4a4c to 2daa41e Compare August 2, 2026 14:46
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

@AlexVelezLl

AlexVelezLl commented Aug 2, 2026

Copy link
Copy Markdown
Member

Hi @soaebhasan12 ! Thanks for this! Just flagging that there are some failing JavaScript tests https://github.com/learningequality/kolibri/actions/runs/30752872282/job/91509919588?pr=15002

@soaebhasan12

Copy link
Copy Markdown
Contributor Author

Hi @AlexVelezLl Sir, I think this was meant for me (@soaebhasan12 ) — checking the failing tests now!

@soaebhasan12

Copy link
Copy Markdown
Contributor Author

Fixed the flaky Sortable tests — turned out to be a race condition where the mock's Sortable instance wasn't guaranteed to exist yet when the test asserted on it. Added an explicit waitFor before accessing it.

@soaebhasan12

Copy link
Copy Markdown
Contributor Author

Hi @AlexVelezLl! Dug into this further — turns out my earlier fix (adding a waitFor on Sortable.mock.results.length) was based on a wrong assumption. Locally it made the flaky test pass reliably, but the latest CI run shows Sortable.mock.results.length staying at 0 for the entire waitFor timeout — meaning Sortable is never being constructed at all in that run, not just constructed late. So this isn't a timing race after all.

I dug into the mock setup: sortablejs is a direct dependency of kolibri-common (where DragContainer.vue lives) but not of the kolibri package (where the device plugin and this spec file live) — the spec file mocks it with jest.mock('sortablejs', factory, { virtual: true }). I suspect this might be a Jest virtual-mock resolution edge case across the pnpm workspace boundary — possibly the mock registered from the device plugin's test file isn't being picked up by DragContainer.vue's import of sortablejs in certain CI conditions (worker/parallelism or fresh-install layout differences).

I wasn't able to reproduce this locally (isolated run or full local suite both pass). Would appreciate a pointer if you've seen this pattern before, or if there's a preferred way to mock cross-package dependencies like this in the Kolibri test setup.

@soaebhasan12

Copy link
Copy Markdown
Contributor Author

Quick update — confirmed the hypothesis further. I tried removing { virtual: true } from the mock and got Cannot find module 'sortablejs' locally, which confirms it genuinely can't be resolved from this spec file without it (since sortablejs isn't a dependency of the kolibri package).

I think the correct fix is a moduleNameMapper entry in jest.conf.js that maps sortablejs to its real resolved path (the one kolibri-common uses) for all test files, rather than each spec file creating its own separate virtual mock. That way every file gets the same module identity for sortablejs, regardless of which package does the importing.

That's a shared config change though, so I don't want to make it without your go-ahead — let me know if that's the right direction, or if there's an existing convention for this in Kolibri's test setup I should follow instead.

@akolson

akolson commented Aug 10, 2026

Copy link
Copy Markdown
Member

HI @AlexVelezLl! Whenever you have a moment! Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

APP: Device Re: Device App (content import/export, facility-syncing, user permissions, etc.) DEV: frontend SIZE: large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants