diff --git a/packages/editor-ui/src/components/CredentialCard.test.ts b/packages/editor-ui/src/components/CredentialCard.test.ts index 9ca589923..7b3abc6ac 100644 --- a/packages/editor-ui/src/components/CredentialCard.test.ts +++ b/packages/editor-ui/src/components/CredentialCard.test.ts @@ -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'); + }); }); diff --git a/packages/editor-ui/src/components/CredentialCard.vue b/packages/editor-ui/src/components/CredentialCard.vue index 33ffefa37..958f236f1 100644 --- a/packages/editor-ui/src/components/CredentialCard.vue +++ b/packages/editor-ui/src/components/CredentialCard.vue @@ -138,16 +138,19 @@ function moveResource() {
{{ credentialType.displayName }} | {{ $locale.baseText('credentials.item.updated') }} | + >{{ locale.baseText('credentials.item.updated') }} | {{ $locale.baseText('credentials.item.created') }} {{ formattedCreatedAtDate }} + >{{ locale.baseText('credentials.item.created') }} {{ formattedCreatedAtDate }}
diff --git a/packages/editor-ui/src/components/CredentialEdit/CredentialEdit.vue b/packages/editor-ui/src/components/CredentialEdit/CredentialEdit.vue index 510176277..856f0c931 100644 --- a/packages/editor-ui/src/components/CredentialEdit/CredentialEdit.vue +++ b/packages/editor-ui/src/components/CredentialEdit/CredentialEdit.vue @@ -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'), diff --git a/packages/editor-ui/src/components/WorkflowCard.test.ts b/packages/editor-ui/src/components/WorkflowCard.test.ts index 735b20ada..c97cb8aaa 100644 --- a/packages/editor-ui/src/components/WorkflowCard.test.ts +++ b/packages/editor-ui/src/components/WorkflowCard.test.ts @@ -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(); @@ -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'); + }); }); diff --git a/packages/editor-ui/src/components/WorkflowCard.vue b/packages/editor-ui/src/components/WorkflowCard.vue index f929bea62..cc0713e2b 100644 --- a/packages/editor-ui/src/components/WorkflowCard.vue +++ b/packages/editor-ui/src/components/WorkflowCard.vue @@ -236,16 +236,19 @@ function moveResource() {
{{ $locale.baseText('workflows.item.updated') }} + >{{ locale.baseText('workflows.item.updated') }} | {{ $locale.baseText('workflows.item.created') }} {{ formattedCreatedAtDate }} + >{{ locale.baseText('workflows.item.created') }} {{ formattedCreatedAtDate }} ; @@ -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; let workflowsStore: ReturnType; @@ -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(); + }); }); diff --git a/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionsPreview.vue b/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionsPreview.vue index 8fe995853..081c8a0d9 100644 --- a/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionsPreview.vue +++ b/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionsPreview.vue @@ -120,7 +120,13 @@ function onRetryButtonBlur(event: FocusEvent) { {{ locale.baseText('executionDetails.runningMessage') }} - + {{ locale.baseText('executionsList.stopExecution') }}
diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index d53c370db..2b5ba19c6 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -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.", @@ -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", diff --git a/packages/editor-ui/src/views/CredentialsView.test.ts b/packages/editor-ui/src/views/CredentialsView.test.ts index f2338f9d9..bc2b47a1a 100644 --- a/packages/editor-ui/src/views/CredentialsView.test.ts +++ b/packages/editor-ui/src/views/CredentialsView.test.ts @@ -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, + ); + }); }); }); diff --git a/packages/editor-ui/src/views/CredentialsView.vue b/packages/editor-ui/src/views/CredentialsView.vue index 28f7e5655..31040e2b3 100644 --- a/packages/editor-ui/src/views/CredentialsView.vue +++ b/packages/editor-ui/src/views/CredentialsView.vue @@ -57,6 +57,7 @@ export default defineComponent({ scopes: credential.scopes, type: credential.type, sharedWithProjects: credential.sharedWithProjects, + readOnly: !getResourcePermissions(credential.scopes).credential.update, })); }, allCredentialTypes(): ICredentialType[] { @@ -179,7 +180,12 @@ export default defineComponent({