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,159 @@
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().mockResolvedValue({
success: true,
contents: "sample file content",
}),
saveProjectFile: jest.fn().mockResolvedValue({
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({
href: "http://localhost:4440/project/default/home",
}),
}));

jest.mock("../../../library/components/utils/AceEditor.vue", () => ({
name: "AceEditor",
functional: true,
template: '<span class="ace_text ace_xml"></span>',
methods: {
getValue: jest.fn().mockReturnValue("sample file content"),
},
}));

describe("EditProjectFile", () => {
let wrapper;
let originalLocation;

const mountEditProjectFile = async (props = {}) => {
wrapper = mount(EditProjectFile, {
props: {
filename: "readme.md",
project: "default",
authAdmin: true,
displayConfig: ["none"],
...props,
},
global: {
mocks: {
$t: (msg) => msg,
},
},
});
await wrapper.vm.$nextTick();
};

beforeEach(async () => {
originalLocation = window.location;
delete (window as any).location;
window.location = {
...originalLocation,
assign: jest.fn(),
href: jest.fn(),
replace: jest.fn(),
toString: () => (window.location as any)._href || "http://localhost:4440",
};

await mountEditProjectFile();
});

afterEach(() => {
jest.clearAllMocks();
window.location = originalLocation;
});

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 inside AceEditor's span element when getFileText method returns successfully", async () => {
wrapper.vm.getFileText = jest.fn().mockResolvedValue("sample file content");
await wrapper.vm.getFileText();
await wrapper.vm.$nextTick();
const aceEditor = wrapper.findComponent({ name: "AceEditor" });
expect(aceEditor.exists()).toBe(true);
const span = aceEditor.find("span.ace_text.ace_xml");
expect(span.exists()).toBe(true);
const spanHtml = span.html();
expect(spanHtml).toContain("sample file content");
});
it("handles failure when getFileText method fails", async () => {
const notifyErrorSpy = jest.spyOn(wrapper.vm, "notifyError");
(editProjectFileService.getFileText as jest.Mock).mockImplementationOnce(
() => Promise.reject(new Error("Failed to fetch file"))
);
await wrapper.vm.getFileText();
expect(notifyErrorSpy).toHaveBeenCalledWith("Failed to fetch file");
});

it("handles failure when user edits the file and fails to save it", async () => {
(
editProjectFileService.saveProjectFile as jest.Mock
).mockImplementationOnce(() =>
Promise.reject(new Error("Failed to save file"))
);
const notifyErrorSpy = jest.spyOn(wrapper.vm, "notifyError");
wrapper.vm.fileText = "new content";
await wrapper.find('[data-test-id="save"]').trigger("click");
expect(notifyErrorSpy).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 warning message and configuration link when user is an admin and displayConfig value is 'none", async () => {
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 warning message and configuration link when user isn't an admin and displayConfig value is 'none", async () => {
await mountEditProjectFile({
authAdmin: false,
});
expect(
wrapper.find('[data-test-id="nonadmin-warning-message"]').text()
).toContain("file.warning.not.displayed.nonadmin.message");
});
it("navigates to the home page when the cancel button is clicked", async () => {
await mountEditProjectFile();
const button = wrapper.find('[data-test-id="cancel"]');

await button.trigger("click");

expect(window.location).toBe("http://localhost:4440/project/default/home");
});
});
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,31 +45,38 @@
</details>
</div>
<ace-editor
ref="aceEditor"
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
type="submit"
class="btn btn-cta reset_page_confirm"
data-test-id="save"
@click="saveProjectFile"
>
Save
</button>
<template v-if="displayConfig.includes('none')">
<span class="text-warning text-right">
<span
class="text-warning text-right"
data-test-id="nonadmin-warning-message"
>
<template v-if="authAdmin">
{{ $t("file.warning.not.displayed.admin.message") }}
<a :href="createProjectConfigureLink">
Expand Down Expand Up @@ -127,6 +134,7 @@ export default defineComponent({
data() {
return {
fileText: "",

markdownSectionOpen: false,
errorMsg: "",
};
Expand All @@ -145,7 +153,7 @@ export default defineComponent({
const resp = await saveProjectFile(
this.project,
this.filename,
this.fileText,
this.fileText
);
if (resp.success) {
this.notifySuccess("Success", "Saved Project File " + this.filename);
Expand All @@ -155,7 +163,7 @@ export default defineComponent({
}
},
createProjectHomeLink() {
document.location = url("project/" + this.project + "/home").href;
window.location = url("project/" + this.project + "/home").href;
},
notifyError(msg) {
Notification.notify({
Expand Down Expand Up @@ -185,6 +193,7 @@ export default defineComponent({
async getFileText() {
try {
const response = await getFileText(this.project, this.filename);

if (response.success) {
this.fileText = response.contents;
}
Expand All @@ -195,7 +204,7 @@ export default defineComponent({
this.filename +
" does not exist in project " +
this.project +
" yet. Please save to create it.",
" yet. Please save to create it."
);
} else {
this.notifyError(e.message);
Expand Down