Skip to content

Commit

Permalink
fixup! feat(suite): distribution for ab testing
Browse files Browse the repository at this point in the history
  • Loading branch information
adderpositive committed Jan 2, 2025
1 parent 60b9b89 commit 8bedb06
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { ReactElement } from 'react';

import { ExperimentNameType } from '@suite-common/message-system';

import { useExperiment } from 'src/hooks/experiment/useExperiment';

interface ExperimentWrapperProps {
id: string;
id: ExperimentNameType;
components: Array<{
variant: string;
element: ReactElement;
}>;
}

/**
* @param components last item in components is default
* @param components first item in components is default
*/
export const ExperimentWrapper = ({
id,
Expand Down
12 changes: 9 additions & 3 deletions packages/suite/src/hooks/experiment/useExperiment.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { useMemo } from 'react';

import { selectAnalyticsInstanceId } from '@suite-common/analytics';
import { selectActiveExperimentGroup, selectExperimentById } from '@suite-common/message-system';
import {
ExperimentNameType,
experiments,
selectActiveExperimentGroup,
selectExperimentById,
} from '@suite-common/message-system';

import { useSelector } from 'src/hooks/suite';

export const useExperiment = (id: string) => {
export const useExperiment = (id: ExperimentNameType) => {
const experimentUuid = experiments[id];
const instanceId = useSelector(selectAnalyticsInstanceId);
const experiment = useSelector(selectExperimentById(id));
const experiment = useSelector(selectExperimentById(experimentUuid));
const activeExperimentVariant = useMemo(
() => selectActiveExperimentGroup({ instanceId, experiment }),
[instanceId, experiment],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { getWeakRandomId } from '@trezor/utils';

import { ExperimentIdType } from '../experiments';

// getWeakRandomId is also used for generating instanceId
export const getArrayOfInstanceIds = (count: number) =>
Array.from({ length: count }, () => getWeakRandomId(10));

export const experimentTest = {
id: 'experiment-test',
id: 'e2e8d05f-1469-4e47-9ab0-53544e5cad07' as ExperimentIdType,
groups: [
{
variant: 'A',
Expand Down
7 changes: 5 additions & 2 deletions suite-common/message-system/src/experiment/__tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import {
selectActiveExperimentGroup,
} from '../';
import { experimentTest, getArrayOfInstanceIds } from '../__fixtures__';
import { ExperimentIdType } from '../experiments';

describe('testing experiment utils', () => {
const experimentId = 'e2e8d05f-1469-4e47-9ab0-53544e5cad07' as ExperimentIdType;

it('test getInclusionFromInstanceId whether returns percentage between 0 and 99', () => {
const arrayOfIds = getArrayOfInstanceIds(100);
const isExistNumberOutOfRange = arrayOfIds.some(id => {
const percentage = getInclusionFromInstanceId(id, 'test-id'); // TODO: map
const percentage = getInclusionFromInstanceId(id, experimentId);

return percentage < 0 || percentage > 99;
});
Expand All @@ -20,7 +23,7 @@ describe('testing experiment utils', () => {
it('test getExperimentGroupByInclusion whether instanceId is not in range of variants', () => {
const arrayOfIds = getArrayOfInstanceIds(100);
const isExistInstanceIdNotInVariantRange = arrayOfIds.some(id => {
const inclusion = getInclusionFromInstanceId(id, 'test-id');
const inclusion = getInclusionFromInstanceId(id, experimentId);
const group = getExperimentGroupByInclusion({
groups: experimentTest.groups,
inclusion,
Expand Down
6 changes: 6 additions & 0 deletions suite-common/message-system/src/experiment/experiments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const experiments = {
// e.g. orangeSendButton: 'fb0eb1bc-8ec3-44d4-98eb-53301d73d981',
} as const;

export type ExperimentNameType = keyof typeof experiments;
export type ExperimentIdType = (typeof experiments)[ExperimentNameType];
10 changes: 7 additions & 3 deletions suite-common/message-system/src/experiment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import { createHash } from 'crypto';

import { ExperimentsItem } from '@suite-common/suite-types';

import { ExperimentIdType } from './experiments';

export type ExperimentsItemUuidType = Omit<ExperimentsItem, 'id'> & { id: ExperimentIdType };

type ExperimentCategoriesProps = {
experiment: ExperimentsItem | undefined;
experiment: ExperimentsItemUuidType | undefined;
instanceId: string | undefined;
};

type ExperimentsGroupsType = ExperimentsItem['groups'];
type ExperimentsGroupsType = ExperimentsItemUuidType['groups'];
type ExperimentsGroupType = ExperimentsGroupsType[number];

type ExperimentGetGroupByInclusion = {
Expand All @@ -18,7 +22,7 @@ type ExperimentGetGroupByInclusion = {
/**
* @returns number between 0 and 99 generated from instanceId and experimentId
*/
export const getInclusionFromInstanceId = (instanceId: string, experimentId: string) => {
export const getInclusionFromInstanceId = (instanceId: string, experimentId: ExperimentIdType) => {
const combinedId = `${instanceId}-${experimentId}`;
const hash = createHash('sha256').update(combinedId).digest('hex').slice(0, 8);

Expand Down
2 changes: 2 additions & 0 deletions suite-common/message-system/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ export * from './messageSystemSelectors';
export * from './messageSystemThunks';
export * from './messageSystemTypes';
export * from './messageSystemUtils';

export * from './experiment';
export * from './experiment/experiments';
8 changes: 6 additions & 2 deletions suite-common/message-system/src/messageSystemSelectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { createWeakMapSelector, returnStableArrayIfEmpty } from '@suite-common/r
import { Message, Category } from '@suite-common/suite-types';

import { ContextDomain, FeatureDomain, MessageSystemRootState } from './messageSystemTypes';
import { ExperimentIdType } from './experiment/experiments';
import { ExperimentsItemUuidType } from './experiment';

// Create app-specific selectors with correct types
export const createMemoizedSelector = createWeakMapSelector.withTypes<MessageSystemRootState>();
Expand Down Expand Up @@ -165,7 +167,9 @@ export const selectAllValidExperiments = createMemoizedSelector(
},
);

export const selectExperimentById = (id: string) =>
export const selectExperimentById = (id: ExperimentIdType) =>
createMemoizedSelector([selectAllValidExperiments], allValidExperiments =>
allValidExperiments.find(experiment => experiment.id === id),
allValidExperiments.find(
(experiment): experiment is ExperimentsItemUuidType => experiment.id === id,
),
);

0 comments on commit 8bedb06

Please sign in to comment.