Skip to content

Commit

Permalink
fix(editor): Follow up fixes and improvements to viewer role (#10684)
Browse files Browse the repository at this point in the history
  • Loading branch information
r00gm authored Sep 10, 2024
1 parent efa5573 commit 63548e6
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 34 deletions.
6 changes: 6 additions & 0 deletions packages/editor-ui/src/components/CredentialCard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,10 @@ describe('CredentialCard', () => {
}
expect(actions).toHaveTextContent('Move');
});

it('should set readOnly variant based on prop', () => {
const { getByRole } = renderComponent({ props: { readOnly: true } });
const heading = getByRole('heading');
expect(heading).toHaveTextContent('Read only');
});
});
7 changes: 5 additions & 2 deletions packages/editor-ui/src/components/CredentialCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,19 @@ function moveResource() {
<template #header>
<n8n-heading tag="h2" bold :class="$style.cardHeading">
{{ data.name }}
<N8nBadge v-if="readOnly" class="ml-3xs" theme="tertiary" bold>
{{ locale.baseText('credentials.item.readonly') }}
</N8nBadge>
</n8n-heading>
</template>
<div :class="$style.cardDescription">
<n8n-text color="text-light" size="small">
<span v-if="credentialType">{{ credentialType.displayName }} | </span>
<span v-show="data"
>{{ $locale.baseText('credentials.item.updated') }} <TimeAgo :date="data.updatedAt" /> |
>{{ locale.baseText('credentials.item.updated') }} <TimeAgo :date="data.updatedAt" /> |
</span>
<span v-show="data"
>{{ $locale.baseText('credentials.item.created') }} {{ formattedCreatedAtDate }}
>{{ locale.baseText('credentials.item.created') }} {{ formattedCreatedAtDate }}
</span>
</n8n-text>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ async function beforeClose() {
},
);
keepEditing = confirmAction === MODAL_CONFIRM;
} else if (isOAuthType.value && !isOAuthConnected.value) {
} else if (credentialPermissions.value.update && isOAuthType.value && !isOAuthConnected.value) {
const confirmAction = await message.confirm(
i18n.baseText('credentialEdit.credentialEdit.confirmMessage.beforeClose2.message'),
i18n.baseText('credentialEdit.credentialEdit.confirmMessage.beforeClose2.headline'),
Expand Down
10 changes: 9 additions & 1 deletion packages/editor-ui/src/components/WorkflowCard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ describe('WorkflowCard', () => {
it('should render a card with the workflow name and open workflow clicking on it', async () => {
const data = createWorkflow();
const { getByRole } = renderComponent({ props: { data } });
const cardTitle = getByRole('heading', { level: 2, name: data.name });
const cardTitle = getByRole('heading', { level: 2, name: new RegExp(data.name) });

expect(cardTitle).toBeInTheDocument();

Expand Down Expand Up @@ -166,4 +166,12 @@ describe('WorkflowCard', () => {
}
expect(actions).toHaveTextContent('Move');
});

it('should show Read only mode', async () => {
const data = createWorkflow();
const { getByRole } = renderComponent({ props: { data } });

const heading = getByRole('heading');
expect(heading).toHaveTextContent('Read only');
});
});
7 changes: 5 additions & 2 deletions packages/editor-ui/src/components/WorkflowCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -236,16 +236,19 @@ function moveResource() {
<template #header>
<n8n-heading tag="h2" bold :class="$style.cardHeading" data-test-id="workflow-card-name">
{{ data.name }}
<N8nBadge v-if="!workflowPermissions.update" class="ml-3xs" theme="tertiary" bold>
{{ locale.baseText('workflows.item.readonly') }}
</N8nBadge>
</n8n-heading>
</template>
<div :class="$style.cardDescription">
<n8n-text color="text-light" size="small">
<span v-show="data"
>{{ $locale.baseText('workflows.item.updated') }}
>{{ locale.baseText('workflows.item.updated') }}
<TimeAgo :date="String(data.updatedAt)" /> |
</span>
<span v-show="data" class="mr-2xs"
>{{ $locale.baseText('workflows.item.created') }} {{ formattedCreatedAtDate }}
>{{ locale.baseText('workflows.item.created') }} {{ formattedCreatedAtDate }}
</span>
<span
v-if="settingsStore.areTagsEnabled && data.tags && data.tags.length > 0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { describe, expect } from 'vitest';
import { render } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import { faker } from '@faker-js/faker';
import { createRouter, createWebHistory } from 'vue-router';
import { createPinia, PiniaVuePlugin, setActivePinia } from 'pinia';
import { createRouter, createWebHistory, RouterLink } from 'vue-router';
import { createPinia, setActivePinia } from 'pinia';
import { randomInt, type ExecutionSummary } from 'n8n-workflow';
import { useSettingsStore } from '@/stores/settings.store';
import WorkflowExecutionsPreview from '@/components/executions/workflow/WorkflowExecutionsPreview.vue';
import { EnterpriseEditionFeature, VIEWS } from '@/constants';
import { i18nInstance, I18nPlugin } from '@/plugins/i18n';
import { FontAwesomePlugin } from '@/plugins/icons';
import { GlobalComponentsPlugin } from '@/plugins/components';
import { useWorkflowsStore } from '@/stores/workflows.store';
import type { ExecutionSummaryWithScopes, IWorkflowDb } from '@/Interface';
import { createComponentRenderer } from '@/__tests__/render';

let pinia: ReturnType<typeof createPinia>;

Expand Down Expand Up @@ -64,6 +61,19 @@ const executionDataFactory = (): ExecutionSummaryWithScopes => ({
scopes: ['workflow:update'],
});

const renderComponent = createComponentRenderer(WorkflowExecutionsPreview, {
global: {
stubs: {
// UN STUB router-link
'router-link': RouterLink,
},
plugins: [router],
mocks: {
$route,
},
},
});

describe('WorkflowExecutionsPreview.vue', () => {
let settingsStore: ReturnType<typeof useSettingsStore>;
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
Expand Down Expand Up @@ -93,30 +103,26 @@ describe('WorkflowExecutionsPreview.vue', () => {

vi.spyOn(workflowsStore, 'getWorkflowById').mockReturnValue({ scopes } as IWorkflowDb);

// Not using createComponentRenderer helper here because this component should not stub `router-link`
const { getByTestId } = render(WorkflowExecutionsPreview, {
props: {
execution: executionData,
},
global: {
plugins: [
I18nPlugin,
i18nInstance,
PiniaVuePlugin,
FontAwesomePlugin,
GlobalComponentsPlugin,
pinia,
router,
],
mocks: {
$route,
},
},
});
const { getByTestId } = renderComponent({ props: { execution: executionData } });

await userEvent.click(getByTestId('execution-debug-button'));

expect(router.currentRoute.value.path).toBe(path);
},
);

it('disables the stop execution button when the user cannot update', () => {
settingsStore.settings.enterprise = {
...(settingsStore.settings.enterprise ?? {}),
};
vi.spyOn(workflowsStore, 'getWorkflowById').mockReturnValue({
scopes: undefined,
} as IWorkflowDb);

const { getByTestId } = renderComponent({
props: { execution: { ...executionData, status: 'running' } },
});

expect(getByTestId('stop-execution')).toBeDisabled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,13 @@ function onRetryButtonBlur(event: FocusEvent) {
<N8nText :class="$style.runningMessage" color="text-light">
{{ locale.baseText('executionDetails.runningMessage') }}
</N8nText>
<N8nButton class="mt-l" type="tertiary" @click="handleStopClick">
<N8nButton
data-test-id="stop-execution"
class="mt-l"
type="tertiary"
:disabled="!workflowPermissions.execute"
@click="handleStopClick"
>
{{ locale.baseText('executionsList.stopExecution') }}
</N8nButton>
</div>
Expand Down
2 changes: 2 additions & 0 deletions packages/editor-ui/src/plugins/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@
"credentials.item.updated": "Last updated",
"credentials.item.created": "Created",
"credentials.item.owner": "Owner",
"credentials.item.readonly": "Read only",
"credentials.search.placeholder": "Search credentials...",
"credentials.filters.type": "Type",
"credentials.filters.active": "Some credentials may be hidden since filters are applied.",
Expand Down Expand Up @@ -2206,6 +2207,7 @@
"workflows.item.move": "Move",
"workflows.item.updated": "Last updated",
"workflows.item.created": "Created",
"workflows.item.readonly": "Read only",
"workflows.search.placeholder": "Search workflows...",
"workflows.filters": "Filters",
"workflows.filters.tags": "Tags",
Expand Down
35 changes: 35 additions & 0 deletions packages/editor-ui/src/views/CredentialsView.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,40 @@ describe('CredentialsView', () => {
null,
);
});

it('should disable cards based on permissions', () => {
vi.spyOn(credentialsStore, 'allCredentials', 'get').mockReturnValue([
{
id: '1',
name: 'test',
type: 'test',
createdAt: '2021-05-05T00:00:00Z',
updatedAt: '2021-05-05T00:00:00Z',
scopes: ['credential:update'],
},
{
id: '2',
name: 'test2',
type: 'test2',
createdAt: '2021-05-05T00:00:00Z',
updatedAt: '2021-05-05T00:00:00Z',
},
]);

renderComponent();
expect(ResourcesListLayout.setup).toHaveBeenCalledWith(
expect.objectContaining({
resources: [
expect.objectContaining({
readOnly: false,
}),
expect.objectContaining({
readOnly: true,
}),
],
}),
null,
);
});
});
});
8 changes: 7 additions & 1 deletion packages/editor-ui/src/views/CredentialsView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export default defineComponent({
scopes: credential.scopes,
type: credential.type,
sharedWithProjects: credential.sharedWithProjects,
readOnly: !getResourcePermissions(credential.scopes).credential.update,
}));
},
allCredentialTypes(): ICredentialType[] {
Expand Down Expand Up @@ -179,7 +180,12 @@ export default defineComponent({
</div>
</template>
<template #default="{ data }">
<CredentialCard data-test-id="resources-list-item" class="mb-2xs" :data="data" />
<CredentialCard
data-test-id="resources-list-item"
class="mb-2xs"
:data="data"
:read-only="data.readOnly"
/>
</template>
<template #filters="{ setKeyValue }">
<div class="mb-s">
Expand Down

0 comments on commit 63548e6

Please sign in to comment.