From 44e5fb9b06c794033204ef1744b54b3b87160082 Mon Sep 17 00:00:00 2001
From: Csaba Tuncsik
Date: Wed, 18 Sep 2024 08:49:41 +0200
Subject: [PATCH] fix(editor): Replace v-html with custom directive to sanitize
html (#10804)
Co-authored-by: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
---
packages/design-system/package.json | 1 -
.../AskAssistantChat/AskAssistantChat.vue | 14 +-
.../__tests__/AskAssistantChat.spec.ts | 21 ++
.../AskAssistantChat.spec.ts.snap | 18 --
.../src/components/N8nActionBox/ActionBox.vue | 4 +-
.../N8nInfoAccordion/InfoAccordion.vue | 4 +-
.../components/N8nInputLabel/InputLabel.vue | 2 +-
.../src/components/N8nMarkdown/Markdown.vue | 2 +-
.../N8nMarkdown/__tests__/Markdown.spec.ts | 21 ++
.../src/components/N8nNotice/Notice.vue | 2 +-
.../N8nNotice/__tests__/Notice.spec.ts | 4 +
.../src/components/N8nSticky/Sticky.vue | 2 +-
.../src/components/N8nTabs/Tabs.vue | 2 +-
.../src/components/N8nTooltip/Tooltip.vue | 2 +-
.../design-system/src/directives/index.ts | 1 +
.../src/directives/n8n-html.test.ts | 45 ++++
.../design-system/src/directives/n8n-html.ts | 37 +++
.../src/components/Error/NodeErrorView.vue | 2 +-
.../src/components/ExpressionEditModal.vue | 2 +-
.../src/components/FeatureComingSoon.vue | 4 +-
.../InlineExpressionTip.vue | 6 +-
.../editor-ui/src/components/InputPanel.vue | 2 +-
.../editor-ui/src/components/MappingPill.vue | 2 +-
packages/editor-ui/src/components/Node.vue | 2 +-
.../Node/NodeCreator/ItemTypes/NodeItem.vue | 2 +-
.../Node/NodeCreator/Modes/ActionsMode.vue | 12 +-
.../Renderers/CategorizedItemsRenderer.vue | 2 +-
.../src/components/ParameterInput.vue | 4 +-
.../src/components/ParameterInputHint.vue | 4 +-
.../src/components/PushConnectionTracker.vue | 2 +-
.../ResourceMapper/MappingModeSelect.vue | 2 +-
packages/editor-ui/src/components/RunData.vue | 4 +-
packages/editor-ui/src/components/RunInfo.vue | 2 +-
.../editor-ui/src/components/TitledList.vue | 2 +-
.../editor-ui/src/components/TriggerPanel.vue | 2 +-
.../editor-ui/src/components/VersionCard.vue | 4 +-
.../src/components/WorkflowActivator.vue | 2 +-
.../src/components/WorkflowSettings.vue | 2 +-
.../src/components/banners/V1Banner.vue | 4 +-
.../banners/__tests__/V1Banner.spec.ts | 8 +-
.../__snapshots__/V1Banner.spec.ts.snap | 226 ++++++++++++++----
.../nodes/render-types/CanvasNodeDefault.vue | 2 +-
.../WorkflowExecutionAnnotationPanel.vue | 2 +-
.../editor-ui/src/views/SettingsLdapView.vue | 2 +-
.../src/views/SettingsLogStreamingView.vue | 8 +-
.../AppsRequiringCredsNotice.vue | 2 +-
.../SetupTemplateFormStep.vue | 2 +-
.../src/views/TemplatesSearchView.vue | 2 +-
pnpm-lock.yaml | 3 -
49 files changed, 379 insertions(+), 130 deletions(-)
create mode 100644 packages/design-system/src/directives/n8n-html.test.ts
create mode 100644 packages/design-system/src/directives/n8n-html.ts
diff --git a/packages/design-system/package.json b/packages/design-system/package.json
index d2cbb0678b..e8f1f99196 100644
--- a/packages/design-system/package.json
+++ b/packages/design-system/package.json
@@ -29,7 +29,6 @@
"@types/sanitize-html": "^2.11.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vitest/coverage-v8": "catalog:frontend",
- "@vue/test-utils": "^2.4.3",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.38",
"sass": "^1.64.1",
diff --git a/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue b/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue
index de2f5e1768..63f4bf9a86 100644
--- a/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue
+++ b/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue
@@ -151,8 +151,7 @@ function growInput() {
/>
-
-
+
@@ -160,19 +159,20 @@ function growInput() {
-
-
-
+
{
it('renders default placeholder chat correctly', () => {
@@ -12,6 +13,11 @@ describe('AskAssistantChat', () => {
});
it('renders chat with messages correctly', () => {
const { container } = render(AskAssistantChat, {
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ },
props: {
user: { firstName: 'Kobi', lastName: 'Dog' },
messages: [
@@ -86,6 +92,11 @@ describe('AskAssistantChat', () => {
});
it('renders streaming chat correctly', () => {
const { container } = render(AskAssistantChat, {
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ },
props: {
user: { firstName: 'Kobi', lastName: 'Dog' },
messages: [
@@ -105,6 +116,11 @@ describe('AskAssistantChat', () => {
});
it('renders end of session chat correctly', () => {
const { container } = render(AskAssistantChat, {
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ },
props: {
user: { firstName: 'Kobi', lastName: 'Dog' },
messages: [
@@ -130,6 +146,11 @@ describe('AskAssistantChat', () => {
});
it('renders message with code snippet', () => {
const { container } = render(AskAssistantChat, {
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ },
props: {
user: { firstName: 'Kobi', lastName: 'Dog' },
messages: [
diff --git a/packages/design-system/src/components/AskAssistantChat/__tests__/__snapshots__/AskAssistantChat.spec.ts.snap b/packages/design-system/src/components/AskAssistantChat/__tests__/__snapshots__/AskAssistantChat.spec.ts.snap
index 8baba648f9..3a587f4fe7 100644
--- a/packages/design-system/src/components/AskAssistantChat/__tests__/__snapshots__/AskAssistantChat.spec.ts.snap
+++ b/packages/design-system/src/components/AskAssistantChat/__tests__/__snapshots__/AskAssistantChat.spec.ts.snap
@@ -129,9 +129,6 @@ exports[`AskAssistantChat > renders chat with messages correctly 1`] = `
-
-
-
@@ -145,7 +142,6 @@ exports[`AskAssistantChat > renders chat with messages correctly 1`] = `
-
@@ -438,7 +434,6 @@ exports[`AskAssistantChat > renders chat with messages correctly 1`] = `
-
Give it to me
@@ -516,7 +511,6 @@ exports[`AskAssistantChat > renders chat with messages correctly 1`] = `
-
Solution steps:
@@ -1060,9 +1054,6 @@ exports[`AskAssistantChat > renders end of session chat correctly 1`] = `
-
-
-
@@ -1076,7 +1067,6 @@ exports[`AskAssistantChat > renders end of session chat correctly 1`] = `
-
@@ -1309,9 +1299,6 @@ exports[`AskAssistantChat > renders message with code snippet 1`] = `
-
-
-
@@ -1325,7 +1312,6 @@ exports[`AskAssistantChat > renders message with code snippet 1`] = `
-
renders streaming chat correctly 1`] = `
-
-
-
@@ -1568,7 +1551,6 @@ exports[`AskAssistantChat > renders streaming chat correctly 1`] = `
-
diff --git a/packages/design-system/src/components/N8nActionBox/ActionBox.vue b/packages/design-system/src/components/N8nActionBox/ActionBox.vue
index c048c3f643..edb877b3d6 100644
--- a/packages/design-system/src/components/N8nActionBox/ActionBox.vue
+++ b/packages/design-system/src/components/N8nActionBox/ActionBox.vue
@@ -37,7 +37,7 @@ withDefaults(defineProps
(), {
-
+
@@ -61,7 +61,7 @@ withDefaults(defineProps(), {
:class="$style.callout"
>
-
+
diff --git a/packages/design-system/src/components/N8nInfoAccordion/InfoAccordion.vue b/packages/design-system/src/components/N8nInfoAccordion/InfoAccordion.vue
index 71830b02dc..e500c73cd6 100644
--- a/packages/design-system/src/components/N8nInfoAccordion/InfoAccordion.vue
+++ b/packages/design-system/src/components/N8nInfoAccordion/InfoAccordion.vue
@@ -75,7 +75,7 @@ const onTooltipClick = (item: string, event: MouseEvent) => emit('tooltipClick',
-
+
@@ -83,7 +83,7 @@ const onTooltipClick = (item: string, event: MouseEvent) => emit('tooltipClick',
-
+
diff --git a/packages/design-system/src/components/N8nInputLabel/InputLabel.vue b/packages/design-system/src/components/N8nInputLabel/InputLabel.vue
index 6c527549ed..bda96c2e21 100644
--- a/packages/design-system/src/components/N8nInputLabel/InputLabel.vue
+++ b/packages/design-system/src/components/N8nInputLabel/InputLabel.vue
@@ -58,7 +58,7 @@ const addTargetBlank = (html: string) =>
-
+
diff --git a/packages/design-system/src/components/N8nMarkdown/Markdown.vue b/packages/design-system/src/components/N8nMarkdown/Markdown.vue
index c953ff9d65..45de891020 100644
--- a/packages/design-system/src/components/N8nMarkdown/Markdown.vue
+++ b/packages/design-system/src/components/N8nMarkdown/Markdown.vue
@@ -202,7 +202,7 @@ const onCheckboxChange = (index: number) => {
@click="onClick"
@mousedown="onMouseDown"
@change="onChange"
- v-html="htmlContent"
+ v-n8n-html="htmlContent"
/>
diff --git a/packages/design-system/src/components/N8nMarkdown/__tests__/Markdown.spec.ts b/packages/design-system/src/components/N8nMarkdown/__tests__/Markdown.spec.ts
index 2c826f5192..ac2faac7b3 100644
--- a/packages/design-system/src/components/N8nMarkdown/__tests__/Markdown.spec.ts
+++ b/packages/design-system/src/components/N8nMarkdown/__tests__/Markdown.spec.ts
@@ -1,10 +1,16 @@
import { render, fireEvent } from '@testing-library/vue';
import N8nMarkdown from '../Markdown.vue';
+import { n8nHtml } from 'n8n-design-system/directives';
describe('components', () => {
describe('N8nMarkdown', () => {
it('should render unchecked checkboxes', () => {
const wrapper = render(N8nMarkdown, {
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ },
props: {
content: '__TODO__\n- [ ] Buy milk\n- [ ] Buy socks\n',
},
@@ -18,6 +24,11 @@ describe('components', () => {
it('should render checked checkboxes', () => {
const wrapper = render(N8nMarkdown, {
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ },
props: {
content: '__TODO__\n- [X] Buy milk\n- [X] Buy socks\n',
},
@@ -31,6 +42,11 @@ describe('components', () => {
it('should toggle checkboxes when clicked', async () => {
const wrapper = render(N8nMarkdown, {
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ },
props: {
content: '__TODO__\n- [ ] Buy milk\n- [ ] Buy socks\n',
},
@@ -50,6 +66,11 @@ describe('components', () => {
it('should render inputs as plain text', () => {
const wrapper = render(N8nMarkdown, {
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ },
props: {
content:
'__TODO__\n- [X] Buy milk\n- \n',
diff --git a/packages/design-system/src/components/N8nNotice/Notice.vue b/packages/design-system/src/components/N8nNotice/Notice.vue
index eb27aa2efd..f630e95e59 100644
--- a/packages/design-system/src/components/N8nNotice/Notice.vue
+++ b/packages/design-system/src/components/N8nNotice/Notice.vue
@@ -73,7 +73,7 @@ const onClick = (event: MouseEvent) => {
:id="`${id}-content`"
:class="showFullContent ? $style['expanded'] : $style['truncated']"
role="region"
- v-html="displayContent"
+ v-n8n-html="displayContent"
/>
diff --git a/packages/design-system/src/components/N8nNotice/__tests__/Notice.spec.ts b/packages/design-system/src/components/N8nNotice/__tests__/Notice.spec.ts
index 84617dab51..180815e536 100644
--- a/packages/design-system/src/components/N8nNotice/__tests__/Notice.spec.ts
+++ b/packages/design-system/src/components/N8nNotice/__tests__/Notice.spec.ts
@@ -1,6 +1,7 @@
import { render } from '@testing-library/vue';
import N8nNotice from '../Notice.vue';
import { N8nText } from 'n8n-design-system/components';
+import { n8nHtml } from 'n8n-design-system/directives';
describe('components', () => {
describe('N8nNotice', () => {
@@ -41,6 +42,9 @@ describe('components', () => {
content: 'Hello world! This is a notice.',
},
global: {
+ directives: {
+ n8nHtml,
+ },
components: {
'n8n-text': N8nText,
},
diff --git a/packages/design-system/src/components/N8nSticky/Sticky.vue b/packages/design-system/src/components/N8nSticky/Sticky.vue
index 4b7dca944a..9e258b4afa 100644
--- a/packages/design-system/src/components/N8nSticky/Sticky.vue
+++ b/packages/design-system/src/components/N8nSticky/Sticky.vue
@@ -116,7 +116,7 @@ const onInputScroll = (event: WheelEvent) => {
-
+
diff --git a/packages/design-system/src/components/N8nTabs/Tabs.vue b/packages/design-system/src/components/N8nTabs/Tabs.vue
index 64277ca863..ff1b142e51 100644
--- a/packages/design-system/src/components/N8nTabs/Tabs.vue
+++ b/packages/design-system/src/components/N8nTabs/Tabs.vue
@@ -89,7 +89,7 @@ const scrollRight = () => scroll(50);
>
-
+
-
+
',
+};
+
+describe('Directive n8n-html', () => {
+ it('should sanitize html', async () => {
+ const { html } = render(TestComponent, {
+ props: {
+ html: 'text malicious ',
+ },
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ },
+ });
+ expect(html()).toBe(
+ '',
+ );
+ });
+
+ it('should not touch safe html', async () => {
+ const { html } = render(TestComponent, {
+ props: {
+ html: 'text safe ',
+ },
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ },
+ });
+ expect(html()).toBe(
+ '',
+ );
+ });
+});
diff --git a/packages/design-system/src/directives/n8n-html.ts b/packages/design-system/src/directives/n8n-html.ts
new file mode 100644
index 0000000000..875905d1d9
--- /dev/null
+++ b/packages/design-system/src/directives/n8n-html.ts
@@ -0,0 +1,37 @@
+import sanitize from 'sanitize-html';
+import type { DirectiveBinding, ObjectDirective } from 'vue';
+
+/**
+ * Custom directive `n8nHtml` to replace v-html from Vue to sanitize content.
+ *
+ * Usage:
+ * In your Vue template, use the directive `v-n8n-html` passing the unsafe HTML.
+ *
+ * Example:
+ * link'">
+ *
+ * Compiles to:
link
+ *
+ * Hint: Do not use it on components
+ * https://vuejs.org/guide/reusability/custom-directives#usage-on-components
+ */
+
+const configuredSanitize = (html: string) =>
+ sanitize(html, {
+ allowedTags: sanitize.defaults.allowedTags.concat(['img', 'input']),
+ allowedAttributes: {
+ ...sanitize.defaults.allowedAttributes,
+ input: ['type', 'id', 'checked'],
+ code: ['class'],
+ a: sanitize.defaults.allowedAttributes.a.concat(['data-*']),
+ },
+ });
+
+export const n8nHtml: ObjectDirective = {
+ beforeMount(el: HTMLElement, binding: DirectiveBinding) {
+ el.innerHTML = configuredSanitize(binding.value);
+ },
+ beforeUpdate(el: HTMLElement, binding: DirectiveBinding) {
+ el.innerHTML = configuredSanitize(binding.value);
+ },
+};
diff --git a/packages/editor-ui/src/components/Error/NodeErrorView.vue b/packages/editor-ui/src/components/Error/NodeErrorView.vue
index 0bc3bf5387..aa0eac4c69 100644
--- a/packages/editor-ui/src/components/Error/NodeErrorView.vue
+++ b/packages/editor-ui/src/components/Error/NodeErrorView.vue
@@ -445,7 +445,7 @@ async function onAskAssistantClick() {
v-if="error.description || error.context?.descriptionKey"
data-test-id="node-error-description"
class="node-error-view__header-description"
- v-html="getErrorDescription()"
+ v-n8n-html="getErrorDescription()"
>
diff --git a/packages/editor-ui/src/components/FeatureComingSoon.vue b/packages/editor-ui/src/components/FeatureComingSoon.vue
index a96b3d0ec3..fd24a35196 100644
--- a/packages/editor-ui/src/components/FeatureComingSoon.vue
+++ b/packages/editor-ui/src/components/FeatureComingSoon.vue
@@ -56,7 +56,7 @@ export default defineComponent({
-
+
@@ -68,7 +68,7 @@ export default defineComponent({
@click:button="openLinkPage"
>
-
+
diff --git a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionTip.vue b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionTip.vue
index 9e0744025c..73f12a0ec3 100644
--- a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionTip.vue
+++ b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionTip.vue
@@ -115,15 +115,15 @@ watchDebounced(
-
+
-
+
-
+
diff --git a/packages/editor-ui/src/components/InputPanel.vue b/packages/editor-ui/src/components/InputPanel.vue
index 7bc38c811e..a9a6dd3909 100644
--- a/packages/editor-ui/src/components/InputPanel.vue
+++ b/packages/editor-ui/src/components/InputPanel.vue
@@ -406,7 +406,7 @@ export default defineComponent({
(), {
diff --git a/packages/editor-ui/src/components/Node.vue b/packages/editor-ui/src/components/Node.vue
index 83efe3b0a7..56ca2cecf8 100644
--- a/packages/editor-ui/src/components/Node.vue
+++ b/packages/editor-ui/src/components/Node.vue
@@ -633,7 +633,7 @@ function openContextMenu(event: MouseEvent, source: 'node-button' | 'node-right-
-
+
diff --git a/packages/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue b/packages/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue
index ebea3cba41..4991638176 100644
--- a/packages/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue
+++ b/packages/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue
@@ -163,7 +163,7 @@ function onCommunityNodeTooltipClick(event: MouseEvent) {
{
data-test-id="actions-panel-no-triggers-callout"
>
{
@@ -293,13 +293,13 @@ onMounted(() => {
slim
data-test-id="actions-panel-activation-callout"
>
-
+
{
:class="$style.resetSearch"
data-test-id="actions-panel-no-matching-actions"
@click="resetSearch"
- v-html="i18n.baseText('nodeCreator.actionsCategory.noMatchingActions')"
+ v-n8n-html="i18n.baseText('nodeCreator.actionsCategory.noMatchingActions')"
/>
@@ -320,7 +320,7 @@ onMounted(() => {
-
+
diff --git a/packages/editor-ui/src/components/ParameterInput.vue b/packages/editor-ui/src/components/ParameterInput.vue
index b5ca14dd88..df63a8b322 100644
--- a/packages/editor-ui/src/components/ParameterInput.vue
+++ b/packages/editor-ui/src/components/ParameterInput.vue
@@ -1391,7 +1391,7 @@ onUpdated(async () => {
@@ -1424,7 +1424,7 @@ onUpdated(async () => {
diff --git a/packages/editor-ui/src/components/ParameterInputHint.vue b/packages/editor-ui/src/components/ParameterInputHint.vue
index 87ce1a92ab..1c28e4e44a 100644
--- a/packages/editor-ui/src/components/ParameterInputHint.vue
+++ b/packages/editor-ui/src/components/ParameterInputHint.vue
@@ -46,13 +46,13 @@ const simplyText = computed(() => {
[$style.highlight]: highlight,
}"
>
-
+
diff --git a/packages/editor-ui/src/components/PushConnectionTracker.vue b/packages/editor-ui/src/components/PushConnectionTracker.vue
index bd4ecc8654..1be8fb5b6a 100644
--- a/packages/editor-ui/src/components/PushConnectionTracker.vue
+++ b/packages/editor-ui/src/components/PushConnectionTracker.vue
@@ -16,7 +16,7 @@ export default defineComponent({
-
+
diff --git a/packages/editor-ui/src/components/ResourceMapper/MappingModeSelect.vue b/packages/editor-ui/src/components/ResourceMapper/MappingModeSelect.vue
index 2c69e15550..ab250a07d3 100644
--- a/packages/editor-ui/src/components/ResourceMapper/MappingModeSelect.vue
+++ b/packages/editor-ui/src/components/ResourceMapper/MappingModeSelect.vue
@@ -131,7 +131,7 @@ defineExpose({
{{ option.name }}
-
+
diff --git a/packages/editor-ui/src/components/RunData.vue b/packages/editor-ui/src/components/RunData.vue
index f85800fa16..348e3a455b 100644
--- a/packages/editor-ui/src/components/RunData.vue
+++ b/packages/editor-ui/src/components/RunData.vue
@@ -1350,7 +1350,7 @@ export default defineComponent({
:class="$style.hintCallout"
:theme="hint.type || 'info'"
>
-
+
{{ tooMuchDataTitle }}
{
data-test-id="node-run-info-stale"
>
diff --git a/packages/editor-ui/src/components/TriggerPanel.vue b/packages/editor-ui/src/components/TriggerPanel.vue
index db8a64736a..0702f34c49 100644
--- a/packages/editor-ui/src/components/TriggerPanel.vue
+++ b/packages/editor-ui/src/components/TriggerPanel.vue
@@ -441,7 +441,7 @@ export default defineComponent({
-
+
{
{{ `${$locale.baseText('versionCard.version')} ${version.name}` }}
-
+
{
diff --git a/packages/editor-ui/src/components/WorkflowSettings.vue b/packages/editor-ui/src/components/WorkflowSettings.vue
index 2d915611ff..1ecac538a6 100644
--- a/packages/editor-ui/src/components/WorkflowSettings.vue
+++ b/packages/editor-ui/src/components/WorkflowSettings.vue
@@ -573,7 +573,7 @@ export default defineComponent({
{{ $locale.baseText('workflowSettings.errorWorkflow') + ':' }}
-
+
diff --git a/packages/editor-ui/src/components/banners/V1Banner.vue b/packages/editor-ui/src/components/banners/V1Banner.vue
index 72077f1a3c..6ea90c712c 100644
--- a/packages/editor-ui/src/components/banners/V1Banner.vue
+++ b/packages/editor-ui/src/components/banners/V1Banner.vue
@@ -17,14 +17,14 @@ const hasOwnerPermission = computed(() => hasPermission(['instanceOwner']));
-
+
-
+
diff --git a/packages/editor-ui/src/components/banners/__tests__/V1Banner.spec.ts b/packages/editor-ui/src/components/banners/__tests__/V1Banner.spec.ts
index d9cbedd9e8..f01ea4c3f6 100644
--- a/packages/editor-ui/src/components/banners/__tests__/V1Banner.spec.ts
+++ b/packages/editor-ui/src/components/banners/__tests__/V1Banner.spec.ts
@@ -1,10 +1,12 @@
-import { render } from '@testing-library/vue';
+import { createComponentRenderer } from '@/__tests__/render';
import V1Banner from '../V1Banner.vue';
import { createPinia, setActivePinia } from 'pinia';
import { useUsersStore } from '@/stores/users.store';
import { ROLE } from '@/constants';
import type { IUser } from '@/Interface';
+const renderComponent = createComponentRenderer(V1Banner);
+
describe('V1 Banner', () => {
let pinia: ReturnType;
let usersStore: ReturnType;
@@ -17,7 +19,7 @@ describe('V1 Banner', () => {
});
it('should render banner', () => {
- const { container } = render(V1Banner);
+ const { container } = renderComponent();
expect(container).toMatchSnapshot();
expect(container.querySelectorAll('a')).toHaveLength(1);
});
@@ -26,7 +28,7 @@ describe('V1 Banner', () => {
usersStore.usersById = { '1': { role: ROLE.Owner } as IUser };
usersStore.currentUserId = '1';
- const { container } = render(V1Banner);
+ const { container } = renderComponent();
expect(container).toMatchSnapshot();
expect(container.querySelectorAll('a')).toHaveLength(2);
});
diff --git a/packages/editor-ui/src/components/banners/__tests__/__snapshots__/V1Banner.spec.ts.snap b/packages/editor-ui/src/components/banners/__tests__/__snapshots__/V1Banner.spec.ts.snap
index afc0523e0c..41daf10517 100644
--- a/packages/editor-ui/src/components/banners/__tests__/__snapshots__/V1Banner.spec.ts.snap
+++ b/packages/editor-ui/src/components/banners/__tests__/__snapshots__/V1Banner.spec.ts.snap
@@ -2,69 +2,209 @@
exports[`V1 Banner > should render banner 1`] = `
-
-
-
- n8n has been updated to version 1, introducing some breaking changes. Please consult the
-
+
- migration guide
-
- for more information.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ n8n has been updated to version 1, introducing some breaking changes. Please consult the
+
+ migration guide
+
+ for more information.
+
+
+
+
+
+
-
+
+
-
+
+
+
+
`;
exports[`V1 Banner > should render banner with dismiss call if user is owner 1`] = `
-
+
+
+
+
`;
diff --git a/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.vue b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.vue
index 85d8dedad7..bc443c1732 100644
--- a/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.vue
+++ b/packages/editor-ui/src/components/canvas/elements/nodes/render-types/CanvasNodeDefault.vue
@@ -96,7 +96,7 @@ function openContextMenu(event: MouseEvent) {
-
+
diff --git a/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.vue b/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.vue
index 1ae4ef0e5c..437cfd8e12 100644
--- a/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.vue
+++ b/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.vue
@@ -179,7 +179,7 @@ const onTagsEditEsc = () => {
-
+
diff --git a/packages/editor-ui/src/views/SettingsLdapView.vue b/packages/editor-ui/src/views/SettingsLdapView.vue
index 8feca49da7..6cab6b1ba5 100644
--- a/packages/editor-ui/src/views/SettingsLdapView.vue
+++ b/packages/editor-ui/src/views/SettingsLdapView.vue
@@ -633,7 +633,7 @@ export default defineComponent({
-
+
diff --git a/packages/editor-ui/src/views/SettingsLogStreamingView.vue b/packages/editor-ui/src/views/SettingsLogStreamingView.vue
index e8abb67e64..0f62a31aee 100644
--- a/packages/editor-ui/src/views/SettingsLogStreamingView.vue
+++ b/packages/editor-ui/src/views/SettingsLogStreamingView.vue
@@ -175,7 +175,7 @@ export default defineComponent({
-
+
@@ -207,7 +207,7 @@ export default defineComponent({
@click:button="addDestination"
>
-
+
@@ -215,7 +215,7 @@ export default defineComponent({
-
+
@@ -225,7 +225,7 @@ export default defineComponent({
@click:button="goToUpgrade"
>
-
+
diff --git a/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/AppsRequiringCredsNotice.vue b/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/AppsRequiringCredsNotice.vue
index a640b49f9d..ac5bfbe63a 100644
--- a/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/AppsRequiringCredsNotice.vue
+++ b/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/AppsRequiringCredsNotice.vue
@@ -28,7 +28,7 @@ const appNodeCounts = computed(() => {
-
+
diff --git a/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/SetupTemplateFormStep.vue b/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/SetupTemplateFormStep.vue
index 2e64dece86..c482d31114 100644
--- a/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/SetupTemplateFormStep.vue
+++ b/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/SetupTemplateFormStep.vue
@@ -95,7 +95,7 @@ const onCredentialModalOpened = () => {
:plural="credentials.usedBy.length"
scope="global"
>
-
+
diff --git a/packages/editor-ui/src/views/TemplatesSearchView.vue b/packages/editor-ui/src/views/TemplatesSearchView.vue
index 61ce8b42e0..af0381d820 100644
--- a/packages/editor-ui/src/views/TemplatesSearchView.vue
+++ b/packages/editor-ui/src/views/TemplatesSearchView.vue
@@ -410,7 +410,7 @@ export default defineComponent({
/>
-
+
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0b9e8ca051..d94ec4a582 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1205,9 +1205,6 @@ importers:
'@vitest/coverage-v8':
specifier: catalog:frontend
version: 1.6.0(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))
- '@vue/test-utils':
- specifier: ^2.4.3
- version: 2.4.3(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.6.2)))(vue@3.4.21(typescript@5.6.2))
autoprefixer:
specifier: ^10.4.19
version: 10.4.19(postcss@8.4.38)