feat: Community nodes details footer update (#17158)

This commit is contained in:
Michael Kret
2025-07-17 14:13:21 +03:00
committed by GitHub
parent 0237d8b8ec
commit 81cd49014a
3 changed files with 81 additions and 13 deletions

View File

@@ -1,13 +1,22 @@
import { fireEvent } from '@testing-library/vue'; import { fireEvent, waitFor } from '@testing-library/vue';
import { VIEWS } from '@/constants'; import { VIEWS } from '@/constants';
import { createTestingPinia } from '@pinia/testing'; import { createTestingPinia } from '@pinia/testing';
import { setActivePinia } from 'pinia'; import { setActivePinia } from 'pinia';
import CommunityNodeFooter from './CommunityNodeFooter.vue'; import CommunityNodeFooter from './CommunityNodeFooter.vue';
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import { vi } from 'vitest'; import { vi } from 'vitest';
import type { PublicInstalledPackage } from 'n8n-workflow';
const getInstalledPackage = vi.fn();
const push = vi.fn(); const push = vi.fn();
const communityNodesStore: {
getInstalledPackage: (packageName: string) => Promise<PublicInstalledPackage>;
} = {
getInstalledPackage,
};
vi.mock('vue-router', async (importOriginal) => { vi.mock('vue-router', async (importOriginal) => {
const actual = await importOriginal(); const actual = await importOriginal();
return { return {
@@ -18,6 +27,10 @@ vi.mock('vue-router', async (importOriginal) => {
}; };
}); });
vi.mock('@/stores/communityNodes.store', () => ({
useCommunityNodesStore: vi.fn(() => communityNodesStore),
}));
describe('CommunityNodeInfo - links & bugs URL', () => { describe('CommunityNodeInfo - links & bugs URL', () => {
beforeEach(() => { beforeEach(() => {
setActivePinia(createTestingPinia()); setActivePinia(createTestingPinia());
@@ -55,4 +68,36 @@ describe('CommunityNodeInfo - links & bugs URL', () => {
expect(queryByText('Manage')).not.toBeInTheDocument(); expect(queryByText('Manage')).not.toBeInTheDocument();
}); });
it('displays "Legacy" when updateAvailable', async () => {
getInstalledPackage.mockResolvedValue({
installedVersion: '1.0.0',
updateAvailable: '1.0.1',
} as PublicInstalledPackage);
const { getByText } = createComponentRenderer(CommunityNodeFooter)({
props: {
packageName: 'n8n-nodes-test',
showManage: false,
},
});
await waitFor(() => expect(getInstalledPackage).toHaveBeenCalled());
expect(getByText('Package version 1.0.0 (Legacy)')).toBeInTheDocument();
});
it('displays "Latest" when not updateAvailable', async () => {
getInstalledPackage.mockResolvedValue({
installedVersion: '1.0.0',
} as PublicInstalledPackage);
const { getByText } = createComponentRenderer(CommunityNodeFooter)({
props: {
packageName: 'n8n-nodes-test',
showManage: false,
},
});
await waitFor(() => expect(getInstalledPackage).toHaveBeenCalled());
expect(getByText('Package version 1.0.0 (Latest)')).toBeInTheDocument();
});
}); });

View File

@@ -3,6 +3,8 @@ import { VIEWS } from '@/constants';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { captureException } from '@sentry/vue'; import { captureException } from '@sentry/vue';
import { useCommunityNodesStore } from '@/stores/communityNodes.store';
import type { PublicInstalledPackage } from 'n8n-workflow';
import { N8nText, N8nLink } from '@n8n/design-system'; import { N8nText, N8nLink } from '@n8n/design-system';
@@ -15,6 +17,7 @@ const props = defineProps<Props>();
const router = useRouter(); const router = useRouter();
const bugsUrl = ref<string>(`https://registry.npmjs.org/${props.packageName}`); const bugsUrl = ref<string>(`https://registry.npmjs.org/${props.packageName}`);
const installedPackage = ref<PublicInstalledPackage | undefined>(undefined);
async function openSettingsPage() { async function openSettingsPage() {
await router.push({ name: VIEWS.COMMUNITY_NODES }); await router.push({ name: VIEWS.COMMUNITY_NODES });
@@ -49,30 +52,45 @@ async function getBugsUrl(packageName: string) {
onMounted(async () => { onMounted(async () => {
if (props.packageName) { if (props.packageName) {
await getBugsUrl(props.packageName); await getBugsUrl(props.packageName);
installedPackage.value = await useCommunityNodesStore().getInstalledPackage(props.packageName);
} }
}); });
</script> </script>
<template> <template>
<div>
<div :class="$style.separator"></div>
<div :class="$style.container"> <div :class="$style.container">
<N8nText v-if="installedPackage" size="small" color="text-light" style="margin-right: auto">
Package version {{ installedPackage.installedVersion }} ({{
installedPackage.updateAvailable ? 'Legacy' : 'Latest'
}})
</N8nText>
<template v-if="props.showManage"> <template v-if="props.showManage">
<N8nLink theme="text" @click="openSettingsPage"> <N8nLink theme="text" @click="openSettingsPage">
<N8nText size="small" color="primary" bold> Manage </N8nText> <N8nText size="small" color="primary" bold> Manage </N8nText>
</N8nLink> </N8nLink>
<N8nText size="small" color="primary" bold>|</N8nText> <N8nText size="small" style="color: var(--color-foreground-base)" bold>|</N8nText>
</template> </template>
<N8nLink theme="text" @click="openIssuesPage"> <N8nLink theme="text" @click="openIssuesPage">
<N8nText size="small" color="primary" bold> Report issue </N8nText> <N8nText size="small" color="primary" bold> Report issue </N8nText>
</N8nLink> </N8nLink>
</div> </div>
</div>
</template> </template>
<style lang="scss" module> <style lang="scss" module>
.container { .container {
display: flex; display: flex;
justify-content: center; justify-content: right;
align-items: center; align-items: center;
gap: var(--spacing-s); gap: var(--spacing-2xs);
padding-bottom: var(--spacing-s); padding: var(--spacing-s);
}
.separator {
height: var(--border-width-base);
background: var(--color-foreground-base);
margin-right: var(--spacing-s);
margin-left: var(--spacing-s);
} }
</style> </style>

View File

@@ -1039,6 +1039,11 @@ function handleWheelEvent(event: WheelEvent) {
@open-connection-node-creator="onOpenConnectionNodeCreator" @open-connection-node-creator="onOpenConnectionNodeCreator"
/> />
<n8n-block-ui :show="blockUI" /> <n8n-block-ui :show="blockUI" />
<CommunityNodeFooter
v-if="openPanel === 'settings' && isCommunityNode"
:package-name="packageName"
:show-manage="useUsersStore().isInstanceOwner"
/>
</div> </div>
</template> </template>