Merge branch 'new-vue-components-tested' into 'master'

Added new vue components tested

See merge request framasoft/mobilizon!739
This commit is contained in:
Thomas Citharel 2020-12-03 17:39:57 +01:00
commit b0ab0af9ea
6 changed files with 480 additions and 4 deletions

View File

@ -26,9 +26,12 @@
</p> </p>
</div> </div>
<div class="send-comment"> <div class="send-comment">
<b-button native-type="submit" type="is-primary">{{ <b-button
$t("Post a comment") native-type="submit"
}}</b-button> type="is-primary"
class="comment-button-submit"
>{{ $t("Post a comment") }}</b-button
>
</div> </div>
</div> </div>
</article> </article>
@ -37,6 +40,9 @@
$t("The organiser has chosen to close comments.") $t("The organiser has chosen to close comments.")
}}</b-notification> }}</b-notification>
<transition name="comment-empty-list" mode="out-in"> <transition name="comment-empty-list" mode="out-in">
<p v-if="$apollo.queries.comments.loading" class="loading">
{{ $t("Loading…") }}
</p>
<transition-group <transition-group
name="comment-list" name="comment-list"
v-if="comments.length" v-if="comments.length"
@ -326,7 +332,7 @@ export default class CommentTree extends Vue {
} }
get isAbleToComment(): boolean { get isAbleToComment(): boolean {
if (this.currentActor.id) { if (this.currentActor && this.currentActor.id) {
return this.areCommentsClosed || this.isEventOrganiser; return this.areCommentsClosed || this.isEventOrganiser;
} }
return false; return false;

View File

@ -0,0 +1,157 @@
import { config, createLocalVue, shallowMount, Wrapper } from "@vue/test-utils";
import CommentTree from "@/components/Comment/CommentTree.vue";
import Buefy from "buefy";
import { InMemoryCache } from "apollo-cache-inmemory";
import {
createMockClient,
MockApolloClient,
RequestHandler,
} from "mock-apollo-client";
import buildCurrentUserResolver from "@/apollo/user";
import VueApollo from "vue-apollo";
import { COMMENTS_THREADS, CREATE_COMMENT_FROM_EVENT } from "@/graphql/comment";
import { CommentModeration } from "@/types/enums";
import { IEvent } from "@/types/event.model";
import {
eventCommentThreadsMock,
newCommentForEventMock,
newCommentForEventResponse,
} from "../../mocks/event";
const localVue = createLocalVue();
localVue.use(Buefy);
localVue.use(VueApollo);
config.mocks.$t = (key: string): string => key;
const eventData = {
id: "1",
uuid: "e37910ea-fd5a-4756-7634-00971f3f4107",
options: {
commentModeration: CommentModeration.ALLOW_ALL,
},
};
describe("CommentTree", () => {
let wrapper: Wrapper<Vue>;
let mockClient: MockApolloClient;
let apolloProvider;
let requestHandlers: Record<string, RequestHandler>;
const generateWrapper = (handlers = {}, baseData = {}) => {
const cache = new InMemoryCache({ addTypename: false });
mockClient = createMockClient({
cache,
resolvers: buildCurrentUserResolver(cache),
});
requestHandlers = {
eventCommentThreadsQueryHandler: jest
.fn()
.mockResolvedValue(eventCommentThreadsMock),
createCommentForEventMutationHandler: jest
.fn()
.mockResolvedValue(newCommentForEventResponse),
...handlers,
};
mockClient.setRequestHandler(
COMMENTS_THREADS,
requestHandlers.eventCommentThreadsQueryHandler
);
mockClient.setRequestHandler(
CREATE_COMMENT_FROM_EVENT,
requestHandlers.createCommentForEventMutationHandler
);
apolloProvider = new VueApollo({
defaultClient: mockClient,
});
wrapper = shallowMount(CommentTree, {
localVue,
apolloProvider,
propsData: {
event: { ...eventData },
},
stubs: ["editor"],
data() {
return {
currentActor: {
id: "76",
},
...baseData,
};
},
});
};
it("renders a comment tree", async () => {
generateWrapper();
expect(wrapper.findComponent({ name: "b-notification" }).text()).toBe(
"The organiser has chosen to close comments."
);
expect(wrapper.find(".loading").text()).toBe("Loading…");
expect(wrapper.html()).toMatchSnapshot();
});
it("renders a comment tree", async () => {
generateWrapper();
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick(); // because of the <transition>
expect(wrapper.exists()).toBe(true);
expect(requestHandlers.eventCommentThreadsQueryHandler).toHaveBeenCalled();
expect(wrapper.vm.$apollo.queries.comments).toBeTruthy();
expect(wrapper.find(".loading").exists()).toBe(false);
expect(wrapper.findAll(".comment-list .root-comment").length).toBe(2);
expect(wrapper.html()).toMatchSnapshot();
});
it("renders the form if we can comment", async () => {
generateWrapper(
{},
{
newComment: {
text: newCommentForEventMock.text,
},
}
);
wrapper.setData({
currentActor: {
id: "67",
},
});
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick(); // because of the <transition>
expect(wrapper.find("form.new-comment").isVisible()).toBe(true);
expect(wrapper.findAll(".comment-list .root-comment").length).toBe(2);
wrapper.find("form.new-comment").trigger("submit");
await wrapper.vm.$nextTick();
expect(
requestHandlers.createCommentForEventMutationHandler
).toHaveBeenCalledWith({
...newCommentForEventMock,
});
if (mockClient) {
const cachedData = mockClient.cache.readQuery<{ event: IEvent }>({
query: COMMENTS_THREADS,
variables: {
eventUUID: eventData.uuid,
},
});
if (cachedData) {
const { event } = cachedData;
expect(event.comments).toHaveLength(3);
}
}
});
});

View File

@ -0,0 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CommentTree renders a comment tree 1`] = `
<div>
<b-notification-stub active="true" duration="2000" animation="fade">The organiser has chosen to close comments.</b-notification-stub>
<transition-stub name="comment-empty-list" mode="out-in">
<p class="loading">
Loading…
</p>
<!---->
</transition-stub>
</div>
`;
exports[`CommentTree renders a comment tree 2`] = `
<div>
<b-notification-stub active="true" duration="2000" animation="fade">The organiser has chosen to close comments.</b-notification-stub>
<transition-stub name="comment-empty-list" mode="out-in">
<!---->
<transition-group-stub tag="ul" name="comment-list" class="comment-list">
<comment-stub comment="[object Object]" event="[object Object]" class="root-comment"></comment-stub>
<comment-stub comment="[object Object]" event="[object Object]" class="root-comment"></comment-stub>
</transition-group-stub>
</transition-stub>
</div>
`;

View File

@ -0,0 +1,62 @@
import { config, createLocalVue, mount } from "@vue/test-utils";
import ReportCard from "@/components/Report/ReportCard.vue";
import Buefy from "buefy";
import { ActorType } from "@/types/enums";
const localVue = createLocalVue();
localVue.use(Buefy);
config.mocks.$t = (key: string): string => key;
const reportData = {
id: "1",
content: "My content",
insertedAt: "2020-12-02T09:01:20.873Z",
reporter: {
preferredUsername: "author",
domain: null,
name: "Reporter of Things",
type: ActorType.PERSON,
},
reported: {
preferredUsername: "my-awesome-group",
domain: null,
name: "My Awesome Group",
},
};
const generateWrapper = (customReportData: Record<string, unknown> = {}) => {
return mount(ReportCard, {
localVue,
propsData: {
report: { ...reportData, ...customReportData },
},
});
};
describe("ReportCard", () => {
it("renders report card with basic informations", () => {
const wrapper = generateWrapper();
expect(wrapper.find(".media-content .title").text()).toBe(
reportData.reported.name
);
expect(wrapper.find(".media-content .subtitle").text()).toBe(
`@${reportData.reported.preferredUsername}`
);
expect(wrapper.find(".is-one-quarter-desktop span").text()).toBe(
`Reported by {reporter}`
);
});
it("renders report card with with a remote reporter", () => {
const wrapper = generateWrapper({
reporter: { domain: "somewhere.else", type: ActorType.APPLICATION },
});
expect(wrapper.find(".is-one-quarter-desktop span").text()).toBe(
"Reported by someone on {domain}"
);
});
});

View File

@ -0,0 +1,130 @@
import { config, createLocalVue, mount } from "@vue/test-utils";
import ReportModal from "@/components/Report/ReportModal.vue";
import Buefy from "buefy";
const localVue = createLocalVue();
localVue.use(Buefy);
config.mocks.$t = (key: string): string => key;
const propsData = {
onConfirm: jest.fn(),
};
const generateWrapper = (customPropsData: Record<string, unknown> = {}) => {
return mount(ReportModal, {
localVue,
propsData: {
...propsData,
...customPropsData,
},
});
};
beforeEach(() => {
jest.resetAllMocks();
});
describe("ReportModal", () => {
it("renders report modal with basic informations and submits it", async () => {
const wrapper = generateWrapper();
expect(wrapper.find(".modal-card-head").exists()).toBe(false);
expect(wrapper.find(".media-content").text()).not.toContain(
"The content came from another server. Transfer an anonymous copy of the report?"
);
expect(
wrapper.find("footer.modal-card-foot button:first-child").text()
).toBe("Cancel");
const submit = wrapper.find("footer.modal-card-foot button.is-primary");
expect(submit.text()).toBe("Send the report");
const textarea = wrapper.find("textarea");
textarea.setValue("some comment with my report");
submit.trigger("click");
await localVue.nextTick();
expect(wrapper.emitted().close).toBeTruthy();
expect(wrapper.vm.$props.onConfirm).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$props.onConfirm).toHaveBeenCalledWith(
"some comment with my report",
false
);
});
it("renders report modal and shows an inline comment if it's provided", async () => {
const wrapper = generateWrapper({
comment: {
actor: { preferredUsername: "author", name: "I am the comment author" },
text: "this is my <b>comment</b> that will be reported",
},
});
const commentContainer = wrapper.find("article.media");
expect(commentContainer.find("strong").text()).toContain(
"I am the comment author"
);
expect(commentContainer.find("small").text()).toContain("author");
expect(commentContainer.find("p").html()).toContain(
"this is my <b>comment</b> that will be reported"
);
});
it("renders report modal with with a remote content", async () => {
const wrapper = generateWrapper({ outsideDomain: "somewhere.else" });
expect(wrapper.find(".media-content").text()).toContain(
"The content came from another server. Transfer an anonymous copy of the report?"
);
const submit = wrapper.find("footer.modal-card-foot button.is-primary");
submit.trigger("click");
await localVue.nextTick();
expect(wrapper.vm.$props.onConfirm).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$props.onConfirm).toHaveBeenCalledWith("", false);
});
it("renders report modal with with a remote content and accept to forward", async () => {
const wrapper = generateWrapper({ outsideDomain: "somewhere.else" });
expect(wrapper.find(".media-content").text()).toContain(
"The content came from another server. Transfer an anonymous copy of the report?"
);
const switchButton = wrapper.find('input[type="checkbox"]');
switchButton.trigger("click");
const submit = wrapper.find("footer.modal-card-foot button.is-primary");
submit.trigger("click");
await localVue.nextTick();
expect(wrapper.vm.$props.onConfirm).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$props.onConfirm).toHaveBeenCalledWith("", true);
});
it("renders report modal custom title and buttons", async () => {
const wrapper = generateWrapper({
title: "want to report something?",
cancelText: "nah",
confirmText: "report!",
});
expect(wrapper.find(".modal-card-head .modal-card-title").text()).toBe(
"want to report something?"
);
expect(
wrapper.find("footer.modal-card-foot button:first-child").text()
).toBe("nah");
expect(
wrapper.find("footer.modal-card-foot button.is-primary").text()
).toBe("report!");
});
});

View File

@ -0,0 +1,95 @@
export const eventCommentThreadsMock = {
data: {
event: {
__typename: "Event",
id: "1",
uuid: "f37910ea-fd5a-4756-9679-00971f3f4106",
comments: [
{
__typename: "Comment",
id: "2",
uuid: "e37910ea-fd5a-4756-9679-00971f3f4107",
url:
"https://some-instance.tld/comments/e37910ea-fd5a-4756-9679-00971f3f4107",
text: "my comment text",
local: true,
visibility: "PUBLIC",
totalReplies: 5,
updatedAt: "2020-12-03T09:02:00Z",
actor: {
avatar: {
id: "78",
url: "http://someavatar.url.me",
},
id: "89",
domain: null,
preferredUsername: "someauthor",
name: "Some author",
summary: "I am the senate",
},
deletedAt: null,
},
{
__typename: "Comment",
id: "29",
uuid: "e37910ea-fd5a-4756-9679-01171f3f4107",
url:
"https://some-instance.tld/comments/e37910ea-fd5a-4756-9679-01171f3f4107",
text: "a second comment",
local: true,
visibility: "PUBLIC",
totalReplies: 0,
updatedAt: "2020-12-03T11:02:00Z",
actor: {
avatar: {
id: "78",
url: "http://someavatar.url.me",
},
id: "89",
domain: null,
preferredUsername: "someauthor",
name: "Some author",
summary: "I am the senate",
},
deletedAt: null,
},
],
},
},
};
export const newCommentForEventMock = {
eventId: "1",
text: "my new comment",
inReplyToCommentId: null,
};
export const newCommentForEventResponse = {
data: {
createComment: {
id: "79",
uuid: "e37910ea-fd5a-4756-9679-01171f3f4444",
url:
"https://some-instance.tld/comments/e37910ea-fd5a-4756-9679-01171f3f4444",
text: newCommentForEventMock.text,
local: true,
visibility: "PUBLIC",
totalReplies: 0,
updatedAt: "2020-12-03T13:02:00Z",
originComment: null,
inReplyToComment: null,
actor: {
avatar: {
id: "78",
url: "http://someavatar.url.me",
},
id: "89",
domain: null,
preferredUsername: "someauthor",
name: "Some author",
summary: "I am the senate",
},
deletedAt: null,
},
},
};