Skip to content

Commit c4ede8b

Browse files
committed
test: add unit tests for bootstrap and register functionality
1 parent 00fac60 commit c4ede8b

File tree

10 files changed

+582
-90
lines changed

10 files changed

+582
-90
lines changed

jest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const config: JestConfigWithTsJest = {
1010
coverageDirectory: './coverage/',
1111
collectCoverage: true,
1212
reporters: ['default', 'jest-junit'],
13+
collectCoverageFrom: ['server/src/**/*.{js,jsx,ts,tsx}'],
1314
globals: {
1415
'ts-jest': {
1516
tsconfig: './tsconfig.test.json',

junit.xml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<testsuites name="jest tests" tests="16" failures="0" errors="0" time="0.991">
3+
<testsuite name="Bootstrap" errors="0" failures="0" skipped="0" timestamp="2025-06-02T16:06:29" time="0.289" tests="11">
4+
<testcase classname="Bootstrap saveConfig when config does not exist in store should save default config if no config exists" name="Bootstrap saveConfig when config does not exist in store should save default config if no config exists" time="0.006">
5+
</testcase>
6+
<testcase classname="Bootstrap saveConfig when config exists in store should not overwrite existing config" name="Bootstrap saveConfig when config exists in store should not overwrite existing config" time="0.001">
7+
</testcase>
8+
<testcase classname="Bootstrap addPermissions should register all permissions" name="Bootstrap addPermissions should register all permissions" time="0">
9+
</testcase>
10+
<testcase classname="Bootstrap registerUploadProvider upload is enabled upload is called from provider" name="Bootstrap registerUploadProvider upload is enabled upload is called from provider" time="0.008">
11+
</testcase>
12+
<testcase classname="Bootstrap registerUploadProvider upload is enabled uploadStream is called from provider" name="Bootstrap registerUploadProvider upload is enabled uploadStream is called from provider" time="0">
13+
</testcase>
14+
<testcase classname="Bootstrap registerUploadProvider upload is enabled delete is called from provider" name="Bootstrap registerUploadProvider upload is enabled delete is called from provider" time="0">
15+
</testcase>
16+
<testcase classname="Bootstrap registerUploadProvider upload is enabled isPrivate is called from provider" name="Bootstrap registerUploadProvider upload is enabled isPrivate is called from provider" time="0">
17+
</testcase>
18+
<testcase classname="Bootstrap registerUploadProvider upload is disabled upload is called from provider" name="Bootstrap registerUploadProvider upload is disabled upload is called from provider" time="0">
19+
</testcase>
20+
<testcase classname="Bootstrap registerUploadProvider upload is disabled uploadStream is called from provider" name="Bootstrap registerUploadProvider upload is disabled uploadStream is called from provider" time="0.001">
21+
</testcase>
22+
<testcase classname="Bootstrap registerUploadProvider upload is disabled delete is called from provider" name="Bootstrap registerUploadProvider upload is disabled delete is called from provider" time="0">
23+
</testcase>
24+
<testcase classname="Bootstrap registerUploadProvider upload is disabled isPrivate is called from provider" name="Bootstrap registerUploadProvider upload is disabled isPrivate is called from provider" time="0">
25+
</testcase>
26+
</testsuite>
27+
<testsuite name="Register" errors="0" failures="0" skipped="0" timestamp="2025-06-02T16:06:29" time="0.698" tests="5">
28+
<testcase classname="Register registerSanitizer sanitizer is registered sanitizer is registered" name="Register registerSanitizer sanitizer is registered sanitizer is registered" time="0.003">
29+
</testcase>
30+
<testcase classname="Register registerSanitizer sanitizer tests should not convert to imagekit url" name="Register registerSanitizer sanitizer tests should not convert to imagekit url" time="0">
31+
</testcase>
32+
<testcase classname="Register registerSanitizer sanitizer tests should not convert to imagekit url" name="Register registerSanitizer sanitizer tests should not convert to imagekit url" time="0.001">
33+
</testcase>
34+
<testcase classname="Register registerSanitizer sanitizer tests should convert to imagekit url" name="Register registerSanitizer sanitizer tests should convert to imagekit url" time="0.002">
35+
</testcase>
36+
<testcase classname="Register registerSanitizer sanitizer tests should convert to imagekit url including formats" name="Register registerSanitizer sanitizer tests should convert to imagekit url including formats" time="0">
37+
</testcase>
38+
</testsuite>
39+
</testsuites>

server/src/bootstrap.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { get } from 'lodash';
33
import { permissions, PLUGIN_ID } from '../../common';
44
import { File } from './services/upload.service';
55
import { getService } from './utils/getService';
6+
67
async function saveConfig(strapi: Core.Strapi) {
78
if (strapi.store) {
89
const pluginStore = strapi.store({ type: 'plugin', name: PLUGIN_ID });
@@ -102,3 +103,6 @@ const bootstrap = async ({ strapi }: { strapi: Core.Strapi }) => {
102103
};
103104

104105
export default bootstrap;
106+
107+
// Export functions for testing
108+
export { addPermissions, registerUploadProvider, saveConfig };

server/src/register.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import { getService } from './utils/getService';
99
function toImageKitUrl(src: string, settings: Settings, client: ImageKit): string {
1010
const endpoint = settings.urlEndpoint;
1111

12+
if (!endpoint) {
13+
return src;
14+
}
15+
1216
if (src.startsWith(endpoint)) {
1317
return client.url({
1418
src,

server/tests/bootstrap.test.ts

Lines changed: 173 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,200 @@
11
import { permissions, PLUGIN_ID } from '../../common';
22
import bootstrap from '../src/bootstrap';
3-
import { settingsServiceMock, uploadServiceMock } from './utils/plugins/imagekit/services';
3+
import { imagekitMock } from './utils/plugins/imagekit';
4+
import { uploadServiceMock } from './utils/plugins/imagekit/services';
45
import { getStrapiMock } from './utils/strapi';
56

6-
// Mock the getService utility
7-
jest.mock('../src/utils/getService', () => ({
8-
getService: jest.fn((_, serviceName) => {
9-
if (serviceName === 'settings') return settingsServiceMock;
10-
if (serviceName === 'upload') return uploadServiceMock;
11-
return jest.fn();
12-
}),
13-
}));
14-
157
describe('Bootstrap', () => {
16-
let strapiMock: ReturnType<typeof getStrapiMock>;
17-
188
beforeEach(() => {
199
jest.clearAllMocks();
20-
strapiMock = getStrapiMock();
21-
22-
bootstrap({ strapi: strapiMock });
2310
});
2411

25-
describe.only('saveConfig', () => {
26-
it('should save default config if no config exists', async () => {
27-
expect(strapiMock.store).toHaveBeenCalledTimes(1);
28-
expect(strapiMock.store).toHaveBeenCalledWith({ type: 'plugin', name: PLUGIN_ID });
12+
describe('saveConfig', () => {
13+
describe('when config does not exist in store', () => {
14+
const strapiMock = getStrapiMock();
15+
16+
beforeEach(() => bootstrap({ strapi: strapiMock }));
17+
it('should save default config if no config exists', async () => {
18+
expect(strapiMock.store).toHaveBeenCalledTimes(1);
19+
expect(strapiMock.store).toHaveBeenCalledWith({ type: 'plugin', name: PLUGIN_ID });
20+
21+
const get = strapiMock.store.mock.results.find((_) => _.type === 'return')?.value.get;
22+
23+
expect(get).toHaveBeenCalledTimes(1);
24+
expect(get).toHaveBeenCalledWith({ key: 'config' });
25+
26+
expect(imagekitMock.config).toHaveBeenCalledTimes(14);
27+
28+
const set = strapiMock.store.mock.results.find((_) => _.type === 'return')?.value.set;
29+
expect(set).toHaveBeenCalledTimes(1);
30+
expect(set).toHaveBeenCalledWith({
31+
key: 'config',
32+
value: {
33+
enabled: false,
34+
publicKey: '',
35+
privateKey: '',
36+
urlEndpoint: '',
37+
useSignedUrls: false,
38+
expiry: 0,
39+
uploadEnabled: false,
40+
uploadOptions: {
41+
tags: [],
42+
folder: '',
43+
overwriteTags: false,
44+
overwriteCustomMetadata: false,
45+
checks: '',
46+
isPrivateFile: false,
47+
},
48+
useTransformUrls: false,
49+
},
50+
});
51+
52+
expect(imagekitMock.config.mock.calls).toEqual([
53+
['enabled', false],
54+
['publicKey', ''],
55+
['privateKey', ''],
56+
['urlEndpoint', ''],
57+
['useSignedUrls', false],
58+
['expiry', 0],
59+
['uploadEnabled', false],
60+
['useTransformUrls', false],
61+
['uploadOptions.tags', []],
62+
['uploadOptions.folder', ''],
63+
['uploadOptions.overwriteTags', false],
64+
['uploadOptions.overwriteCustomMetadata', false],
65+
['uploadOptions.checks', ''],
66+
['uploadOptions.isPrivateFile', false],
67+
]);
68+
});
69+
});
2970

30-
const get = strapiMock.store.mock.results.find((_) => _.type === 'return')?.value.get;
71+
describe('when config exists in store', () => {
72+
const strapiMock = getStrapiMock({ storeConfig: {} });
3173

32-
expect(get).toHaveBeenCalledTimes(1);
33-
expect(get).toHaveBeenCalledWith({ key: 'config' });
74+
beforeEach(() => bootstrap({ strapi: strapiMock }));
75+
it('should not overwrite existing config', async () => {
76+
expect(strapiMock.store).toHaveBeenCalledTimes(1);
77+
expect(strapiMock.store).toHaveBeenCalledWith({ type: 'plugin', name: PLUGIN_ID });
3478

35-
console.log(get);
36-
});
79+
const get = strapiMock.store.mock.results.find((_) => _.type === 'return')?.value.get;
3780

38-
it('should not overwrite existing config', async () => {
39-
// Mock the store to return existing config
40-
const existingConfig = {
41-
publicKey: 'existing-public-key',
42-
privateKey: 'existing-private-key',
43-
urlEndpoint: 'https://existing-endpoint.com',
44-
};
45-
const mockGet = jest.fn().mockResolvedValue(existingConfig);
46-
const mockSet = jest.fn().mockResolvedValue(undefined);
47-
48-
const pluginStore = { get: mockGet, set: mockSet };
49-
jest.spyOn(strapiMock, 'store').mockReturnValue(pluginStore);
50-
51-
// Import the function to test
52-
const { saveConfig } = require('../src/bootstrap');
53-
await saveConfig(strapiMock);
54-
55-
// Verify set was not called
56-
expect(mockSet).not.toHaveBeenCalled();
81+
expect(get).toHaveBeenCalledTimes(1);
82+
expect(get).toHaveBeenCalledWith({ key: 'config' });
83+
84+
expect(imagekitMock.config).toHaveBeenCalledTimes(0);
85+
const set = strapiMock.store.mock.results.find((_) => _.type === 'return')?.value.set;
86+
expect(set).toHaveBeenCalledTimes(0);
87+
});
5788
});
5889
});
5990

6091
describe('addPermissions', () => {
92+
const strapiMock = getStrapiMock();
93+
94+
beforeEach(() => bootstrap({ strapi: strapiMock }));
6195
it('should register all permissions', async () => {
62-
// Mock the permissions service
63-
const registerManyMock = jest.fn().mockResolvedValue(undefined);
64-
strapiMock.admin = {
65-
services: {
66-
permission: {
67-
actionProvider: {
68-
registerMany: registerManyMock,
69-
},
96+
expect(
97+
strapiMock.admin.services.permission.actionProvider.registerMany
98+
).toHaveBeenCalledTimes(1);
99+
expect(strapiMock.admin.services.permission.actionProvider.registerMany).toHaveBeenCalledWith(
100+
[
101+
{
102+
section: 'plugins',
103+
displayName: 'Access ImageKit Media Library',
104+
uid: permissions.mediaLibrary.read,
105+
pluginName: PLUGIN_ID,
106+
},
107+
{
108+
section: 'plugins',
109+
displayName: 'Settings: Read',
110+
uid: permissions.settings.read,
111+
subCategory: 'settings',
112+
pluginName: PLUGIN_ID,
70113
},
71-
},
72-
};
73-
74-
// Import the function to test
75-
const { addPermissions } = require('../src/bootstrap');
76-
await addPermissions(strapiMock);
77-
78-
// Verify permissions were registered
79-
expect(registerManyMock).toHaveBeenCalledWith([
80-
{
81-
section: 'plugins',
82-
displayName: 'Access ImageKit Media Library',
83-
uid: permissions.mediaLibrary.read,
84-
pluginName: PLUGIN_ID,
85-
},
86-
{
87-
section: 'plugins',
88-
displayName: 'Settings: Read',
89-
uid: permissions.settings.read,
90-
subCategory: 'settings',
91-
pluginName: PLUGIN_ID,
92-
},
93-
{
94-
section: 'plugins',
95-
displayName: 'Settings: Change',
96-
uid: permissions.settings.change,
97-
subCategory: 'settings',
98-
pluginName: PLUGIN_ID,
99-
},
100-
]);
114+
{
115+
section: 'plugins',
116+
displayName: 'Settings: Change',
117+
uid: permissions.settings.change,
118+
subCategory: 'settings',
119+
pluginName: PLUGIN_ID,
120+
},
121+
]
122+
);
101123
});
102124
});
103125

104-
describe('registerTransformResponseDecorator', () => {
105-
it('should add transformResponse to API controllers', async () => {
106-
// This would test the transform response decorator logic
107-
// Implementation would depend on how the decorator is used in the app
126+
describe('registerUploadProvider', () => {
127+
const strapiMock = getStrapiMock({
128+
storeConfig: { uploadEnabled: true },
129+
config: { uploadEnabled: true },
130+
});
131+
132+
describe('upload is enabled', () => {
133+
beforeEach(() => {
134+
bootstrap({ strapi: strapiMock });
135+
strapiMock
136+
.plugin('imagekit')
137+
.service('settings')
138+
.getSettings.mockResolvedValue({ uploadEnabled: true });
139+
});
140+
141+
it('upload is called from provider', async () => {
142+
expect(uploadServiceMock.upload).toHaveBeenCalledTimes(0);
143+
await strapiMock.plugin('upload').provider.upload({});
144+
expect(uploadServiceMock.upload).toHaveBeenCalledTimes(1);
145+
});
146+
147+
it('uploadStream is called from provider', async () => {
148+
expect(uploadServiceMock.uploadStream).toHaveBeenCalledTimes(0);
149+
await strapiMock.plugin('upload').provider.uploadStream({});
150+
expect(uploadServiceMock.uploadStream).toHaveBeenCalledTimes(1);
151+
});
152+
153+
it('delete is called from provider', async () => {
154+
expect(uploadServiceMock.delete).toHaveBeenCalledTimes(0);
155+
await strapiMock.plugin('upload').provider.delete({});
156+
expect(uploadServiceMock.delete).toHaveBeenCalledTimes(1);
157+
});
158+
159+
it('isPrivate is called from provider', async () => {
160+
expect(uploadServiceMock.isPrivate).toHaveBeenCalledTimes(0);
161+
await strapiMock.plugin('upload').provider.isPrivate({});
162+
expect(uploadServiceMock.isPrivate).toHaveBeenCalledTimes(1);
163+
});
108164
});
109-
});
110165

111-
describe('registerUploadProvider', () => {
112-
it('should wrap upload provider methods', async () => {
113-
// This would test the upload provider registration
114-
// Implementation would depend on how the provider is used in the app
166+
describe('upload is disabled', () => {
167+
beforeEach(() => {
168+
bootstrap({ strapi: strapiMock });
169+
strapiMock
170+
.plugin('imagekit')
171+
.service('settings')
172+
.getSettings.mockResolvedValue({ uploadEnabled: false });
173+
});
174+
175+
it('upload is called from provider', async () => {
176+
expect(uploadServiceMock.upload).toHaveBeenCalledTimes(0);
177+
await strapiMock.plugin('upload').provider.upload({});
178+
expect(uploadServiceMock.upload).toHaveBeenCalledTimes(0);
179+
});
180+
181+
it('uploadStream is called from provider', async () => {
182+
expect(uploadServiceMock.uploadStream).toHaveBeenCalledTimes(0);
183+
await strapiMock.plugin('upload').provider.uploadStream({});
184+
expect(uploadServiceMock.uploadStream).toHaveBeenCalledTimes(0);
185+
});
186+
187+
it('delete is called from provider', async () => {
188+
expect(uploadServiceMock.delete).toHaveBeenCalledTimes(0);
189+
await strapiMock.plugin('upload').provider.delete({});
190+
expect(uploadServiceMock.delete).toHaveBeenCalledTimes(0);
191+
});
192+
193+
it('isPrivate is called from provider', async () => {
194+
expect(uploadServiceMock.isPrivate).toHaveBeenCalledTimes(0);
195+
await strapiMock.plugin('upload').provider.isPrivate({});
196+
expect(uploadServiceMock.isPrivate).toHaveBeenCalledTimes(0);
197+
});
115198
});
116199
});
117200
});

0 commit comments

Comments
 (0)