Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RUN-2321-Feat/unit test for activity running indicator #9102

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

jayas006
Copy link
Contributor

@jayas006 jayas006 commented May 3, 2024

Is this a bugfix, or an enhancement? Please describe.
This is an enhancement

Describe the solution you've implemented

  • Verifying that the component renders correctly when the count is 0.
  • Checking that the component renders and displays the correct count when the count is greater than 0.
  • Ensuring that the count updates correctly when the updateNowrunning method is called.
  • Confirming that the component emits the activity-nowrunning-click-action event when clicked.
  • Checking that the component registers the activity-nowrunning-count event on mount.
    -Ensuring that the component unregisters the activity-nowrunning-count event on unmount.

Describe alternatives you've considered

Additional context
These unit tests were created to improve the reliability and maintainability of the ActivityRunningIndicator.vue component by automating the verification of its key behaviors. This helps ensure that the component works as expected and reduces the risk of introducing bugs when making changes to the component in the future.

@jayas006 jayas006 changed the title Feat/unit test for activity running indicator RUN-2321-Feat/unit test for activity running indicator May 13, 2024
@jayas006 jayas006 marked this pull request as ready for review May 15, 2024 00:22
Copy link
Contributor

@smartinellibenedetti smartinellibenedetti left a comment

Choose a reason for hiding this comment

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

Good job selecting what to test, there's some cleanup to do, but overall looking good. Please let me know if there are any doubts.

props: ["eventBus", "displayMode"],
props: {
eventBus: {
type: Object,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
type: Object,
type: Object as PropType<typeof EventBus>,

@@ -44,6 +55,7 @@
</li>
</template>
</dropdown>
<span v-else data-test-id="no-filters-message"> No filters available </span>
Copy link
Contributor

Choose a reason for hiding this comment

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

I would recommend removing this line, as it is confusing to read "no filters available" when it's still possible to filter (it's just that there are no saved filters), besides the fact that when writing tests, components shouldn't have their behavior altered.

@@ -1,14 +1,17 @@
<template>
<span>
<btn
v-if="hasQuery && (!query || !query.ftilerName)"
v-if="hasQuery && (!query || !query.filterName)"
Copy link
Contributor

Choose a reason for hiding this comment

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

good catch 👍

required: true,
},
eventBus: {
type: Object,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
type: Object,
type: Object as PropType<typeof EventBus>,

Comment on lines +69 to +72
hasQuery: {
type: Boolean,
required: true,
},
Copy link
Contributor

Choose a reason for hiding this comment

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

question, are all places using this component passing the prop hasQuery?

Comment on lines 125 to 144
if (initialCount > 0) {
console.log("Before delete:", wrapper.vm.filters);
// Mock the deleteFilter method
wrapper.vm.deleteFilter = (filterName) => {
const index = wrapper.vm.filters.findIndex(
(filter) => filter.filterName === filterName,
);
if (index !== -1) {
wrapper.vm.filters.splice(index, 1);
}
};
await wrapper.vm.deleteFilter(wrapper.vm.filters[0].filterName);
await wrapper.vm.$nextTick();
console.log("After delete:", wrapper.vm.filters);

expect(wrapper.vm.filters.length).toBeLessThan(initialCount);
} else {
throw new Error("No filters available to delete");
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

A few things here:

  • It's understandable if using if/else helps during the development of tests, but please avoid keeping them once tests are finished. Unless components have random functionalities, unit tests should be deterministic, which means that they use the same data every time and will always output the same results, therefore, there's no point in using if/else on them.

  • instead of triggering wrapper.vm.deleteFilter, trigger the user interaction that will call the method deleteFilter;

  • Instead of mocking the delete filter, mock the $confirm to return a resolved promise and either adjust filterStore.removeFilter to return the correct array of filters, OR before the click to trigger deleteFilter, do a mockReturnValue for filterStore.loadForProject to return an object with the filter property;

  • lastly, instead of checking if filters.length is less than initialCount, it would be better to count the number of links rendered, from this block:

<li v-for="filter in filters" :key="filter.filterName">
          <a role="button" @click="selectFilter(filter)">
            {{ filter.filterName }}
            <span v-if="query && filter.filterName === query.filterName"
              >√</span
            >
          </a>
        </li>

To count them, it's possible to add a "data-test" attribute to the tag, like this example here and then do the same logic of an initial count before removal, and a comparison of the numbers afterward.

Comment on lines 146 to 154
it("shows 'No filters available' when filters array is empty", async () => {
wrapper.vm.filters = [];
await wrapper.vm.$nextTick();

const noFiltersMessage = wrapper
.find('[data-test-id="no-filters-message"]')
.text();
expect(noFiltersMessage).toContain("No filters available");
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Would recommend removing this test.

});
describe("Filter Management", () => {
it("updates filters after successful deletion", async () => {
await wrapper.vm.loadFilters();
Copy link
Contributor

Choose a reason for hiding this comment

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

There's no need to call loadFilters here.

});

it("shows 'No filters available' when filters array is empty", async () => {
wrapper.vm.filters = [];
Copy link
Contributor

Choose a reason for hiding this comment

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

Recommend to recreate the wrapper with the correct data instead of mutating it like this.

expect(noFiltersMessage).toContain("No filters available");
});
it("updates visible elements when 'query.filterName' changes", async () => {
await wrapper.setProps({ query: { filterName: "Updated Filter" } });
Copy link
Contributor

Choose a reason for hiding this comment

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

Recommend to recreate the wrapper with the correct data instead of mutating it like this.

@jayas006
Copy link
Contributor Author

@smartinellibenedetti , Thank you for the feedback. I will go ahead and fix it.

Copy link
Contributor

@smartinellibenedetti smartinellibenedetti left a comment

Choose a reason for hiding this comment

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

Left pointers for the mocks, but overall it's the right direction!

'[data-test-id="filter-item"]',
).length;
// Mock the $confirm to return a resolved promise
wrapper.vm.$confirm = () => Promise.resolve();
Copy link
Contributor

Choose a reason for hiding this comment

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

$confirm should be mocked the same way $t is mocked, which is inside the mount method (in this case the mountSavedFilters) via the global.mocks property.

Comment on lines 165 to 174
ActivityFilterStore.prototype.removeFilter = jest
.fn()
.mockImplementation((filterName) => {
const index = wrapper.vm.filters.findIndex(
(filter) => filter.filterName === filterName
);
if (index !== -1) {
wrapper.vm.filters.splice(index, 1);
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Might be needed to tweak this mock as well, to either match the format of the editProjectFileService in EditProjectFile.spec.ts OR follow one of the approaches described here: https://www.mikeborozdin.com/post/changing-jest-mocks-between-tests

Copy link
Contributor

@smartinellibenedetti smartinellibenedetti left a comment

Choose a reason for hiding this comment

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

Please double-check if using MessageBox.confirm is the same behavior as $confirm. If not, I recommend undoing the change. Other than that, this PR just need small changes.

<a role="button" @click="deleteFilter">
<a
role="button"
data-test-id="delete-filter-btn"
Copy link
Contributor

Choose a reason for hiding this comment

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

if the element is rendered only once, can use data-test-id, if it's rendered multiple times, opt for data-test. Here you can remove the data-test attribute and leave just the data-test-id

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks Sarah. Good to know when to use data-test-id and data-test

<li
v-for="filter in filters"
:key="filter.filterName"
data-test-id="filter-item"
Copy link
Contributor

Choose a reason for hiding this comment

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

here should be data-test instead of data-test-id


it("emits 'activity-nowrunning-click-action' event when clicked", async () => {
const wrapper = await mountActivityRunningIndicator();
wrapper.vm.updateNowrunning(5);
Copy link
Contributor

Choose a reason for hiding this comment

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

Need to follow the same logic that was done in "renders correctly when count is greater than 0": create the eventBus and trigger an emit from it instead of calling wrapper.vm.updateNowrunning(5);

Comment on lines 43 to 48
it("updates count correctly", async () => {
const wrapper = await mountActivityRunningIndicator();
wrapper.vm.updateNowrunning(10);
await wrapper.vm.$nextTick();
expect(wrapper.vm.count).toBe(10);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

you can combine this test and the next into a single test - checking that it emits the right event and updates the count correctly

Comment on lines 134 to 136
.catch(() => {
//this.$notify("Delete canceled.");
// this.$notify("Delete canceled.");
});
Copy link
Contributor

Choose a reason for hiding this comment

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

was this commented before?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes it was commented from the starting. I removed it and then added it.

Comment on lines 98 to 127
this.$confirm({

MessageBox.confirm({
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this change really equivalent?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this change really equivalent?

thanks for checking, I probably used it when working on tests but forgot to undo it.

).length;
expect(initialCount).toBeGreaterThan(0);
const deleteButton = wrapper.find('[data-test-id="delete-filter-btn"]');
if (deleteButton.exists()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

please remove this if, as explained on another PR.

await wrapper.vm.doDeleteFilter(wrapper.vm.query.filterName);
await wrapper.vm.$nextTick();
const finalCount = wrapper.findAll('[data-test-id="filter-item"]').length;
expect(finalCount).toBeLessThan(initialCount);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest to change toBeLessThan to toBe(initialCount-1) so that the intention is more clear

global: {
mocks: {
$t: (msg) => msg,
$confirm: () => Promise.resolve(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you replace the $confirm for the modal, you don't need to mock it here anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants