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-2255 add unit tests for RundeckInfo component #9048
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a good start, left comments and pointers. Also, all the properties "versionProps", "serverProps", "VersionDisplay", "latestProps" can be moved to the mount method to make the tests cleaner.
...ails-spa/packages/ui-trellis/src/library/components/widgets/rundeck-info/RundeckInfo.spec.ts
Outdated
Show resolved
Hide resolved
...ails-spa/packages/ui-trellis/src/library/components/widgets/rundeck-info/RundeckInfo.spec.ts
Outdated
Show resolved
Hide resolved
...ails-spa/packages/ui-trellis/src/library/components/widgets/rundeck-info/RundeckInfo.spec.ts
Outdated
Show resolved
Hide resolved
}; | ||
const wrapper = await mountRundeckInfo({ version: versionProps }); | ||
const versionComponent = wrapper.findComponent({ name: "RundeckVersion" }); | ||
expect(versionComponent.exists()).toBe(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 things:
-
the text you write in the "it" is meant to describe what the test does. In this case "renders information correctly". To check this, it's not enough to only assert if that component exists, especially since this component will actually generate the text it will display by combining the properties from the versionProps.
-
secondly, you should check for the final dom elements, in this case, find the span inside versionComponent and check that the text generated is what it should be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback. I am going to look at it
expect(serverComponent.props()).toEqual({ | ||
name: serverProps.name, | ||
glyphicon: serverProps.icon, | ||
uuid: serverProps.uuid, | ||
showId: serverProps.showId, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expect(serverComponent.props()).toEqual({ | |
name: serverProps.name, | |
glyphicon: serverProps.icon, | |
uuid: serverProps.uuid, | |
showId: serverProps.showId, | |
}); | |
expect(serverComponent.props()).toEqual(serverProps); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have first applied it expect(serverComponent.props()).toEqual(serverProps);
but it was giving error - ```
"icon": "paperclip",
- "glyphicon": "paperclip",
"name": "localHost", - "nameClass": undefined,
"showId": true,
"uuid": "uuid1",
}
Then I looked at the SeverDisplay prop then these are included
glyphicon: String,
uuid: String,
name: String,
nameClass: String,
showId: { default: true },
``` and also showing in the devTool. Though nameClass
is undefined so I didn't include it in the test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To test Prop I saw this link. https://vueschool.io/articles/vuejs-tutorials/how-to-test-custom-prop-validators-in-vuejs/
So instead of testing each props it can might work like this.
expect(serverComponent.props()).toMatchObject({
name: serverProps.name,
glyphicon: serverProps.icon,
uuid: serverProps.uuid,
showId: serverProps.showId,
});
I am testing locally to trying to see the differences. This also worked but I wasn't sure to include it. In our focus time I will note down these to discuss.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The link you've added in your comment is not related to unit tests, it's about adding validation in the component to ensure that what is passed as a prop has the correct type or value.
While it's perfectly valid to test that the props being passed are correct, we should prioritize asserting dom elements and data that has been massaged. So in this scenario, it's more beneficial to test the title of the span has the correct value, as it is generated from a combination of 3 other props.
expect(latestVersionComponent.props()).toEqual({ | ||
logo: latestProps.logo, | ||
number: latestProps.full, | ||
tag: latestProps.tag, | ||
logocss: latestProps.logocss, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expect(latestVersionComponent.props()).toEqual({ | |
logo: latestProps.logo, | |
number: latestProps.full, | |
tag: latestProps.tag, | |
logocss: latestProps.logocss, | |
}); | |
expect(latestVersionComponent.props()).toEqual(latestProps); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would like you to add another expect here, checking that the span of rundeckVersion has the expected text
const wrapper = await mountRundeckInfo({ | ||
latest: latestProps, | ||
}); | ||
const latestVersionComponent = wrapper.findComponent({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's a good practice to name the const with the name of the component
const latestVersionComponent = wrapper.findComponent({ | |
const rundeckVersionComponent = wrapper.findComponent({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's still work to do, but you're on the right track. I've left some new pointers, let me know if you have any doubts.
const latestSection = wrapper | ||
.find(".rundeck-info-widget__latest") | ||
.findComponent({ name: "RundeckVersion" }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to the .find here, the .findComponent method will find the component as long it's rendered. Changes to do:
- remove
.find(".rundeck-info-widget__latest")
- rename latestSection to rundeckVersionComponent and update the rest of this test to use the new name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to the .find here, the .findComponent method will find the component as long it's rendered. Changes to do:
- remove
.find(".rundeck-info-widget__latest")
- rename latestSection to rundeckVersionComponent and update the rest of this test to use the new name
Done:
removed .find(".rundeck-info-widget__latest")
renamed latestSection to rundeckVersionComponent and update the rest of this test to use the new name
it("should have an anchor with the correct href", async () => { | ||
const wrapper = await mountRundeckInfo(); | ||
const expectedHref = "http://localhost"; | ||
const anchorElement = wrapper.find(`a[href="${expectedHref}"]`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So ideally here you don't want to select the a by the property you're about to test, but by a data-test-id (it's more resilient. Changes to do:
- add a data-test-id for the
<a>
you're going to test inRundeckInfo.vue
; - update test to find this a by the data-test-id;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So ideally here you don't want to select the a by the property you're about to test, but by a data-test-id (it's more resilient. Changes to do:
- add a data-test-id for the
<a>
you're going to test inRundeckInfo.vue
;- update test to find this a by the data-test-id;
Fixed
props: { | ||
appInfo: { title: "Rundeck", logocss: "some-css" }, | ||
...props, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By my previous comment in this PR, I'd like to move the props here. Ie:
props: { | |
appInfo: { title: "Rundeck", logocss: "some-css" }, | |
...props, | |
}, | |
props: { | |
appInfo: { title: "Rundeck", logocss: "some-css" }, | |
version: { logo: false, | |
logocss: "some-css", | |
number: "1.0.0", | |
tag: "stable", | |
title: "Rundeck", }, | |
/// add the other props here | |
...props, | |
}, |
The goal is to keep tests dry, but also mount the components with everything you need for the tests, while having the ability to override something if needed.
expect(latestSection.props("number")).toEqual(latestProps.full); | ||
expect(latestSection.props("tag")).toEqual(latestProps.tag); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, would like you to add another expect here, checking that the span of rundeckVersion has the expected text
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, would like you to add another expect here, checking that the span of rundeckVersion has the expected text
I have question regarding latest release:
In the latest release even though tag is included but the output it is giving "Rundeck and number". Then I checked the computed property and it seems like tag is not included
computed: {
cssclass(): string {
return `${this.logocss} app-logo`;
},
text(): string {
const { title, number, tag } = this;
let text = "";
text += `${title ? title + " " : ""}`;
text += `${number}`;
return text;
},
},
Two ways I am thinking to fix it. Either I include tag in the computed property or don't use tag in the test as the actual output it is asking { title: "Rundeck", full: "1.0.0" }
I am sending fix. Please review and suggest. Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, would like you to add another expect here, checking that the span of rundeckVersion has the expected text
I have question regarding latest release:
In the latest release even though tag is included but the output it is giving "Rundeck and number". Then I checked the computed property and it seems like tag is not includedcomputed: { cssclass(): string { return `${this.logocss} app-logo`; }, text(): string { const { title, number, tag } = this; let text = ""; text += `${title ? title + " " : ""}`; text += `${number}`; return text; }, },
Two ways I am thinking to fix it. Either I include tag in the computed property or don't use tag in the test as the actual output it is asking
{ title: "Rundeck", full: "1.0.0" }
I am sending fix. Please review and suggest. Thanks
realized that span of rundeckVersion has the expected text of title and number
and no need to test tag there. Added tag in my new commit which I removed in this commit.
@@ -120,6 +124,7 @@ describe("RundeckInfo", () => { | |||
expect(rundeckVersionComponent.exists()).toBe(true); | |||
expect(rundeckVersionComponent.props("title")).toEqual(latestProps.title); | |||
expect(rundeckVersionComponent.props("number")).toEqual(latestProps.full); | |||
expect(rundeckVersionComponent.props("tag")).toEqual(latestProps.tag); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
included tag now here and also added one more expect for checking that the span of rundeckVersion has the expected text which is in previous commit. Please suggest If I am missing something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To do:
- remove lines 125 up to 127 (due to reasons explained in previous comment);
- change line 129 to the literal string instead of
${latestProps.title} ${latestProps.full}
as previously discussed;
expect(latestSection.props("number")).toEqual(latestProps.full); | ||
expect(latestSection.props("tag")).toEqual(latestProps.tag); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, would like you to add another expect here, checking that the span of rundeckVersion has the expected text
I have question regarding latest release:
In the latest release even though tag is included but the output it is giving "Rundeck and number". Then I checked the computed property and it seems like tag is not includedcomputed: { cssclass(): string { return `${this.logocss} app-logo`; }, text(): string { const { title, number, tag } = this; let text = ""; text += `${title ? title + " " : ""}`; text += `${number}`; return text; }, },
Two ways I am thinking to fix it. Either I include tag in the computed property or don't use tag in the test as the actual output it is asking
{ title: "Rundeck", full: "1.0.0" }
I am sending fix. Please review and suggest. Thanks
realized that span of rundeckVersion has the expected text of title and number
and no need to test tag there. Added tag in my new commit which I removed in this commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments were not addressed so I will recap them here. As we spoke before, the reason to create a method on the top of the test suite to return the mounted component is to avoid repetition/having to pass the same props every time you write a test.
So unless there is a scenario where the component needs to be mounted with a different set of values (like on line 44), there's no need to declare versionProps, latestProps, etc.
Also, props required by a component, may not be a prop for their child and vice-versa. Therefore, it's important to check how these values are being defined to avoid the scenario of generating console warnings that mismatches are happening (by either passing extra props or lacking the required ones).
ShowId
isn't a prop for RundeckInfo
, but for ServerDisplay
component, that when it's not passed, it has the default value of true. As there isn't anything in RundeckInfo that points out that it also requires this prop, we shouldn't pass this property when mounting this component.
Lastly, when asserting the value of an object, it's fine to pass a variable, but when asserting the value of a string, please write the literal value to highlight the intention.
const versionProps = { | ||
logo: false, | ||
logocss: "some-css", | ||
number: "1.0.0", | ||
tag: "stable", | ||
title: "Rundeck", | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To do: remove versionProps const as it's unused
const versionProps = { | ||
logo: false, | ||
logocss: "some-css", | ||
number: "5.3.0", | ||
title: "Rundeck", | ||
}; | ||
|
||
const wrapper = await mountRundeckInfo({ version: versionProps }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To do:
- remove versionProps const;
- update line 66 to:
const wrapper = await mountRundeckInfo();
- either update line 75 to match the values you've passed in mountRundeckInfo OR update the values in mountRundeckInfo so that test will pass;
const expectedUrl = "http://localhost"; | ||
const anchorElement = wrapper.find('[data-test-id="welcome-link"]'); | ||
expect(anchorElement.exists()).toBe(true); | ||
expect(anchorElement.attributes("href")).toBe(expectedUrl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To do:
- remove expectedUrl const;
- update line 137 to : expect(anchorElement.attributes("href")).toBe("http://localhost");
version: { | ||
logo: false, | ||
logocss: "some-css", | ||
number: "1.0.0", | ||
tag: "stable", | ||
title: "Rundeck", | ||
}, | ||
server: { | ||
name: "localHost", | ||
icon: "paperclip", | ||
uuid: "uuid1", | ||
showId: true, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When mounting a component, you must ensure that the component will be mounted with all the required props and that they match their type definition. In the case of rundeckInfo, the type definitions live in the file store/System.ts
.
To do:
- remove from version object:
logo
,logocss
,title
; - add to version object:
name: "Erebus",
color: "red",
icon: "glass",
edition: "Community",
- also, add the object for latest (that you have in one of your latest tests), with the following properties:
full: "v5.2.0-20240410",
number: "5.2.0",
tag: "GA",
color: "aquamarine",
date: new Date(),
icon: "knight",
edition: "Community"
const serverProps = { | ||
name: "localHost", | ||
icon: "paperclip", | ||
uuid: "uuid1", | ||
showId: true, | ||
}; | ||
const wrapper = await mountRundeckInfo({ server: serverProps }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to pass the serverProps again, because you've defined the default values in the mount method. To do:
- remove serverProps const;
- update line 84 to:
const wrapper = await mountRundeckInfo();
- update line 2 in
ServerDisplay.vue
, adding a data-test-id attribute:data-test-id="server-title"
- update line 88 to check that the span with the data-test-id you've just added, has a title attribute with the value of
paperclip-uu / uuid1
Important, do not do write the expected value as a variable. We spoke about this in the last meeting, on tests we want to write the literal strings when asserting text.
expect(serverComponent.props()).toEqual({ | ||
name: serverProps.name, | ||
glyphicon: serverProps.icon, | ||
uuid: serverProps.uuid, | ||
showId: serverProps.showId, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The link you've added in your comment is not related to unit tests, it's about adding validation in the component to ensure that what is passed as a prop has the correct type or value.
While it's perfectly valid to test that the props being passed are correct, we should prioritize asserting dom elements and data that has been massaged. So in this scenario, it's more beneficial to test the title of the span has the correct value, as it is generated from a combination of 3 other props.
const versionDisplay = { | ||
name: "Rundeck", | ||
icon: "icon-url", | ||
color: "lamp", | ||
}; | ||
const wrapper = await mountRundeckInfo({ version: versionDisplay }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To do:
- remove versionProps const;
- update line 102 to: const wrapper = await mountRundeckInfo();
const latestProps = { | ||
title: "Rundeck", | ||
full: "1.0.0", | ||
tag: "stable", | ||
}; | ||
|
||
const wrapper = await mountRundeckInfo({ | ||
latest: latestProps, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To do:
- remove latestProps const;
- update line 117 to: const wrapper = await mountRundeckInfo();
@@ -120,6 +124,7 @@ describe("RundeckInfo", () => { | |||
expect(rundeckVersionComponent.exists()).toBe(true); | |||
expect(rundeckVersionComponent.props("title")).toEqual(latestProps.title); | |||
expect(rundeckVersionComponent.props("number")).toEqual(latestProps.full); | |||
expect(rundeckVersionComponent.props("tag")).toEqual(latestProps.tag); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To do:
- remove lines 125 up to 127 (due to reasons explained in previous comment);
- change line 129 to the literal string instead of
${latestProps.title} ${latestProps.full}
as previously discussed;
Thank you @smartinellibenedetti , sending fix in few minutes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good progress! Left a few suggestions for the tests' descriptions that could be a little bit more clear. There are 2 small fixes to be done but afterwards, it's good to merge!
); | ||
expect(wrapper.findComponent({ name: "RundeckLogo" }).exists()).toBe(false); | ||
}); | ||
it("renders RundeckVersions information with correct props", async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it("renders RundeckVersions information with correct props", async () => { | |
it("renders RundeckVersion information with correct props", async () => { |
); | ||
expect(wrapper.findComponent({ name: "RundeckLogo" }).exists()).toBe(false); | ||
}); | ||
it("renders RundeckVersions information with correct props", async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it("renders RundeckVersions information with correct props", async () => { | |
it("renders the correct Rundeck version based on number and title props", async () => { |
|
||
expect(rundeckVersionComponent.find("span").text()).toBe("Rundeck 1.0.0"); | ||
}); | ||
it("renders ServerDisplay correctly with default props", async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it("renders ServerDisplay correctly with default props", async () => { | |
it("renders the server's shortened uuid along with a title attribute", async () => { |
expect( | ||
serverComponent.find('[data-test-id="server-title"]').attributes("title"), | ||
).toBe("paperclip-uu / uuid1"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add here another expect that checks that there's a span with the text equal to the short uuid
).toBe("paperclip-uu / uuid1"); | ||
}); | ||
|
||
it("renders version display correctly", async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it("renders version display correctly", async () => { | |
it("renders Rundeck's version display name", async () => { |
expect(rundeckVersionComponent.exists()).toBe(true); | ||
expect(rundeckVersionComponent.find("span").text()).toBe("Rundeck 1.0.0"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RundeckInfo renders 2 RundeckVersion components - one that you've tested before, that will render "Rundeck 1.0.0", and one that renders the latest release info.
This test should be checking the text of the second RundeckVersion. To do so, you can use the method findAllComponents as findComponent will always return the first occurrence that matches what you're searching.
@@ -0,0 +1,1108 @@ | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jayas006 could you please delete this package-lock?
@smartinellibenedetti thanks for the pointers. I've fixed it. |
abc63db
to
78086b9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please delete the package-lock.json
it("renders the link with the correct href", async () => { | ||
const wrapper = await mountRundeckInfo(); | ||
const anchorElement = wrapper.find('[data-test-id="welcome-link"]'); | ||
expect(anchorElement.exists()).toBe(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't need to change this but a note for the future: if an element doesn't have a v-if, you don't need to assert every time that the element exists
expect( | ||
serverComponent.find('[data-test-id="server-title"]').attributes("title") | ||
).toBe("paperclip-uu / uuid1"); | ||
expect(serverComponent.find("span").text()).toBe("uulocalHost"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heads-up, this assertion is wrong as it's getting the first parent span and concatenating all the content from the subsequent spans. Please use a data-test-id on the span that renders the {{shortUuid}}
, so that you'll expect the text to be just 2 characters (in your case, uu)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good to go!
Is this a bugfix, or an enhancement? Please describe.
This is an enhancement. I have created
RundeckInfo.spec.ts
to check if theRundeckInfo
components of our project works right. This helps us make sure everything inRundeckInfo
is working well.Describe the solution you've implemented
In the
RundeckInfo.spec.ts
file, I wrote tests to check different parts of RundeckInfo. These tests help us find problems early and make sure we don't mess up the RundeckInfo when we change other things in the future.Describe alternatives you've considered
Additional context