diff --git a/js/src/components/Comment/CommentTree.vue b/js/src/components/Comment/CommentTree.vue index e3639bf36..592e23cd9 100644 --- a/js/src/components/Comment/CommentTree.vue +++ b/js/src/components/Comment/CommentTree.vue @@ -26,9 +26,12 @@

- {{ - $t("Post a comment") - }} + {{ $t("Post a comment") }}
@@ -37,6 +40,9 @@ $t("The organiser has chosen to close comments.") }} +

+ {{ $t("Loading…") }} +

key; + +const eventData = { + id: "1", + uuid: "e37910ea-fd5a-4756-7634-00971f3f4107", + options: { + commentModeration: CommentModeration.ALLOW_ALL, + }, +}; +describe("CommentTree", () => { + let wrapper: Wrapper; + let mockClient: MockApolloClient; + let apolloProvider; + let requestHandlers: Record; + + 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 + + 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 + + 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); + } + } + }); +}); diff --git a/js/tests/unit/specs/components/Comment/__snapshots__/CommentTree.spec.ts.snap b/js/tests/unit/specs/components/Comment/__snapshots__/CommentTree.spec.ts.snap new file mode 100644 index 000000000..4ea79739c --- /dev/null +++ b/js/tests/unit/specs/components/Comment/__snapshots__/CommentTree.spec.ts.snap @@ -0,0 +1,26 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CommentTree renders a comment tree 1`] = ` +
+ The organiser has chosen to close comments. + +

+ Loading… +

+ +
+
+`; + +exports[`CommentTree renders a comment tree 2`] = ` +
+ The organiser has chosen to close comments. + + + + + + + +
+`; diff --git a/js/tests/unit/specs/components/Report/ReportCard.spec.ts b/js/tests/unit/specs/components/Report/ReportCard.spec.ts new file mode 100644 index 000000000..4fde36067 --- /dev/null +++ b/js/tests/unit/specs/components/Report/ReportCard.spec.ts @@ -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 = {}) => { + 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}" + ); + }); +}); diff --git a/js/tests/unit/specs/components/Report/ReportModal.spec.ts b/js/tests/unit/specs/components/Report/ReportModal.spec.ts new file mode 100644 index 000000000..0f3944b80 --- /dev/null +++ b/js/tests/unit/specs/components/Report/ReportModal.spec.ts @@ -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 = {}) => { + 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 comment 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 comment 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!"); + }); +}); diff --git a/js/tests/unit/specs/mocks/event.ts b/js/tests/unit/specs/mocks/event.ts new file mode 100644 index 000000000..b9aeebd6b --- /dev/null +++ b/js/tests/unit/specs/mocks/event.ts @@ -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, + }, + }, +};