Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/meteor/.mocharc.client.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ module.exports = {
'tests/unit/lib/**/*.tests.ts',
'tests/unit/client/**/*.test.ts',
],
exclude: ['client/hooks/*.spec.{ts,tsx}'],
};
177 changes: 177 additions & 0 deletions apps/meteor/client/hooks/useFeaturePreview.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/* eslint-disable react/no-multi-comp */
import type { ISetting } from '@rocket.chat/core-typings';
import type { LoginService } from '@rocket.chat/ui-contexts';
import { SettingsContext, UserContext } from '@rocket.chat/ui-contexts';
import { renderHook } from '@testing-library/react-hooks';
import type { ObjectId } from 'mongodb';
import type { ContextType } from 'react';
import React from 'react';

import { useFeaturePreview } from './useFeaturePreview';

const userContextValue: ContextType<typeof UserContext> = {
userId: 'john.doe',
user: {
_id: 'john.doe',
username: 'john.doe',
name: 'John Doe',
createdAt: new Date(),
active: true,
_updatedAt: new Date(),
roles: ['admin'],
type: 'user',
},
queryPreference: <T,>(pref: string | ObjectId, defaultValue: T) => [
() => () => undefined,
() => (typeof pref === 'string' ? undefined : defaultValue),
],
querySubscriptions: () => [() => () => undefined, () => []],
querySubscription: () => [() => () => undefined, () => undefined],
queryRoom: () => [() => () => undefined, () => undefined],

queryAllServices: () => [() => (): void => undefined, (): LoginService[] => []],
loginWithService: () => () => Promise.reject('loginWithService not implemented'),
loginWithPassword: async () => Promise.reject('loginWithPassword not implemented'),
loginWithToken: async () => Promise.reject('loginWithToken not implemented'),
logout: () => Promise.resolve(),
};

const settingContextValue: ContextType<typeof SettingsContext> = {
hasPrivateAccess: true,
isLoading: false,
querySetting: (_id: string) => [() => () => undefined, () => undefined],
querySettings: () => [() => () => undefined, () => []],
dispatch: async () => undefined,
};

it('should return false if featurePreviewEnabled is false', () => {
const { result } = renderHook(
() => {
return useFeaturePreview('quickReactions');
},
{
wrapper: ({ children }) => (
<MockedSettingsContext
settings={{
Accounts_AllowFeaturePreview: false,
}}
>
<MockedUserContext userPreferences={{}}>{children}</MockedUserContext>
</MockedSettingsContext>
),
},
);

expect(result.all[0]).toBe(false);
});

it('should return false if featurePreviewEnabled is true but feature is not in userPreferences', () => {
const { result } = renderHook(
() => {
return useFeaturePreview('quickReactions');
},
{
wrapper: ({ children }) => (
<MockedSettingsContext
settings={{
Accounts_AllowFeaturePreview: false,
}}
>
<MockedUserContext
userPreferences={{
featuresPreview: [
{
name: 'quickReactions',
value: true,
},
],
}}
>
{children}
</MockedUserContext>
</MockedSettingsContext>
),
},
);

expect(result.all[0]).toBe(false);
});

it('should return true if featurePreviewEnabled is true and feature is in userPreferences', () => {
const { result } = renderHook(
() => {
return useFeaturePreview('quickReactions');
},
{
wrapper: ({ children }) => (
<MockedSettingsContext
settings={{
Accounts_AllowFeaturePreview: true,
}}
>
<MockedUserContext
userPreferences={{
featuresPreview: [
{
name: 'quickReactions',
value: true,
},
],
}}
>
{children}
</MockedUserContext>
</MockedSettingsContext>
),
},
);

expect(result.all[0]).toBe(true);
});

const createUserContextValue = ({ userPreferences }: { userPreferences?: Record<string, unknown> }): ContextType<typeof UserContext> => {
return {
...userContextValue,
...(userPreferences && { queryPreference: (id) => [() => () => undefined, () => userPreferences[id as unknown as string] as any] }),
};
};

const createSettingContextValue = ({ settings }: { settings?: Record<string, ISetting['value']> }): ContextType<typeof SettingsContext> => {
const cache = new Map<string, ISetting['value']>();

return {
...settingContextValue,
...(settings && {
querySetting: (_id: string) => [
() => () => undefined,
() => {
if (cache.has(_id)) {
return cache.get(_id) as any;
}
cache.set(_id, { value: settings[_id] } as any);
return cache.get(_id) as any;
},
],
}),
};
};

export const MockedSettingsContext = ({
settings,
children,
}: {
children: React.ReactNode;
settings?: Record<string, ISetting['value']>;
}) => {
return <SettingsContext.Provider value={createSettingContextValue({ settings })}>{children}</SettingsContext.Provider>;
};

export const MockedUserContext = ({
userPreferences,
children,
}: {
children: React.ReactNode;
userPreferences?: Record<string, unknown>;
}) => {
return <UserContext.Provider value={createUserContextValue({ userPreferences })}>{children}</UserContext.Provider>;
};
2 changes: 1 addition & 1 deletion apps/meteor/client/hooks/useFeaturePreview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { FeaturesAvailable, FeaturePreviewProps } from './useFeaturePreview

export const useFeaturePreview = (featureName: FeaturesAvailable) => {
const featurePreviewEnabled = useSetting('Accounts_AllowFeaturePreview');

const features = useUserPreference<FeaturePreviewProps[]>('featuresPreview');

const currentFeature = features?.find((feature) => feature.name === featureName);
Expand All @@ -13,7 +14,6 @@ export const useFeaturePreview = (featureName: FeaturesAvailable) => {
}

if (!currentFeature) {
console.error(`Feature ${featureName} not found`);
return false;
}

Expand Down
89 changes: 89 additions & 0 deletions apps/meteor/client/hooks/useFeaturePreviewList.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { renderHook } from '@testing-library/react-hooks';
import React from 'react';

import { MockedSettingsContext, MockedUserContext } from './useFeaturePreview.spec';
import { useFeaturePreviewList, defaultFeaturesPreview } from './useFeaturePreviewList';

it('should return the number of unseen features and Accounts_AllowFeaturePreview enabled ', () => {
const { result } = renderHook(
() => {
return useFeaturePreviewList();
},
{
wrapper: ({ children }) => (
<MockedSettingsContext
settings={{
Accounts_AllowFeaturePreview: true,
}}
>
<MockedUserContext userPreferences={{}}>{children}</MockedUserContext>
</MockedSettingsContext>
),
},
);

expect(result.all[0]).toEqual(
expect.objectContaining({
featurePreviewEnabled: true,
unseenFeatures: defaultFeaturesPreview.length,
}),
);
});

it('should return the number of unseen features and Accounts_AllowFeaturePreview disabled ', () => {
const { result } = renderHook(
() => {
return useFeaturePreviewList();
},
{
wrapper: ({ children }) => (
<MockedSettingsContext
settings={{
Accounts_AllowFeaturePreview: false,
}}
>
<MockedUserContext userPreferences={{}}>{children}</MockedUserContext>
</MockedSettingsContext>
),
},
);

expect(result.all[0]).toEqual(
expect.objectContaining({
featurePreviewEnabled: false,
unseenFeatures: 0,
}),
);
});

it('should return 0 unseen features', () => {
const { result } = renderHook(
() => {
return useFeaturePreviewList();
},
{
wrapper: ({ children }) => (
<MockedSettingsContext
settings={{
Accounts_AllowFeaturePreview: true,
}}
>
<MockedUserContext
userPreferences={{
featuresPreview: defaultFeaturesPreview,
}}
>
{children}
</MockedUserContext>
</MockedSettingsContext>
),
},
);

expect(result.all[0]).toEqual(
expect.objectContaining({
featurePreviewEnabled: true,
unseenFeatures: 0,
}),
);
});
14 changes: 14 additions & 0 deletions apps/meteor/jest.client.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default {
errorOnDeprecated: true,

testEnvironment: 'jsdom',
modulePathIgnorePatterns: ['<rootDir>/dist/'],
testMatch: ['<rootDir>/client/hooks/**.spec.[jt]s?(x)', './client/hooks/**.spec.ts', '/client/hooks/**.spec.ts'],
transform: {
'^.+\\.(t|j)sx?$': '@swc/jest',
},
moduleNameMapper: {
'\\.css$': 'identity-obj-proxy',
'^react($|/.+)': '<rootDir>/node_modules/react$1',
},
};
2 changes: 2 additions & 0 deletions apps/meteor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"@storybook/react": "~6.5.16",
"@storybook/testing-library": "0.0.13",
"@swc/core": "^1.3.66",
"@swc/jest": "^0.2.26",
"@tanstack/react-query-devtools": "^4.19.1",
"@testing-library/react": "~12.1.5",
"@testing-library/react-hooks": "^8.0.1",
Expand Down Expand Up @@ -177,6 +178,7 @@
"eslint-plugin-you-dont-need-lodash-underscore": "~6.12.0",
"fast-glob": "^3.2.12",
"i18next": "^20.6.1",
"jest": "^29.6.1",
"jsdom-global": "^3.0.2",
"mocha": "^9.2.2",
"nyc": "^15.1.0",
Expand Down
Loading