Skip to content

Commit

Permalink
feat!: factory API (#9)
Browse files Browse the repository at this point in the history
* feat: initial instance implementation

* test: update snapshots

* feat: complete ModelFactory and working `createMockInstance()`

* feat: add `createMockFactory()`

* feat!: require seed

* fix: build

* chore: upgrade `@prismicio/types` to `v0.2.0`

* test: update snapshots
  • Loading branch information
angeloashmore authored Jul 8, 2022
1 parent 21b882c commit 7cbadce
Show file tree
Hide file tree
Showing 232 changed files with 2,264 additions and 1,409 deletions.
188 changes: 94 additions & 94 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,18 @@
"unit": "nyc --reporter=lcovonly --reporter=text --exclude-after-remap=false ava"
},
"dependencies": {
"@prismicio/types": "^0.1.29",
"@prismicio/types": "^0.2.0",
"change-case": "^4.1.2",
"rand-seed": "^1.0.1"
},
"devDependencies": {
"@size-limit/preset-small-lib": "^7.0.8",
"@typescript-eslint/eslint-plugin": "^5.30.3",
"@typescript-eslint/parser": "^5.30.3",
"@typescript-eslint/eslint-plugin": "^5.30.5",
"@typescript-eslint/parser": "^5.30.5",
"ava": "^4.3.0",
"esbuild": "^0.14.48",
"esbuild-register": "^3.3.3",
"eslint": "^8.18.0",
"eslint": "^8.19.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-tsdoc": "^0.2.16",
Expand Down
58 changes: 58 additions & 0 deletions src/api/createAPIMockFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as prismicT from "@prismicio/types";

import { createFaker, Faker } from "../lib/createFaker";

import { Seed } from "../types";

import { query, MockRestApiQueryConfig } from "./query";
import { ref, MockRestApiRefConfig } from "./ref";
import { repository, MockRestApiRepositoryConfig } from "./repository";
import { tags, MockRestApiTagsConfig } from "./tags";

export const createAPIMockFactory = (
...args: ConstructorParameters<typeof APIMockFactory>
): APIMockFactory => {
return new APIMockFactory(...args);
};

type WithoutFakerConfig<T> = Omit<T, "faker" | "seed">;

type APIMockFactoryConfig =
| {
seed: Seed;
}
| {
faker: Faker;
};

export class APIMockFactory {
private faker: Faker;

constructor(config: APIMockFactoryConfig) {
this.faker = "faker" in config ? config.faker : createFaker(config.seed);
}

get seed() {
return this.faker.seed;
}

query<Document extends prismicT.PrismicDocument = prismicT.PrismicDocument>(
config?: WithoutFakerConfig<MockRestApiQueryConfig<Document>>,
) {
return query({ ...config, faker: this.faker });
}

ref<IsScheduled extends boolean = false>(
config?: WithoutFakerConfig<MockRestApiRefConfig<IsScheduled>>,
) {
return ref({ ...config, faker: this.faker });
}

repository(config?: WithoutFakerConfig<MockRestApiRepositoryConfig>) {
return repository({ ...config, faker: this.faker });
}

tags(config?: WithoutFakerConfig<MockRestApiTagsConfig>) {
return tags({ ...config, faker: this.faker });
}
}
4 changes: 2 additions & 2 deletions src/api/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export type MockRestApiQueryConfig<
export const query = <
Document extends prismicT.PrismicDocument = prismicT.PrismicDocument,
>(
config: MockRestApiQueryConfig<Document> = {},
config: MockRestApiQueryConfig<Document>,
): prismicT.Query<Document> => {
const faker = createFaker(config.seed);
const faker = config.faker || createFaker(config.seed);

const documents = config.documents || [];
const page = Math.max(1, config.page ?? 1);
Expand Down
6 changes: 3 additions & 3 deletions src/api/ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ export type MockRestApiRefValue<IsScheduled extends boolean = false> = Omit<
: { scheduledAt?: never });

export const ref = <IsScheduled extends boolean = false>(
config: MockRestApiRefConfig<IsScheduled> = {},
config: MockRestApiRefConfig<IsScheduled>,
): MockRestApiRefValue<IsScheduled> => {
const faker = createFaker(config.seed);
const faker = config.faker || createFaker(config.seed);

const value: prismicT.Ref = {
id: faker.hash(16),
Expand All @@ -34,7 +34,7 @@ export const ref = <IsScheduled extends boolean = false>(

if (config.isScheduled) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
value.scheduledAt = timestamp({ seed: config.seed })!;
value.scheduledAt = timestamp({ faker })!;
}

return value as MockRestApiRefValue<IsScheduled>;
Expand Down
16 changes: 7 additions & 9 deletions src/api/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,22 @@ export type MockRestApiRepositoryConfig = {
} & MockRestApiConfig;

export const repository = (
config: MockRestApiRepositoryConfig = {},
config: MockRestApiRepositoryConfig,
): prismicT.Repository => {
const faker = createFaker(config.seed);
const faker = config.faker || createFaker(config.seed);

const types = (config.customTypeModels || []).reduce((acc, model) => {
acc[model.id] = model.label;
acc[model.id] = model.label || model.id;

return acc;
}, {} as prismicT.Repository["types"]);

return {
refs: [
ref({ seed: config.seed, isMasterRef: true }),
...(config.withReleases
? [ref({ seed: config.seed }), ref({ seed: config.seed })]
: []),
ref({ faker, isMasterRef: true }),
...(config.withReleases ? [ref({ faker }), ref({ faker })] : []),
],
integrationFieldsRef: ref({ seed: config.seed }).ref,
integrationFieldsRef: ref({ faker }).ref,
types,
languages: [
{
Expand All @@ -39,7 +37,7 @@ export const repository = (
},
],
tags: generateTags({
seed: config.seed,
faker,
min: 1,
max: 10,
}),
Expand Down
7 changes: 5 additions & 2 deletions src/api/tags.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import * as prismicT from "@prismicio/types";

import { createFaker } from "../lib/createFaker";
import { generateTags } from "../lib/generateTags";

import { MockRestApiConfig } from "../types";

export type MockRestApiTagsConfig = MockRestApiConfig;

export const tags = (config: MockRestApiTagsConfig = {}): prismicT.Tags => {
export const tags = (config: MockRestApiTagsConfig): prismicT.Tags => {
const faker = config.faker || createFaker(config.seed);

return generateTags({
seed: config.seed,
faker,
min: 1,
max: 10,
});
Expand Down
52 changes: 52 additions & 0 deletions src/createMockFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { createFaker, Faker } from "./lib/createFaker";

import { Seed } from "./types";

import {
createModelMockFactory,
ModelMockFactory,
} from "./model/createModelMockFactory";
import {
createValueMockFactory,
ValueMockFactory,
} from "./value/createValueMockFactory";
import {
createAPIMockFactory,
APIMockFactory,
} from "./api/createAPIMockFactory";

export const createMockFactory = (
...args: ConstructorParameters<typeof MockFactory>
): MockFactory => {
return new MockFactory(...args);
};

type PrismicMockConfig =
| {
seed: Seed;
faker?: never;
}
| {
faker: Faker;
seed?: never;
};

export class MockFactory {
private faker: Faker;

api: APIMockFactory;
model: ModelMockFactory;
value: ValueMockFactory;

constructor(config: PrismicMockConfig) {
this.faker = config.faker || createFaker(config.seed);

this.api = createAPIMockFactory({ faker: this.faker });
this.model = createModelMockFactory({ faker: this.faker });
this.value = createValueMockFactory({ faker: this.faker });
}

get seed() {
return this.faker.seed;
}
}
16 changes: 16 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
export { createMockFactory, MockFactory } from "./createMockFactory";

export {
createAPIMockFactory,
APIMockFactory,
} from "./api/createAPIMockFactory";
export * as api from "./api";

export {
createModelMockFactory,
ModelMockFactory,
} from "./model/createModelMockFactory";
export * as model from "./model";

export {
createValueMockFactory,
ValueMockFactory,
} from "./value/createValueMockFactory";
export * as value from "./value";
17 changes: 13 additions & 4 deletions src/lib/buildEmbedField.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import * as prismicT from "@prismicio/types";

import { createFaker } from "../lib/createFaker";
import { createFaker, Faker } from "../lib/createFaker";

import { MockValueConfig } from "../types";
import { Seed } from "../types";

type BuildEmbedFieldConfig<
Data extends prismicT.AnyOEmbed = prismicT.AnyOEmbed,
> = {
url?: string;
html?: string;
data: Data;
} & Pick<MockValueConfig, "seed">;
} & (
| {
seed: Seed;
faker?: never;
}
| {
faker: Faker;
seed?: never;
}
);

export const buildEmbedField = <
Data extends prismicT.AnyOEmbed = prismicT.AnyOEmbed,
>(
config: BuildEmbedFieldConfig<Data>,
): prismicT.EmbedField<Data, "filled"> => {
const faker = createFaker(config.seed);
const faker = config.faker || createFaker(config.seed);

return {
embed_url: config.url ?? faker.url(),
Expand Down
21 changes: 16 additions & 5 deletions src/lib/buildImageFieldImage.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
import * as prismicT from "@prismicio/types";
import * as changeCase from "change-case";

import { createFaker } from "../lib/createFaker";
import { createFaker, Faker } from "../lib/createFaker";

import { MockValueStateConfig, MockImageData, MockValueConfig } from "../types";
import { MockValueStateConfig, MockImageData, Seed } from "../types";

type BuildImageFieldConfig<
State extends prismicT.FieldState = prismicT.FieldState,
> = {
imageData: MockImageData;
constraint?: prismicT.CustomTypeModelImageField["config"]["constraint"];
} & Pick<MockValueConfig, "seed"> &
constraint?: NonNullable<
prismicT.CustomTypeModelImageField["config"]
>["constraint"];
} & (
| {
seed: Seed;
faker?: never;
}
| {
faker: Faker;
seed?: never;
}
) &
Pick<MockValueStateConfig<State>, "state">;

export const buildImageFieldImage = <
Expand All @@ -26,7 +37,7 @@ export const buildImageFieldImage = <
copyright: null,
} as prismicT.ImageFieldImage<State>;
} else {
const faker = createFaker(config.seed);
const faker = config.faker || createFaker(config.seed);

const url = new URL(config.imageData.url);

Expand Down
29 changes: 14 additions & 15 deletions src/lib/createFaker.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
import Rand from "rand-seed";

import { Seed } from "../types";
import { FAKER_SEED } from "../constants";
import { lorem, loremWords } from "./lorem";

export const createFaker = (seed: Seed = FAKER_SEED) => {
const normalizedSeed = seed.toString();

if (createFaker.cache[normalizedSeed]) {
return createFaker.cache[normalizedSeed];
} else {
const faker = new Faker(normalizedSeed);
import { lorem, loremWords } from "./lorem";

return (createFaker.cache[normalizedSeed] = faker);
}
export const createFaker = (seed: Seed): Faker => {
return new Faker(seed);
};
createFaker.cache = {} as Record<string, Faker>;

const DAY_MS = 1000 * 60 * 60 * 24;
const YEAR_MS = DAY_MS * 365;
const YEAR_2022_MS = 52 * (YEAR_MS + DAY_MS / 4);

export class Faker {
seed: Seed;

private rand: Rand;

constructor(seed: string) {
this.rand = new Rand(seed);
constructor(seed: Seed) {
this.seed = seed;

this.rand = new Rand(seed.toString());
}

boolean(): boolean {
Expand All @@ -41,7 +36,11 @@ export class Faker {
}

randomElements<T>(elements: readonly T[]): T[] {
return elements.filter(() => this.boolean());
const alwaysInclude = this.randomElement(elements);

return elements.filter(
(element) => element === alwaysInclude || this.boolean(),
);
}

range(min: number, max: number): number {
Expand Down
Loading

0 comments on commit 7cbadce

Please sign in to comment.