Skip to content

Commit

Permalink
fixup! ✨(frontend) implement our own factory unique helper
Browse files Browse the repository at this point in the history
  • Loading branch information
jbpenrath committed May 31, 2023
1 parent 2c03e87 commit 4af8315
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 52 deletions.
4 changes: 2 additions & 2 deletions src/frontend/js/utils/test/factories/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const factory = <TData>(builder: FactoryBuilder<TData>): Factory<TData> =
export class FactoryConfig {
static #isInternalConstructing = false;
static #instance: FactoryConfig;
static GLOBAL_UNIQUE_STORE: any[] = [];
static GLOBAL_UNIQUE_STORE = new Set<any>();

constructor() {
if (!FactoryConfig.#isInternalConstructing) {
Expand All @@ -87,6 +87,6 @@ export class FactoryConfig {
}

static resetUniqueStore(): void {
FactoryConfig.GLOBAL_UNIQUE_STORE = [];
FactoryConfig.GLOBAL_UNIQUE_STORE.clear();
}
}
22 changes: 10 additions & 12 deletions src/frontend/js/utils/test/factories/helper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,28 @@ describe('FactoryHelper', () => {

it('should return an unique value', () => {
const factory = incrementalNumberFactory();
expect(FactoryHelper.unique(factory)()).toEqual(1);
expect(FactoryHelper.unique(factory)()).toEqual(2);
expect(FactoryHelper.unique(factory)).toEqual(1);
expect(FactoryHelper.unique(factory)).toEqual(2);
});

it('should allow to pass parameters to original factory', () => {
const factory = incrementalNumberFactory();
expect(FactoryHelper.unique(factory)(2)).toEqual(2);
expect(FactoryHelper.unique(factory)(3)).toEqual(5);
expect(FactoryHelper.unique(factory, { args: [2] })).toEqual(2);
expect(FactoryHelper.unique(factory, { args: [3] })).toEqual(5);
});

it('should allow to use a shared store', () => {
const factory = incrementalNumberFactory();
const store = [1, 2, 3];
const uniqueFactory = FactoryHelper.unique(factory, { store });
expect(uniqueFactory()).toEqual(4);
expect(uniqueFactory()).toEqual(5);
const store = new Set([1, 2, 3]);
expect(FactoryHelper.unique(factory, { store })).toEqual(4);
expect(FactoryHelper.unique(factory, { store })).toEqual(5);
});

it('should throw an error if maxRetries is reached', () => {
const factory = incrementalNumberFactory();
const uniqueFactory = FactoryHelper.unique(factory, { maxRetries: 1, store: [1] });
expect(() => uniqueFactory()).toThrowErrorMatchingInlineSnapshot(
`"Could not generate a unique value after 1 retries."`,
);
expect(() =>
FactoryHelper.unique(factory, { maxRetries: 1, store: new Set([1]) }),
).toThrowErrorMatchingInlineSnapshot(`"Could not generate a unique value after 1 retries."`);
});
});
});
35 changes: 18 additions & 17 deletions src/frontend/js/utils/test/factories/helper.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// Faker 9.0 has removed helpers.unique method. We need to implement a custom and simpler one.

import { FactoryConfig } from 'utils/test/factories/factories';

type Generator = (...args: any[]) => any;
type Options = {
store?: any[] | Set<any>;
type Options<G extends Generator> = {
store?: Set<any>;
maxRetries?: number;
args?: Parameters<G>;
};

export class FactoryHelper {
Expand All @@ -12,25 +15,23 @@ export class FactoryHelper {
* @param generator
* @param options
*/
static unique<Factory extends Generator>(generator: Factory, options: Options = {}) {
const internalStore = new Set(options?.store);
static unique<G extends Generator>(generator: G, options: Options<G> = {}): ReturnType<G> {
const internalStore = options?.store ?? FactoryConfig.GLOBAL_UNIQUE_STORE;
const maxRetries = options?.maxRetries || 10;

return (...args: Parameters<Factory>) => {
let value;
let retry = 0;
let value;
let retry = 0;

do {
value = generator(...args);
retry++;
} while (retry <= maxRetries && internalStore.has(value));
do {
value = generator(...(options?.args ?? []));
retry++;
} while (retry <= maxRetries && internalStore.has(value));

if (retry > maxRetries) {
throw new Error(`Could not generate a unique value after ${maxRetries} retries.`);
}
if (retry > maxRetries) {
throw new Error(`Could not generate a unique value after ${maxRetries} retries.`);
}

internalStore.add(value);
return value;
};
internalStore.add(value);
return value;
}
}
37 changes: 18 additions & 19 deletions src/frontend/js/utils/test/factories/joanie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export const TargetCourseFactory = factory((): TargetCourse => {
const code = FactoryHelper.unique(faker.string.alphanumeric, {
maxRetries: 20,
store: FactoryConfig.GLOBAL_UNIQUE_STORE,
})(5);
args: [5],
});
const courseRuns: CourseRun[] = CourseRunFactory().many(3);

return {
Expand All @@ -53,8 +54,7 @@ export const TargetCourseFactory = factory((): TargetCourse => {
organizations: [],
title: FactoryHelper.unique(() => `${faker.lorem.words(3)}(${code})`, {
maxRetries: 20,
store: FactoryConfig.GLOBAL_UNIQUE_STORE,
})(),
}),
course_runs: courseRuns,
is_graded: true,
position: 1,
Expand Down Expand Up @@ -86,7 +86,9 @@ export const OrganizationLightFactory = factory((): OrganizationLight => {
export const CertificationDefinitionFactory = factory((): CertificateDefinition => {
return {
id: faker.string.uuid(),
title: FactoryHelper.unique(faker.lorem.word)([Math.ceil(Math.random() * 5)]),
title: FactoryHelper.unique(faker.lorem.word, {
args: [Math.ceil(Math.random() * 5)],
}),
description: faker.lorem.sentences(2),
};
});
Expand All @@ -103,7 +105,7 @@ export const CertificateFactory = factory((): Certificate => {
export const CertificateProductFactory = factory((): Product => {
return {
id: faker.string.uuid(),
title: FactoryHelper.unique(faker.lorem.words)(10),
title: FactoryHelper.unique(faker.lorem.words, { args: [10] }),
type: ProductType.CERTIFICATE,
price: faker.number.int(),
price_currency: faker.finance.currencyCode(),
Expand Down Expand Up @@ -135,10 +137,7 @@ export const CourseRunFactory = factory((): CourseRun => {
id: faker.string.uuid(),
resource_link: FactoryHelper.unique(
() => faker.internet.url() + '/' + faker.string.alphanumeric(5),
{
store: FactoryConfig.GLOBAL_UNIQUE_STORE,
},
)(),
),
start: faker.date.past({ years: 0.25 }).toISOString(),
title: faker.lorem.words(Math.ceil(Math.random() * 3)),
state: CourseStateFactory({
Expand All @@ -164,8 +163,8 @@ export const CourseFactory = factory((): CourseMock => {
organizations: OrganizationFactory().many(1),
selling_organizations: OrganizationFactory().many(3),
title: FactoryHelper.unique(faker.lorem.words, {
store: FactoryConfig.GLOBAL_UNIQUE_STORE,
})(Math.ceil(Math.random() * 3)),
args: [Math.ceil(Math.random() * 3)],
}),
products: CertificateCourseProductFactory().many(3),
course_runs: CourseRunFactory().many(3),
state: CourseStateFactory().one(),
Expand All @@ -184,8 +183,8 @@ export const CourseListItemFactory = factory((): CourseListItemMock => {
code: faker.string.alphanumeric(5),
organizations: OrganizationFactory().many(1),
title: FactoryHelper.unique(faker.lorem.words, {
store: FactoryConfig.GLOBAL_UNIQUE_STORE,
})(Math.ceil(Math.random() * 3)),
args: [Math.ceil(Math.random() * 3)],
}),
course_runs: [],
state: CourseStateFactory().one(),
cover: {
Expand All @@ -203,8 +202,8 @@ export const CourseLightFactory = factory((): CourseLight => {
code: faker.string.alphanumeric(5),
organizations: OrganizationLightFactory().many(1),
title: FactoryHelper.unique(faker.lorem.words, {
store: FactoryConfig.GLOBAL_UNIQUE_STORE,
})(Math.ceil(Math.random() * 3)),
args: [Math.ceil(Math.random() * 3)],
}),
products: CertificateCourseProductFactory().many(3),
course_runs: [],
orders: [],
Expand Down Expand Up @@ -281,8 +280,8 @@ export const AddressFactory = factory((): Address => {
is_main: false,
postcode: faker.location.zipCode(),
title: FactoryHelper.unique(faker.lorem.words, {
store: FactoryConfig.GLOBAL_UNIQUE_STORE,
})(5),
args: [5],
}),
};
});

Expand All @@ -295,8 +294,8 @@ export const CreditCardFactory = factory((): CreditCard => {
is_main: false,
last_numbers: faker.finance.creditCardNumber('visa').slice(-4),
title: FactoryHelper.unique(faker.lorem.words, {
store: FactoryConfig.GLOBAL_UNIQUE_STORE,
})(5),
args: [5],
}),
};
});

Expand Down
6 changes: 4 additions & 2 deletions src/frontend/js/utils/test/factories/richie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const CourseStateFutureOpenFactory = factory<CourseState>(() => {
export const CourseRunFactory = factory<CourseRun>(() => {
return {
id: faker.number.int(),
resource_link: FactoryHelper.unique(faker.internet.url)(),
resource_link: FactoryHelper.unique(faker.internet.url),
start: faker.date.past().toISOString(),
end: faker.date.past().toISOString(),
enrollment_start: faker.date.past().toISOString(),
Expand Down Expand Up @@ -94,7 +94,9 @@ export const RichieContextFactory = factory<CommonDataProps['context']>(() => ({
}));

export const CourseLightFactory = factory<Course>(() => {
const organizationName = FactoryHelper.unique(faker.lorem.words)(Math.ceil(Math.random() * 3));
const organizationName = FactoryHelper.unique(faker.lorem.words, {
args: [Math.ceil(Math.random() * 3)],
});
return {
id: faker.string.uuid(),
absolute_url: '',
Expand Down

0 comments on commit 4af8315

Please sign in to comment.