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 @@
@@ -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,
+ },
+ },
+};