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-2307 unit tests for edit project file #9084

Merged
merged 13 commits into from
May 8, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { mount } from "@vue/test-utils";
import EditProjectFile from "./EditProjectFile.vue";
import * as editProjectFileService from "./editProjectFileService";

jest.mock("@/app/components/readme-motd/editProjectFileService", () => ({
getFileText: jest.fn(() =>
Promise.resolve({
success: true,
contents: "sample file content",
}),
),
saveProjectFile: jest.fn(() =>
Promise.resolve({
success: true,
message: "File saved successfully.",
}),
),
}));

jest.mock("@/library/rundeckService", () => ({
getRundeckContext: jest.fn().mockReturnValue({
rundeckClient: {
getFileText: jest.fn().mockResolvedValue({
filename: "readme.md",
success: true,
contents: "Some content",
rdBase: "http://localhost:4440",
}),
saveProjectFile: jest.fn().mockResolvedValue({
success: true,
message: "File saved successfully.",
}),
},
rdBase: "http://localhost:4440",
}),
url: jest.fn().mockReturnValue("http://localhost:4440"),
}));
let wrapper;
const mountEditProjectFile = async (props = {}) => {
wrapper = mount(EditProjectFile, {
props: {
filename: "readme.md",
project: "default",
authAdmin: true,
...props,
},
global: {
mocks: {
$t: (msg) => msg,
},
},
});
};

describe("EditProjectFile", () => {
beforeEach(async () => {
await mountEditProjectFile();
});

afterEach(() => {
jest.clearAllMocks();
});

it.each([
["readme.md", "edit.readme.label"],
["motd.md", "edit.motd.label"],
])("renders the correct title for %s", async (filename, expectedTitle) => {
await mountEditProjectFile({ filename });
expect(wrapper.find('[data-test-id="title"]').text()).toContain(
expectedTitle,
);
});

it("renders file content when getFileText method returns successfully", async () => {
await wrapper.vm.$nextTick();
expect(wrapper.vm.fileText).toBe("sample file content");
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please change this test to assert instead that the HTML rendered by ace-editor has this string inside of it?

});

it("handles failure when getFileText method fails", async () => {
(editProjectFileService.getFileText as jest.Mock).mockImplementationOnce(
() => Promise.reject(new Error("Failed to fetch file")),
);
wrapper.vm.notifyError = jest.fn();
Copy link
Contributor

Choose a reason for hiding this comment

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

no need to override wrapper.vm.notifyError, you can use spyOn instead (https://jestjs.io/docs/jest-object#jestspyonobject-methodname)

await wrapper.vm.getFileText();
expect(wrapper.vm.notifyError).toHaveBeenCalledWith("Failed to fetch file");
});

it("handles failure when user edits the file and fails to save it", async () => {
(editProjectFileService.saveProjectFile as jest.Mock).mockRejectedValue(
new Error("Failed to save file"),
);
wrapper.vm.notifyError = jest.fn();
Copy link
Contributor

Choose a reason for hiding this comment

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

Same thing here, please use spyOn

wrapper.vm.fileText = "new content";
await wrapper.find('[data-test-id="save"]').trigger("click");
expect(wrapper.vm.notifyError).toHaveBeenCalledWith("Failed to save file");
});

it("handles success when user edits the file and saves it", async () => {
wrapper.vm.fileText = "new content";
await wrapper.find('[data-test-id="save"]').trigger("click");
expect(
editProjectFileService.saveProjectFile as jest.Mock,
).toHaveBeenCalledWith("default", "readme.md", "new content");
});

it("displays admin specific message and configuration link when user is an admin", 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("displays admin specific message and configuration link when user is an admin", async () => {
it("displays warning message and configuration link when user is an admin and displayConfig value is 'none'", async () => {

await mountEditProjectFile({
displayConfig: ["none"],
});
const footerText = wrapper.find(".card-footer").text();
expect(footerText).toContain("file.warning.not.displayed.admin.message");
expect(wrapper.find(".card-footer a").text()).toBe(
"project.configuration.label",
);
});

it("displays non-admin specific message when user is not an admin", 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("displays non-admin specific message when user is not an admin", async () => {
it("displays warning message and configuration link when user isn't an admin and displayConfig value is 'none'", async () => {

await mountEditProjectFile({
authAdmin: false,
displayConfig: ["none"],
});
await wrapper.vm.$nextTick();
expect(wrapper.text()).toContain(
Copy link
Contributor

Choose a reason for hiding this comment

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

please use a data-test-id instead of using wrapper.text()

"file.warning.not.displayed.nonadmin.message",
);
});
it("does not allow non-admin user to save the file", async () => {
await mountEditProjectFile({ authAdmin: false });
expect(wrapper.find('[data-test-id="save"]').exists()).toBe(false);
});
it("does not allow non-admin user to edit the file", async () => {
await mountEditProjectFile({ authAdmin: false });
expect(wrapper.find('[data-test-id="editor"]').exists()).toBe(false);
});

//TODO: working on this one
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@smartinellibenedetti Can you please look at it? This one not working. Thanks

Copy link
Contributor

Choose a reason for hiding this comment

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

When you trigger actions that require the UI to re-render, remember to do a:

await wrapper.vm.$nextTick();

before trying to assert the values

// it("does not change file content when user cancels the edit", async () => {
//
// });
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="content">
<div id="layoutBody">
<div class="title">
<span class="text-h3">
<span class="text-h3" data-test-id="title">
<template v-if="filename === 'readme.md'">
<i class="fas fa-file-alt"></i>
{{ $t("edit.readme.label") }}
Expand Down Expand Up @@ -45,25 +45,30 @@
</details>
</div>
<ace-editor
v-if="authAdmin"
Copy link
Contributor

Choose a reason for hiding this comment

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

Important, the behaviour of the components shouldn't change while writing tests, therefore please undo the changes on lines 48 and 68.

When the user doesn't have permissions to create/edit files, the component will render strings (whose logic is defined inside the block that starts with <template v-if="displayConfig.includes('none')">) and the tests should confirm that the right strings are being rendered in such cases.

v-model="fileText"
:soft-wrap-control="true"
height="250"
width="100%"
lang="markdown"
:read-only="false"
data-test-id="ace-editor"
/>
</div>
<div class="card-footer">
<button
type="button"
class="btn btn-default reset_page_confirm"
data-test-id="cancel"
@click="createProjectHomeLink"
>
Cancel
</button>
<button
v-if="authAdmin"
type="submit"
class="btn btn-cta reset_page_confirm"
data-test-id="save"
@click="saveProjectFile"
>
Save
Expand Down Expand Up @@ -127,6 +132,7 @@ export default defineComponent({
data() {
return {
fileText: "",

markdownSectionOpen: false,
errorMsg: "",
};
Expand All @@ -138,6 +144,7 @@ export default defineComponent({
},
mounted() {
this.getFileText();
this.originalFileText = this.fileText;
},
methods: {
async saveProjectFile() {
Expand Down Expand Up @@ -185,6 +192,7 @@ export default defineComponent({
async getFileText() {
try {
const response = await getFileText(this.project, this.filename);

if (response.success) {
this.fileText = response.contents;
}
Expand Down