1
1
mirror of https://github.com/n8n-io/n8n.git synced 2024-09-11 13:15:28 +03:00

refactor(editor): Fix type errors (no-changelog) (#9584)

This commit is contained in:
Csaba Tuncsik 2024-06-03 14:24:26 +02:00 committed by GitHub
parent c1b1ee57b1
commit 4eb6edc73a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 83 additions and 62 deletions

View File

@ -81,13 +81,10 @@ export default defineComponent({
return this.workflowsStore.workflow;
},
currentWorkflow(): string {
return this.$route.params.name || this.workflowsStore.workflowId;
return String(this.$route.params.name || this.workflowsStore.workflowId);
},
onWorkflowPage(): boolean {
return (
this.$route.meta &&
(this.$route.meta.nodeView || this.$route.meta.keepWorkflowAlive === true)
);
return !!(this.$route.meta.nodeView || this.$route.meta.keepWorkflowAlive);
},
readOnly(): boolean {
return this.sourceControlStore.preferences.branchReadOnly;
@ -124,11 +121,15 @@ export default defineComponent({
this.activeHeaderTab = MAIN_HEADER_TABS.WORKFLOW;
}
if (to.params.name !== 'new') {
if (to.params.name !== 'new' && typeof to.params.name === 'string') {
this.workflowToReturnTo = to.params.name;
}
if (from?.name === VIEWS.EXECUTION_PREVIEW && to.params.name === from.params.name) {
if (
from?.name === VIEWS.EXECUTION_PREVIEW &&
to.params.name === from.params.name &&
typeof from.params.executionId === 'string'
) {
this.executionToReturnTo = from.params.executionId;
}
},

View File

@ -3,7 +3,6 @@ import { createComponentRenderer } from '@/__tests__/render';
import { STORES, WORKFLOW_SHARE_MODAL_KEY } from '@/constants';
import { createTestingPinia } from '@pinia/testing';
import userEvent from '@testing-library/user-event';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useUIStore } from '@/stores/ui.store';
vi.mock('vue-router', async () => {
@ -48,12 +47,10 @@ const renderComponent = createComponentRenderer(WorkflowDetails, {
pinia: createTestingPinia({ initialState }),
});
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
let uiStore: ReturnType<typeof useUIStore>;
describe('WorkflowDetails', () => {
beforeEach(() => {
workflowsStore = useWorkflowsStore();
uiStore = useUIStore();
});
it('renders workflow name and tags', async () => {
@ -101,8 +98,6 @@ describe('WorkflowDetails', () => {
});
it('opens share modal on share button click', async () => {
vi.spyOn(workflowsStore, 'getWorkflowById', 'get').mockReturnValue(() => ({}));
const openModalSpy = vi.spyOn(uiStore, 'openModalWithData');
const { getByTestId } = renderComponent({

View File

@ -67,8 +67,8 @@
>
<div :class="{ [$style.avatar]: true, ['clickable']: isCollapsed }">
<n8n-avatar
:first-name="usersStore.currentUser.firstName"
:last-name="usersStore.currentUser.lastName"
:first-name="usersStore.currentUser?.firstName"
:last-name="usersStore.currentUser?.lastName"
size="small"
/>
</div>
@ -88,7 +88,7 @@
:class="{ ['ml-2xs']: true, [$style.userName]: true, [$style.expanded]: fullyExpanded }"
>
<n8n-text size="small" :bold="true" color="text-dark">{{
usersStore.currentUser.fullName
usersStore.currentUser?.fullName
}}</n8n-text>
</div>
<div :class="{ [$style.userActions]: true, [$style.expanded]: fullyExpanded }">
@ -142,7 +142,7 @@ export default defineComponent({
ProjectNavigation,
},
mixins: [userHelpers],
setup(props, ctx) {
setup() {
const externalHooks = useExternalHooks();
const { callDebounced } = useDebounce();
@ -435,11 +435,11 @@ export default defineComponent({
findFirstAccessibleSettingsRoute() {
const settingsRoutes = this.$router
.getRoutes()
.find((route) => route.path === '/settings')!
.children.map((route) => route.name ?? '');
.find((route) => route.path === '/settings')
?.children.map((route) => route.name ?? '');
let defaultSettingsRoute = { name: VIEWS.USERS_SETTINGS };
for (const route of settingsRoutes) {
for (const route of settingsRoutes ?? []) {
if (this.canUserAccessRouteByName(route.toString())) {
defaultSettingsRoute = {
name: route.toString() as VIEWS,

View File

@ -193,11 +193,6 @@ export default defineComponent({
openUpdatesPanel() {
this.uiStore.openModal(VERSIONS_MODAL_KEY);
},
async navigateTo(routeName: (typeof VIEWS)[keyof typeof VIEWS]) {
if (this.$router.currentRoute.name !== routeName) {
await this.$router.push({ name: routeName });
}
},
async handleSelect(key: string) {
switch (key) {
case 'users': // Fakedoor feature added via hooks when user management is disabled on cloud

View File

@ -42,9 +42,9 @@ const files = ref<SourceControlAggregatedFile[]>(
const commitMessage = ref('');
const loading = ref(true);
const context = ref<'workflow' | 'workflows' | 'credentials' | string>('');
const context = ref<'workflow' | 'workflows' | 'credentials' | ''>('');
const statusToBadgeThemeMap = {
const statusToBadgeThemeMap: Record<string, string> = {
created: 'success',
deleted: 'danger',
modified: 'warning',
@ -64,7 +64,7 @@ const workflowId = computed(() => {
});
const sortedFiles = computed(() => {
const statusPriority = {
const statusPriority: Record<string, number> = {
modified: 1,
renamed: 2,
created: 3,
@ -86,7 +86,11 @@ const sortedFiles = computed(() => {
return 1;
}
return a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0;
return (a.updatedAt ?? 0) < (b.updatedAt ?? 0)
? 1
: (a.updatedAt ?? 0) > (b.updatedAt ?? 0)
? -1
: 0;
});
});
@ -151,13 +155,18 @@ function getContext() {
return '';
}
function getStagedFilesByContext(files: SourceControlAggregatedFile[]): Record<string, boolean> {
const stagedFiles = files.reduce((acc, file) => {
acc[file.file] = false;
return acc;
}, {});
function getStagedFilesByContext(
filesByContext: SourceControlAggregatedFile[],
): Record<string, boolean> {
const stagedFiles = filesByContext.reduce(
(acc, file) => {
acc[file.file] = false;
return acc;
},
{} as Record<string, boolean>,
);
files.forEach((file) => {
filesByContext.forEach((file) => {
if (defaultStagedFileTypes.includes(file.type)) {
stagedFiles[file.file] = true;
}
@ -184,13 +193,13 @@ function close() {
}
function renderUpdatedAt(file: SourceControlAggregatedFile) {
const currentYear = new Date().getFullYear();
const currentYear = new Date().getFullYear().toString();
return i18n.baseText('settings.sourceControl.lastUpdated', {
interpolate: {
date: dateformat(
file.updatedAt,
`d mmm${file.updatedAt.startsWith(currentYear) ? '' : ', yyyy'}`,
`d mmm${file.updatedAt?.startsWith(currentYear) ? '' : ', yyyy'}`,
),
time: dateformat(file.updatedAt, 'HH:MM'),
},
@ -227,6 +236,22 @@ async function commitAndPush() {
loadingService.stopLoading();
}
}
function getStatusText(file: SourceControlAggregatedFile): string {
if (file.status === 'deleted') {
return i18n.baseText('settings.sourceControl.status.deleted');
}
if (file.status === 'created') {
return i18n.baseText('settings.sourceControl.status.created');
}
if (file.status === 'modified') {
return i18n.baseText('settings.sourceControl.status.modified');
}
return i18n.baseText('settings.sourceControl.status.renamed');
}
</script>
<template>
@ -296,7 +321,7 @@ async function commitAndPush() {
Current workflow
</n8n-badge>
<n8n-badge :theme="statusToBadgeThemeMap[file.status] || 'default'">
{{ i18n.baseText(`settings.sourceControl.status.${file.status}`) }}
{{ getStatusText(file) }}
</n8n-badge>
</div>
</div>

View File

@ -8,7 +8,8 @@
<div :class="$style.cardDescription">
<n8n-text color="text-light" size="small">
<span v-show="data"
>{{ $locale.baseText('workflows.item.updated') }} <TimeAgo :date="data.updatedAt" /> |
>{{ $locale.baseText('workflows.item.updated') }}
<TimeAgo :date="String(data.updatedAt)" /> |
</span>
<span v-show="data" class="mr-2xs"
>{{ $locale.baseText('workflows.item.created') }} {{ formattedCreatedAtDate }}
@ -52,7 +53,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import type { PropType } from 'vue';
import type { IWorkflowDb, IUser, ITag } from '@/Interface';
import type { IWorkflowDb, IUser } from '@/Interface';
import { DUPLICATE_MODAL_KEY, MODAL_CONFIRM, VIEWS, WORKFLOW_SHARE_MODAL_KEY } from '@/constants';
import { useMessage } from '@/composables/useMessage';
import { useToast } from '@/composables/useToast';
@ -149,11 +150,11 @@ export default defineComponent({
return actions;
},
formattedCreatedAtDate(): string {
const currentYear = new Date().getFullYear();
const currentYear = new Date().getFullYear().toString();
return dateformat(
this.data.createdAt,
`d mmmm${this.data.createdAt.startsWith(currentYear) ? '' : ', yyyy'}`,
`d mmmm${String(this.data.createdAt).startsWith(currentYear) ? '' : ', yyyy'}`,
);
},
},
@ -191,7 +192,9 @@ export default defineComponent({
data: {
id: this.data.id,
name: this.data.name,
tags: (this.data.tags || []).map((tag: ITag) => tag.id),
tags: (this.data.tags ?? []).map((tag) =>
typeof tag !== 'string' && 'id' in tag ? tag.id : tag,
),
},
});
} else if (action === WORKFLOW_LIST_ITEM_ACTIONS.SHARE) {

View File

@ -279,7 +279,7 @@
<el-switch
ref="inputField"
:disabled="readOnlyEnv"
:model-value="workflowSettings.executionTimeout > -1"
:model-value="(workflowSettings.executionTimeout ?? -1) > -1"
active-color="#13ce66"
data-test-id="workflow-settings-timeout-workflow"
@update:model-value="toggleTimeout"
@ -288,7 +288,7 @@
</el-col>
</el-row>
<div
v-if="workflowSettings.executionTimeout > -1"
v-if="(workflowSettings.executionTimeout ?? -1) > -1"
data-test-id="workflow-settings-timeout-form"
>
<el-row>
@ -306,7 +306,7 @@
:disabled="readOnlyEnv"
:model-value="timeoutHMS.hours"
:min="0"
@update:model-value="(value) => setTimeout('hours', value)"
@update:model-value="(value: string) => setTimeout('hours', value)"
>
<template #append>{{ $locale.baseText('workflowSettings.hours') }}</template>
</n8n-input>
@ -317,7 +317,7 @@
:model-value="timeoutHMS.minutes"
:min="0"
:max="60"
@update:model-value="(value) => setTimeout('minutes', value)"
@update:model-value="(value: string) => setTimeout('minutes', value)"
>
<template #append>{{ $locale.baseText('workflowSettings.minutes') }}</template>
</n8n-input>
@ -328,7 +328,7 @@
:model-value="timeoutHMS.seconds"
:min="0"
:max="60"
@update:model-value="(value) => setTimeout('seconds', value)"
@update:model-value="(value: string) => setTimeout('seconds', value)"
>
<template #append>{{ $locale.baseText('workflowSettings.seconds') }}</template>
</n8n-input>
@ -828,7 +828,10 @@ export default defineComponent({
data.versionId = this.workflowsStore.workflowVersionId;
try {
const workflow = await this.workflowsStore.updateWorkflow(this.$route.params.name, data);
const workflow = await this.workflowsStore.updateWorkflow(
String(this.$route.params.name),
data,
);
this.workflowsStore.setWorkflowVersionId(workflow.versionId);
} catch (error) {
this.showError(
@ -840,12 +843,9 @@ export default defineComponent({
}
// Get the settings without the defaults set for local workflow settings
const localWorkflowSettings: IWorkflowSettings = {};
for (const key of Object.keys(this.workflowSettings)) {
if (this.workflowSettings[key] !== 'DEFAULT') {
localWorkflowSettings[key] = this.workflowSettings[key];
}
}
const localWorkflowSettings = Object.fromEntries(
Object.entries(this.workflowSettings).filter(([, value]) => value !== 'DEFAULT'),
);
const oldSettings = deepCopy(this.workflowsStore.workflowSettings);

View File

@ -31,7 +31,7 @@ const { debounce } = useDebounce();
const telemetry = useTelemetry();
const props = withDefaults(defineProps<ExecutionFilterProps>(), {
workflows: [] as Array<IWorkflowDb | IWorkflowShortResponse>,
workflows: () => [] as Array<IWorkflowDb | IWorkflowShortResponse>,
popoverPlacement: 'bottom' as Placement,
teleported: true,
});

View File

@ -1,4 +1,4 @@
import { vi, describe, expect } from 'vitest';
import { describe, expect } from 'vitest';
import { render } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import { faker } from '@faker-js/faker';
@ -7,7 +7,7 @@ import { createPinia, PiniaVuePlugin, setActivePinia } from 'pinia';
import type { ExecutionSummary } from 'n8n-workflow';
import { useSettingsStore } from '@/stores/settings.store';
import WorkflowExecutionsPreview from '@/components/executions/workflow/WorkflowExecutionsPreview.vue';
import { VIEWS } from '@/constants';
import { EnterpriseEditionFeature, VIEWS } from '@/constants';
import { i18nInstance, I18nPlugin } from '@/plugins/i18n';
import { FontAwesomePlugin } from '@/plugins/icons';
import { GlobalComponentsPlugin } from '@/plugins/components';
@ -78,9 +78,10 @@ describe('WorkflowExecutionsPreview.vue', () => {
])(
'when debug enterprise feature is %s it should handle debug link click accordingly',
async (availability, path) => {
vi.spyOn(settingsStore, 'isEnterpriseFeatureEnabled', 'get').mockReturnValue(
() => availability,
);
settingsStore.settings.enterprise = {
...(settingsStore.settings.enterprise ?? {}),
[EnterpriseEditionFeature.DebugInEditor]: availability,
};
// Not using createComponentRenderer helper here because this component should not stub `router-link`
const { getByTestId } = render(WorkflowExecutionsPreview, {

View File

@ -72,7 +72,7 @@ import WorkflowExecutionsInfoAccordion from '@/components/executions/workflow/Wo
import ExecutionsFilter from '@/components/executions/ExecutionsFilter.vue';
import { VIEWS } from '@/constants';
import type { ExecutionSummary } from 'n8n-workflow';
import type { Route } from 'vue-router';
import type { RouteRecord } from 'vue-router';
import { defineComponent } from 'vue';
import type { PropType } from 'vue';
import { mapStores } from 'pinia';
@ -131,7 +131,7 @@ export default defineComponent({
...mapStores(useExecutionsStore, useWorkflowsStore),
},
watch: {
$route(to: Route, from: Route) {
$route(to: RouteRecord, from: RouteRecord) {
if (from.name === VIEWS.EXECUTION_PREVIEW && to.name === VIEWS.EXECUTION_HOME) {
// Skip parent route when navigating through executions with back button
this.$router.go(-1);

View File

@ -1836,6 +1836,7 @@
"settings.sourceControl.status.modified": "Modified",
"settings.sourceControl.status.deleted": "Deleted",
"settings.sourceControl.status.created": "New",
"settings.sourceControl.status.renamed": "Renamed",
"settings.sourceControl.pull.oneLastStep.title": "One last step",
"settings.sourceControl.pull.oneLastStep.description": "You have new creds/vars. Fill them out to make sure your workflows function properly",
"settings.sourceControl.pull.success.title": "Pulled successfully",