Skip to content

Migrate ExtraDemographics tests to Vue Testing Library - #15466

Merged
rtibbles merged 3 commits into
learningequality:developfrom
rishiraj38:fix/14270-extra-demographics-vtl
Oct 6, 2026
Merged

rtibbles merged 3 commits into
learningequality:developfrom
rishiraj38:fix/14270-extra-demographics-vtl

Conversation

@rishiraj38

@rishiraj38 rishiraj38 commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Migrates ExtraDemographicField.spec.js and ExtraDemographics.spec.js from @vue/test-utils to Vue Testing Library. The old tests read wrapper.vm.description, wrapper.vm.options and wrapper.vm.customSchema and fired select by hand on the child component. The new ones render the real KSelect, open it, and assert on the labels, options and emitted values a user produces.

Only the two spec files change.

Acceptance criteria from #14270:

  1. Both files migrated to use @testing-library/vue: both specs use render and screen.
  2. No residual @vue/test-utils imports: none left in either file.
  3. Cover all major workflows while avoiding excessive testing of details: the field's label and options (default and translated), the selected option, picking an option, and the parent's emitted answers with and without saved answers. Six tests in total.
  4. Test descriptions from a user point of view: see the parity list under Reviewer guidance.
  5. Testing Library principles and query priority: options are queried with getAllByRole('listitem'). KSelect gives its control no role or accessible name and its label is a plain div, and its selected option is marked only by a class. The description tests tab to the control, find the description inside it and click it to open this select's options. The selected option test finds the displayed label outside the option list, then confirms the preselected option with Enter and checks the emitted value.
  6. Patterns consistent with existing VTL suites: renderComponent and emitted() as in CoreMenuOption.spec.js, selectKSelectOption from testUtils as in the coach LessonsRootPage, ExamsRootPage and LearnersRootPage specs, userEvent for the direct clicks.
  7. pnpm run test-jest -- --testPathPattern ExtraDemographics passes: 2 suites, 6 tests.

References

Fixes #14270

Reviewer guidance

Commands and results:

  • pnpm run test-jest -- --testPathPattern ExtraDemographics: 2 suites, 6 tests pass.
  • pnpm test-jest packages/kolibri-common: 63 suites, 1115 tests pass.
  • prek run --from-ref origin/develop --to-ref HEAD: all hooks pass, nothing rewritten.

Old to new, ExtraDemographicField.spec.js (5 tests to 4):

Old test New test
renders the label correctly shows the description and the default option labels of a field without translations. Tabs to the select, asserts the description is visible inside the focused control and clicks it to open the list.
generates the correct options Same test: asserts the option labels in order. Option values are not visible in the DOM, so the value mapping is covered by emits the value of the option the user picks.
renders the translated label correctly shows the description and the option labels translated into the current language. Same steps with the translated description.
generates the correct translated options Same test: asserts the translated option labels in order.
emits the select event when the value changes emits the value of the option the user picks, with a real click on the option instead of $emit on the child.
(not covered before) shows the selected option as the value and keeps it selected when the list opens

Old to new, ExtraDemographics.spec.js (3 tests to 2):

Old test New test
renders ExtraDemographicField for each field in customSchema Covered by the two tests below: one picks an option in the first field, the other in the second. There is no separate presence test because both would already fail if a field were missing.
emits input event with updated value when ExtraDemographicField emits select emits the picked answer when no answers are saved yet, same value: null input, with a real click.
computes customSchema correctly Removed. It compared wrapper.vm.customSchema to the prop it is read from. Its visible effect, one select per entry in demographic_fields, is covered by the two tests.
(not covered before) keeps the saved answers when another field is answered

What each test pins. I broke the components one line at a time (18 changes) and ran both specs after each one. Every change failed at least one test. The changes that fail only one test:

  • shows the description and the option labels translated into the current language: dropping getTranslatedString(this.field) from description, or getTranslatedString(option) from options.
  • shows the selected option as the value and keeps it selected when the list opens: removing :value="optionValue", or the find in optionValue.
  • emits the picked answer when no answers are saved yet: removing the (value || {}) guard in the index.vue template.
  • keeps the saved answers when another field is answered: removing ...this.value from setInput, or rendering only the first field.

The other two tests share their lines with the parent spec, which renders the same field component. Within ExtraDemographicField.spec.js, shows the description and the default option labels of a field without translations fails when the || this.field.description or || option.defaultLabel fallback is removed, and emits the value of the option the user picks fails when @select="emitSelectEvent" is removed or emitSelectEvent emits the option object instead of its value.

Picking an option goes through selectKSelectOption, which uses fireEvent and KSelect's class names internally. I used it because it is the shared helper for this; the specs themselves have no class selectors.

Not checked:

  • Nothing was run in a browser. No production code or UI files changed.
  • The disabled prop is a plain pass-through to KSelect in both components. The old specs did not cover it and I left it that way.
  • I ran the packages/kolibri-common suite, not the whole repo's Jest suite.

The two added tests go slightly beyond a straight port. I added them because they are the two pieces of logic the old suite skipped (optionValue and the merge in setInput). I can drop them if you prefer a strict one-to-one migration.

AI usage

I used Claude Code to migrate both specs, prompting it to follow CoreMenuOption.spec.js, the selectKSelectOption helper the coach specs use, and the review feedback on the earlier migrations in #14563, #14656 and #14883. I had it break each component one line at a time to confirm a test fails for every change, and run the ExtraDemographics specs, the kolibri-common suite and prek. I reviewed the new tests against the old ones for parity, which is the list above: the four label and option tests became two, the two parent tests that counted child components or read customSchema were removed, and two tests were added. I also used it to check what KSelect renders in each state and to tighten the description and selected-option assertions after review. I reviewed those changes and ran the specs, the kolibri-common suite and prek.

Rewrite both specs to drive the real KSelect the way a user does instead of
reading component internals. Add cases for the selected option label and
for keeping saved answers when another field changes.
@rishiraj38
rishiraj38 requested review from a team and marcellamaki October 2, 2026 11:44
@learning-equality-bot

Copy link
Copy Markdown
Contributor

👋 Hi @rishiraj38, 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."

📢✨ Before we assign a reviewer:

  • @rtibblesbot will pre-review this pull request. Its comments are generated by an LLM, and should be evaluated accordingly.
  • We'll also invite community pre-review. See the community review guidance for both authors and reviewers.

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.

@rtibblesbot

rtibblesbot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

🟡 Waiting for changes

Last updated: 2026-10-05 04:10 UTC

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #15466 is a clean, in-scope migration. Both specs now drive the real KSelect. They assert on labels and emitted values, with no wrapper.vm reads. I didn't run Jest locally; the worktree has no node_modules.

  • CI: Build WHL failed at "Build Kolibri" (exit 2). WHL smoke tests failed only because it depends on that job. This diff touches only two spec files, so it can't cause the failure. I couldn't read the logs from the sandbox. Please re-run the job. If it fails again, check develop.
  • One nitpick inline.

@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Ran a core review pass only:

  • Classified the diff and reviewed the whole PR for correctness, design, testing, and completeness
  • Specialized frontend/backend lenses and manual QA run when a review is explicitly requested by a maintainer
  • Checked CI status and chose the verdict from the findings, CI status, and available evidence

@rtibbles rtibbles self-assigned this Oct 2, 2026
The two description tests only clicked the description text. They now
tab to the select and assert the description is visible inside the
focused control, then open it.
@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

npm Package Versions

Merging this PR will publish the following packages to npm:

Package Current New
eslint-plugin-kolibri 1.2.0 1.3.0
kolibri-format 2.4.0 3.0.0

Warning

The following packages have changed files but no version bump:

Package Version Changed files
kolibri-i18n 1.0.0 1
kolibri 0.18.0 6

If these changes affect published code, consider bumping the version.

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #15466: no new findings. The 1 prior finding is resolved. CI is still pending.

Prior-finding status

RESOLVED — packages/kolibri-common/components/ExtraDemographics/tests/ExtraDemographicField.spec.js:56 — test never asserts the description is shown


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Ran an automatic code-only delta review triggered by new commits on a previously reviewed PR:

  • Retrieved prior bot reviews via the GitHub API
  • Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
  • Only raised NEW findings for newly introduced code
  • Core review pass only — specialized frontend/backend lenses and manual QA run when a review is explicitly requested
  • Synthesized one review from the passes and chose the verdict from the findings, CI status, and QA evidence

@rtibbles rtibbles left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible for some of these assertions to be more specific, I think.

renderComponent({ field: translatedField });

const select = await tabToSelect();
expect(within(select).getByText(TRANSLATED_DESCRIPTION)).toBeVisible();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getByText along with toBeVisible, or toBeInDocument are very weak assertions, and lots could be changed about the component while still passing these tests. See here for more details: https://testing-library.com/docs/queries/about/#priority

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KSelect's control has no role or accessible name, so only getByText matches here. The test now finds the description inside the focused control and clicks it to open the list, then asserts the options in order. Do you want a KDS follow-up that adds a combobox role and a label association?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went through the priority list. getByRole, getByLabelText and getByPlaceholderText return nothing for KSelect, so getByText is the highest query available here. If you prefer, I'll add a data-testid on the select and assert toHaveTextContent on it, as in #14376.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working through the query priorities. No KDS follow-up needed from you: I've filed learningequality/kolibri-design-system#1348 to make KSelect a combobox with a labelled listbox. A data-testid would land on KSelect's root, which also contains the option labels, so it wouldn't be any stronger. Keep the current test for now; once #1348 lands this can become getByRole('combobox', { name: description }).

it('shows the label of the selected option', () => {
renderComponent({ field, value: SIX_TO_TEN.value });

expect(screen.getByText(SIX_TO_TEN.defaultLabel)).toBeInTheDocument();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now reads the value from the focused control, outside the hidden option list, then opens with Enter and confirms with Enter, which has to emit the preselected value. The selected item carries only a class, so there is no aria-selected to query.

The description tests now open the select by clicking the description
inside the focused control, so the text has to be the select's label.
The selected option test finds the displayed value outside the hidden
option list, then opens the list and confirms with Enter, which has to
emit the preselected option.

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #15466: the one prior finding is resolved; no new findings.

Prior-finding status

RESOLVED — packages/kolibri-common/components/ExtraDemographics/tests/ExtraDemographicField.spec.js:65 — description test never asserts the description is shown


Written by rtibblesbot, an LLM-based coding agent.

@rtibbles rtibbles left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for sticking with this one, @rishiraj38. The tests now check real behaviour, and digging into KSelect's markup turned up the gap we're fixing in learningequality/kolibri-design-system#1348.

@rtibbles
rtibbles merged commit 19bae3c into learningequality:develop Oct 6, 2026
110 checks passed
@rishiraj38

Copy link
Copy Markdown
Contributor Author

Thanks @rtibbles for the reviews.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate ExtraDemographics tests to Vue Testing Library

3 participants