Skip to content

Commit

Permalink
✅ Adding unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
MiloradFilipovic committed Jan 24, 2025
1 parent 01459f1 commit 5a982c1
Show file tree
Hide file tree
Showing 3 changed files with 355 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
import type { ICredentialMap, ICredentialTypeMap } from '@/Interface';

export const TEST_CREDENTIALS: ICredentialMap = {
// OpenAI credential in personal
1: {
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
id: '1',
name: 'OpenAi account',
data: 'test123',
type: 'openAiApi',
isManaged: false,
homeProject: {
id: '1',
type: 'personal',
name: 'Kobi Dog <[email protected]>',
icon: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
sharedWithProjects: [
{
id: '2',
type: 'team',
name: 'Test Project',
icon: { type: 'icon', value: 'exchange-alt' },
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
],
scopes: [
'credential:create',
'credential:delete',
'credential:list',
'credential:move',
'credential:read',
'credential:share',
'credential:update',
],
},
// Supabase credential in another project
2: {
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
id: '2',
name: 'Supabase account',
data: 'test123',
type: 'supabaseApi',
isManaged: false,
homeProject: {
id: '2',
type: 'team',
name: 'Test Project',
icon: { type: 'icon', value: 'exchange-alt' },
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
sharedWithProjects: [],
scopes: [
'credential:create',
'credential:delete',
'credential:list',
'credential:move',
'credential:read',
'credential:share',
'credential:update',
],
},
// Slack account in personal
3: {
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
id: '3',
name: 'Slack account',
data: 'test123',
type: 'slackOAuth2Api',
isManaged: false,
homeProject: {
id: '1',
type: 'personal',
name: 'Kobi Dog <[email protected]>',
icon: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
sharedWithProjects: [],
scopes: [
'credential:create',
'credential:delete',
'credential:list',
'credential:move',
'credential:read',
'credential:share',
'credential:update',
],
},
// OpenAI credential in another project
4: {
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
id: '4',
name: '[PROJECT] OpenAI Account',
data: 'test123',
type: 'openAiApi',
isManaged: false,
homeProject: {
id: '2',
type: 'team',
name: 'Test Project',
icon: { type: 'icon', value: 'exchange-alt' },
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
sharedWithProjects: [],
scopes: [
'credential:create',
'credential:delete',
'credential:list',
'credential:move',
'credential:read',
'credential:share',
'credential:update',
],
},
};

export const TEST_CREDENTIAL_TYPES: ICredentialTypeMap = {
openAiApi: {
name: 'openAiApi',
displayName: 'OpenAi',
documentationUrl: 'openAi',
properties: [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: {
password: true,
},
required: true,
default: '',
},
{
displayName: 'Base URL',
name: 'url',
type: 'string',
default: 'https://api.openai.com/v1',
description: 'Override the base URL for the API',
},
],
authenticate: {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.apiKey}}',
},
},
},
test: {
request: {
baseURL: '={{$credentials?.url}}',
url: '/models',
},
},
iconUrl: {
light: 'icons/n8n-nodes-base/dist/nodes/OpenAi/openai.svg',
dark: 'icons/n8n-nodes-base/dist/nodes/OpenAi/openai.dark.svg',
},
supportedNodes: [
'n8n-nodes-base.openAi',
'@n8n/n8n-nodes-langchain.embeddingsOpenAi',
'@n8n/n8n-nodes-langchain.lmChatOpenAi',
'@n8n/n8n-nodes-langchain.lmOpenAi',
],
},
supabaseApi: {
name: 'supabaseApi',
displayName: 'Supabase API',
documentationUrl: 'supabase',
properties: [
{
displayName: 'Host',
name: 'host',
type: 'string',
placeholder: 'https://your_account.supabase.co',
default: '',
},
{
displayName: 'Service Role Secret',
name: 'serviceRole',
type: 'string',
default: '',
typeOptions: {
password: true,
},
},
],
authenticate: {
type: 'generic',
properties: {
headers: {
apikey: '={{$credentials.serviceRole}}',
Authorization: '=Bearer {{$credentials.serviceRole}}',
},
},
},
test: {
request: {
baseURL: '={{$credentials.host}}/rest/v1',
headers: {
Prefer: 'return=representation',
},
url: '/',
},
},
iconUrl: 'icons/n8n-nodes-base/dist/nodes/Supabase/supabase.svg',
supportedNodes: ['n8n-nodes-base.supabase'],
},
slackOAuth2Api: {
name: 'slackOAuth2Api',
extends: ['oAuth2Api'],
displayName: 'Slack OAuth2 API',
documentationUrl: 'slack',
properties: [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://slack.com/oauth/v2/authorize',
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://slack.com/api/oauth.v2.access',
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: 'chat:write',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default:
'user_scope=channels:read channels:write chat:write files:read files:write groups:read im:read mpim:read reactions:read reactions:write stars:read stars:write usergroups:write usergroups:read users.profile:read users.profile:write users:read',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'body',
},
{
displayName:
'If you get an Invalid Scopes error, make sure you add the correct one <a target="_blank" href="https://docs.n8n.io/integrations/builtin/credentials/slack/#using-oauth">here</a> to your Slack integration',
name: 'notice',
type: 'notice',
default: '',
},
],
iconUrl: 'icons/n8n-nodes-base/dist/nodes/Slack/slack.svg',
supportedNodes: ['n8n-nodes-base.slack'],
},
};

export const PERSONAL_OPENAI_CREDENTIAL = TEST_CREDENTIALS[1];
export const PROJECT_OPENAI_CREDENTIAL = TEST_CREDENTIALS[4];
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { createComponentRenderer } from '@/__tests__/render';
import { mockedStore } from '@/__tests__/utils';
import { useCredentialsStore } from '@/stores/credentials.store';
import { createTestingPinia } from '@pinia/testing';
import CredentialPicker from './CredentialPicker.vue';
import {
PERSONAL_OPENAI_CREDENTIAL,
PROJECT_OPENAI_CREDENTIAL,
TEST_CREDENTIAL_TYPES,
TEST_CREDENTIALS,
} from './CredentialPicker.test.constants';
import userEvent from '@testing-library/user-event';
import { screen } from '@testing-library/vue';

vi.mock('vue-router', () => {
const push = vi.fn();
const resolve = vi.fn().mockReturnValue({ href: 'https://test.com' });
return {
useRouter: () => ({
push,
resolve,
}),
useRoute: () => ({}),
RouterLink: vi.fn(),
};
});

let credentialsStore: ReturnType<typeof mockedStore<typeof useCredentialsStore>>;

const renderComponent = createComponentRenderer(CredentialPicker);

describe('CredentialPicker', () => {
beforeEach(() => {
createTestingPinia();
credentialsStore = mockedStore(useCredentialsStore);
credentialsStore.state.credentials = TEST_CREDENTIALS;
credentialsStore.state.credentialTypes = TEST_CREDENTIAL_TYPES;
});

it('should render', () => {
expect(() =>
renderComponent({
props: {
appName: 'OpenAI',
credentialType: 'openAiApi',
selectedCredentialId: null,
},
}),
).not.toThrowError();
});

it('should only render personal credentials of the specified type', async () => {
const TEST_APP_NAME = 'OpenAI';
const TEST_CREDENTIAL_TYPE = 'openAiApi';
const { getByTestId } = renderComponent({
props: {
appName: TEST_APP_NAME,
credentialType: TEST_CREDENTIAL_TYPE,
selectedCredentialId: null,
},
});
expect(getByTestId('credential-dropdown')).toBeInTheDocument();
expect(getByTestId('credential-dropdown')).toHaveAttribute(
'credential-type',
TEST_CREDENTIAL_TYPE,
);
// Open the dropdown
await userEvent.click(getByTestId('credential-dropdown'));
// Personal openAI credential should be in the dropdown
expect(
screen.getByTestId(`node-credentials-select-item-${PERSONAL_OPENAI_CREDENTIAL.id}`),
).toBeInTheDocument();
// OpenAI credential that belong to other project should not be in the dropdown
expect(
screen.queryByTestId(`node-credentials-select-item-${PROJECT_OPENAI_CREDENTIAL.id}`),
).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ listenForModalChanges({
:credential-type="props.credentialType"
:credential-options="credentialOptions"
:selected-credential-id="props.selectedCredentialId"
data-test-id="credential-dropdown"
@credential-selected="onCredentialSelected"
@new-credential="createNewCredential"
/>
Expand Down

0 comments on commit 5a982c1

Please sign in to comment.