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-2255 add unit tests for RundeckInfo component #9048

Merged
merged 15 commits into from May 8, 2024

Conversation

jayas006
Copy link
Contributor

@jayas006 jayas006 commented Apr 8, 2024

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

This is an enhancement. I have created RundeckInfo.spec.ts to check if theRundeckInfocomponents of our project works right. This helps us make sure everything in RundeckInfo 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

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.

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.

};
const wrapper = await mountRundeckInfo({ version: versionProps });
const versionComponent = wrapper.findComponent({ name: "RundeckVersion" });
expect(versionComponent.exists()).toBe(true);
Copy link
Contributor

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.

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 for the feedback. I am going to look at it

Comment on lines 56 to 61
expect(serverComponent.props()).toEqual({
name: serverProps.name,
glyphicon: serverProps.icon,
uuid: serverProps.uuid,
showId: serverProps.showId,
});
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
expect(serverComponent.props()).toEqual({
name: serverProps.name,
glyphicon: serverProps.icon,
uuid: serverProps.uuid,
showId: serverProps.showId,
});
expect(serverComponent.props()).toEqual(serverProps);

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Comment on lines 92 to 97
expect(latestVersionComponent.props()).toEqual({
logo: latestProps.logo,
number: latestProps.full,
tag: latestProps.tag,
logocss: latestProps.logocss,
});
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
expect(latestVersionComponent.props()).toEqual({
logo: latestProps.logo,
number: latestProps.full,
tag: latestProps.tag,
logocss: latestProps.logocss,
});
expect(latestVersionComponent.props()).toEqual(latestProps);

Copy link
Contributor

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({
Copy link
Contributor

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

Suggested change
const latestVersionComponent = wrapper.findComponent({
const rundeckVersionComponent = wrapper.findComponent({

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.

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.

Comment on lines 94 to 96
const latestSection = wrapper
.find(".rundeck-info-widget__latest")
.findComponent({ name: "RundeckVersion" });
Copy link
Contributor

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

Copy link
Contributor Author

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}"]`);
Copy link
Contributor

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 in RundeckInfo.vue;
  • update test to find this a by 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.

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 in RundeckInfo.vue;
  • update test to find this a by the data-test-id;

Fixed

Comment on lines 11 to 35
props: {
appInfo: { title: "Rundeck", logocss: "some-css" },
...props,
},
Copy link
Contributor

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:

Suggested change
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.

Comment on lines 99 to 100
expect(latestSection.props("number")).toEqual(latestProps.full);
expect(latestSection.props("tag")).toEqual(latestProps.tag);
Copy link
Contributor

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

Copy link
Contributor Author

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

Copy link
Contributor Author

@jayas006 jayas006 Apr 12, 2024

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

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);
Copy link
Contributor Author

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.

Copy link
Contributor

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;

Comment on lines 99 to 100
expect(latestSection.props("number")).toEqual(latestProps.full);
expect(latestSection.props("tag")).toEqual(latestProps.tag);
Copy link
Contributor Author

@jayas006 jayas006 Apr 12, 2024

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

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.

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.

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.

Comment on lines 46 to 52
const versionProps = {
logo: false,
logocss: "some-css",
number: "1.0.0",
tag: "stable",
title: "Rundeck",
};
Copy link
Contributor

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

Comment on lines 59 to 66
const versionProps = {
logo: false,
logocss: "some-css",
number: "5.3.0",
title: "Rundeck",
};

const wrapper = await mountRundeckInfo({ version: versionProps });
Copy link
Contributor

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);
Copy link
Contributor

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");

Comment on lines 13 to 33
version: {
logo: false,
logocss: "some-css",
number: "1.0.0",
tag: "stable",
title: "Rundeck",
},
server: {
name: "localHost",
icon: "paperclip",
uuid: "uuid1",
showId: true,
},
Copy link
Contributor

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"

Comment on lines 78 to 84
const serverProps = {
name: "localHost",
icon: "paperclip",
uuid: "uuid1",
showId: true,
};
const wrapper = await mountRundeckInfo({ server: serverProps });
Copy link
Contributor

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.

Comment on lines 56 to 61
expect(serverComponent.props()).toEqual({
name: serverProps.name,
glyphicon: serverProps.icon,
uuid: serverProps.uuid,
showId: serverProps.showId,
});
Copy link
Contributor

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.

Comment on lines 97 to 102
const versionDisplay = {
name: "Rundeck",
icon: "icon-url",
color: "lamp",
};
const wrapper = await mountRundeckInfo({ version: versionDisplay });
Copy link
Contributor

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();

Comment on lines 111 to 119
const latestProps = {
title: "Rundeck",
full: "1.0.0",
tag: "stable",
};

const wrapper = await mountRundeckInfo({
latest: latestProps,
});
Copy link
Contributor

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);
Copy link
Contributor

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;

@jayas006
Copy link
Contributor Author

Thank you @smartinellibenedetti , sending fix in few minutes

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 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 () => {
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
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 () => {
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
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 () => {
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
it("renders ServerDisplay correctly with default props", async () => {
it("renders the server's shortened uuid along with a title attribute", async () => {

Comment on lines 71 to 74
expect(
serverComponent.find('[data-test-id="server-title"]').attributes("title"),
).toBe("paperclip-uu / uuid1");
Copy link
Contributor

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 () => {
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
it("renders version display correctly", async () => {
it("renders Rundeck's version display name", async () => {

Comment on lines 88 to 103
expect(rundeckVersionComponent.exists()).toBe(true);
expect(rundeckVersionComponent.find("span").text()).toBe("Rundeck 1.0.0");
Copy link
Contributor

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 @@
{
Copy link
Contributor

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?

@jayas006
Copy link
Contributor Author

jayas006 commented May 6, 2024

@smartinellibenedetti thanks for the pointers. I've fixed 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.

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);
Copy link
Contributor

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");
Copy link
Contributor

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)

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 to go!

@smartinellibenedetti smartinellibenedetti added this to the 5.3.0 milestone May 7, 2024
@ltamaster ltamaster merged commit 05e5133 into main May 8, 2024
3 checks passed
@ltamaster ltamaster deleted the feat/unit-test-for-rundeckInfo branch May 8, 2024 18:39
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

3 participants