diff --git a/client/src/components/History/SwitchToHistoryLink.test.ts b/client/src/components/History/SwitchToHistoryLink.test.ts index e45a043e5fa3..e4ff8970cafa 100644 --- a/client/src/components/History/SwitchToHistoryLink.test.ts +++ b/client/src/components/History/SwitchToHistoryLink.test.ts @@ -1,11 +1,12 @@ import { createTestingPinia } from "@pinia/testing"; import { getLocalVue } from "@tests/jest/helpers"; -import { shallowMount } from "@vue/test-utils"; +import { mount } from "@vue/test-utils"; import axios from "axios"; import MockAdapter from "axios-mock-adapter"; import flushPromises from "flush-promises"; -import { type HistorySummary } from "@/api"; +import { type HistorySummaryExtended } from "@/api"; +import { useUserStore } from "@/stores/userStore"; import SwitchToHistoryLink from "./SwitchToHistoryLink.vue"; @@ -15,13 +16,13 @@ const selectors = { historyLink: ".history-link", } as const; -function mountSwitchToHistoryLinkForHistory(history: HistorySummary) { +function mountSwitchToHistoryLinkForHistory(history: HistorySummaryExtended) { const pinia = createTestingPinia(); const axiosMock = new MockAdapter(axios); axiosMock.onGet(`/api/histories/${history.id}`).reply(200, history); - const wrapper = shallowMount(SwitchToHistoryLink as object, { + const wrapper = mount(SwitchToHistoryLink as object, { propsData: { historyId: history.id, }, @@ -31,10 +32,19 @@ function mountSwitchToHistoryLinkForHistory(history: HistorySummary) { FontAwesomeIcon: true, }, }); + + const userStore = useUserStore(); + userStore.currentUser = { + email: "email", + id: "user_id", + tags_used: [], + isAnonymous: false, + total_disk_usage: 0, + }; return wrapper; } -async function expectOptionForHistory(option: string, history: HistorySummary) { +async function expectOptionForHistory(option: string, history: HistorySummaryExtended) { const wrapper = mountSwitchToHistoryLinkForHistory(history); // Wait for the history to be loaded @@ -52,7 +62,7 @@ describe("SwitchToHistoryLink", () => { deleted: false, archived: false, purged: false, - } as HistorySummary; + } as HistorySummaryExtended; const wrapper = mountSwitchToHistoryLinkForHistory(history); expect(wrapper.find(selectors.historyLink).exists()).toBe(false); @@ -72,7 +82,8 @@ describe("SwitchToHistoryLink", () => { deleted: false, purged: false, archived: false, - } as HistorySummary; + user_id: "user_id", + } as HistorySummaryExtended; await expectOptionForHistory("Switch", history); }); @@ -83,7 +94,8 @@ describe("SwitchToHistoryLink", () => { deleted: false, purged: true, archived: false, - } as HistorySummary; + user_id: "user_id", + } as HistorySummaryExtended; await expectOptionForHistory("View", history); }); @@ -94,7 +106,23 @@ describe("SwitchToHistoryLink", () => { deleted: false, purged: false, archived: true, - } as HistorySummary; + user_id: "user_id", + } as HistorySummaryExtended; await expectOptionForHistory("View", history); }); + + it("should view in new tab when the history is accessible", async () => { + const history = { + id: "public-history-id", + name: "History Published", + deleted: false, + purged: false, + archived: false, + published: true, + user_id: "other_user_id", + } as HistorySummaryExtended; + await expectOptionForHistory("View", history); + }); + + // if the history is inaccessible, the HistorySummary would never be fetched in the first place }); diff --git a/client/src/components/History/SwitchToHistoryLink.vue b/client/src/components/History/SwitchToHistoryLink.vue index 4087ccecd27c..58dca75e389e 100644 --- a/client/src/components/History/SwitchToHistoryLink.vue +++ b/client/src/components/History/SwitchToHistoryLink.vue @@ -6,8 +6,11 @@ import { BLink } from "bootstrap-vue"; import { computed } from "vue"; import { useRouter } from "vue-router/composables"; -import { HistorySummary } from "@/api"; +import { type HistorySummary, userOwnsHistory } from "@/api"; +import { Toast } from "@/composables/toast"; import { useHistoryStore } from "@/stores/historyStore"; +import { useUserStore } from "@/stores/userStore"; +import { errorMessageAsString } from "@/utils/simple-error"; import LoadingSpan from "@/components/LoadingSpan.vue"; @@ -15,6 +18,7 @@ library.add(faArchive, faBurn); const router = useRouter(); const historyStore = useHistoryStore(); +const userStore = useUserStore(); interface Props { historyId: string; @@ -25,7 +29,13 @@ const props = defineProps(); const history = computed(() => historyStore.getHistoryById(props.historyId)); -const canSwitch = computed(() => !!history.value && !history.value.archived && !history.value.purged); +const canSwitch = computed( + () => + !!history.value && + !history.value.archived && + !history.value.purged && + userOwnsHistory(userStore.currentUser, history.value) +); const actionText = computed(() => { if (canSwitch.value) { @@ -37,12 +47,16 @@ const actionText = computed(() => { return "View in new tab"; }); -function onClick(history: HistorySummary) { +async function onClick(history: HistorySummary) { if (canSwitch.value) { if (props.filters) { historyStore.applyFilters(history.id, props.filters); } else { - historyStore.setCurrentHistory(history.id); + try { + await historyStore.setCurrentHistory(history.id); + } catch (error) { + Toast.error(errorMessageAsString(error)); + } } return; } diff --git a/client/src/stores/historyStore.ts b/client/src/stores/historyStore.ts index 38c430ead734..b15227ddde67 100644 --- a/client/src/stores/historyStore.ts +++ b/client/src/stores/historyStore.ts @@ -98,9 +98,13 @@ export const useHistoryStore = defineStore("historyStore", () => { }); async function setCurrentHistory(historyId: string) { - const currentHistory = (await setCurrentHistoryOnServer(historyId)) as HistoryDevDetailed; - selectHistory(currentHistory); - setFilterText(historyId, ""); + try { + const currentHistory = (await setCurrentHistoryOnServer(historyId)) as HistoryDevDetailed; + selectHistory(currentHistory); + setFilterText(historyId, ""); + } catch (error) { + rethrowSimple(error); + } } function setCurrentHistoryId(historyId: string) {