Skip to content

Commit

Permalink
feat: add api mocks
Browse files Browse the repository at this point in the history
  • Loading branch information
angeloashmore committed Aug 20, 2021
1 parent 1e29322 commit 8e0ef1b
Show file tree
Hide file tree
Showing 36 changed files with 1,148 additions and 256 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@
},
"license": "Apache-2.0",
"author": "Prismic <[email protected]> (https://prismic.io)",
"sideEffects": false,
"type": "module",
"exports": {
".": {
"require": "./dist/index.cjs",
"import": "./dist/index.mjs"
},
"./api": {
"require": "./dist/api/index.cjs",
"import": "./dist/api/index.mjs"
},
"./model": {
"require": "./dist/model/index.cjs",
"import": "./dist/model/index.mjs"
Expand Down
11 changes: 11 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export { repository } from "./repository";
export type { MockRestApiRepositoryConfig } from "./repository";

export { ref } from "./ref";
export type { MockRestApiRefConfig, MockRestApiRefValue } from "./ref";

export { query } from "./query";
export type { MockRestApiQueryConfig } from "./query";

export { tags } from "./tags";
export type { MockRestApiTagsConfig } from "./tags";
38 changes: 38 additions & 0 deletions src/api/query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as prismicT from "@prismicio/types";

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

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

export type MockRestApiQueryConfig<
Document extends prismicT.PrismicDocument = prismicT.PrismicDocument,
> = {
documents?: Document[];
page?: number;
pageSize?: number;
} & MockRestApiConfig;

export const query = <
Document extends prismicT.PrismicDocument = prismicT.PrismicDocument,
>(
config: MockRestApiQueryConfig<Document> = {},
): prismicT.Query<Document> => {
const faker = createFaker(config.seed);

const documents = config.documents || [];
const page = Math.max(1, config.page ?? 1);
const pageSize = Math.min(100, Math.max(1, config.pageSize ?? 100));
const totalPages = Math.ceil(documents.length / pageSize);
const results = documents.slice((page - 1) * pageSize, page * pageSize);

return {
page,
next_page: page < totalPages ? faker.internet.url() : null,
prev_page: page > 1 ? faker.internet.url() : null,
total_pages: totalPages,
results_size: results.length,
results_per_page: pageSize,
total_results_size: documents.length,
results,
};
};
41 changes: 41 additions & 0 deletions src/api/ref.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as prismicT from "@prismicio/types";
import * as changeCase from "change-case";

import { createFaker } from "../lib/createFaker";
import { MockRestApiConfig } from "../types";
import { timestamp } from "../value";

export type MockRestApiRefConfig<IsScheduled extends boolean = false> = {
isMasterRef?: boolean;
isScheduled?: IsScheduled;
} & MockRestApiConfig;

export type MockRestApiRefValue<IsScheduled extends boolean = false> = Omit<
prismicT.Ref,
"scheduledAt"
> &
(IsScheduled extends true
? { scheduledAt: string }
: { scheduledAt?: never });

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

const value: prismicT.Ref = {
id: faker.git.shortSha(),
ref: faker.git.shortSha(),
isMasterRef: config.isMasterRef ?? false,
label: config.isMasterRef
? "Master"
: changeCase.capitalCase(faker.company.bsNoun()),
};

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

return value as MockRestApiRefValue<IsScheduled>;
};
54 changes: 54 additions & 0 deletions src/api/repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as prismicT from "@prismicio/types";
import * as changeCase from "change-case";

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

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

export type MockRestApiRepositoryConfig = {
customTypeModels?: prismicT.CustomTypeModel[];
withReleases?: boolean;
} & MockRestApiConfig;

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

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

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

return {
refs: [
ref({ seed: config.seed, isMasterRef: true }),
...(config.withReleases
? [ref({ seed: config.seed }), ref({ seed: config.seed })]
: []),
],
integrationFieldsRef: ref({ seed: config.seed }).ref,
types,
languages: [
{
id: faker.lorem.word(),
name: changeCase.capitalCase(faker.lorem.word()),
},
],
tags: generateTags({
seed: config.seed,
min: 1,
max: 10,
}),
forms: {},
license: "All Rights Reserved",
version: faker.git.shortSha(),
bookmarks: {},
experiments: {},
oauth_token: faker.internet.url(),
oauth_initiate: faker.internet.url(),
};
};
15 changes: 15 additions & 0 deletions src/api/tags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as prismicT from "@prismicio/types";

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

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

export type MockRestApiTagsConfig = MockRestApiConfig;

export const tags = (config: MockRestApiTagsConfig = {}): prismicT.Tags => {
return generateTags({
seed: config.seed,
min: 1,
max: 10,
});
};
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * as api from "./api";
export * as model from "./model";
export * as value from "./value";
12 changes: 10 additions & 2 deletions src/lib/generateTags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@ import { createFaker } from "../lib/createFaker";

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

type GenerateTagsConfig = Pick<MockValueConfig, "seed">;
type GenerateTagsConfig = {
min?: number;
max?: number;
} & Pick<MockValueConfig, "seed">;

export const generateTags = (config: GenerateTagsConfig): string[] => {
const faker = createFaker(config.seed);

return Array(faker.datatype.number(2))
return Array(
faker.datatype.number({
min: config.min ?? 0,
max: config.max ?? 2,
}),
)
.fill(undefined)
.map(() =>
changeCase.capitalCase(
Expand Down
4 changes: 4 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export interface MockEmbedData {
thumbnail_width: number | null;
}

export type MockRestApiConfig = {
seed?: number;
};

export type MockModelConfig = {
seed?: number;
};
Expand Down
6 changes: 5 additions & 1 deletion src/value/customType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { timestamp } from "./timestamp";
export type MockCustomTypeValueConfig<
Model extends prismicT.CustomTypeModel = prismicT.CustomTypeModel,
> = {
withURL?: boolean;
sharedSliceModels?: prismicT.SharedSliceModel[];
configs?: ValueForModelMapConfigs;
} & MockValueConfig<Model>;
Expand All @@ -34,15 +35,18 @@ export const customType = <
{},
...Object.values(model.json),
) as prismicT.CustomTypeModelTab;

const hasUID = Object.values(fieldModelsMap).some(
(fieldModel) => fieldModel.type === prismicT.CustomTypeModelFieldType.UID,
);

const withURL = config.withURL ?? true;

return {
type: model.id,
id: faker.git.shortSha(),
uid: hasUID ? changeCase.snakeCase(faker.lorem.words(2)) : null,
url: faker.internet.url(),
url: withURL ? faker.internet.url() : null,
href: faker.internet.url(),
lang: faker.lorem.word(),
tags: generateTags({ seed: config.seed }),
Expand Down
45 changes: 45 additions & 0 deletions test/api-query.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import test from "ava";

import { snapshotTwiceMacro } from "./__testutils__/snapshotTwiceMacro";

import * as mock from "../src";

test("creates a mock query value", snapshotTwiceMacro, mock.api.query);

test("supports custom seed", snapshotTwiceMacro, () =>
mock.api.query({ seed: 1 }),
);

test("can be configured to return a set of documents", (t) => {
const documents = Array(20)
.fill(undefined)
.map(() => mock.value.document());

const actual = mock.api.query({ documents });

t.deepEqual(actual.results, documents);
});

test("can be configured to return paginated results", (t) => {
const documents = Array(100)
.fill(undefined)
.map(() => mock.value.document());
const page = 2;
const pageSize = 10;

const actual = mock.api.query({
documents,
page: 2,
pageSize: 10,
});

t.is(actual.results.length, pageSize);
t.is(actual.page, page);
t.is(actual.total_pages, Math.ceil(documents.length / pageSize));
t.is(typeof actual.next_page, "string");
t.is(typeof actual.prev_page, "string");
t.is(actual.results_size, pageSize);
t.is(actual.results_per_page, pageSize);

t.deepEqual(actual.results, documents.slice(10, 20));
});
24 changes: 24 additions & 0 deletions test/api-ref.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import test from "ava";

import { snapshotTwiceMacro } from "./__testutils__/snapshotTwiceMacro";

import * as mock from "../src";

test("creates a mock ref value", snapshotTwiceMacro, mock.api.ref);

test("supports custom seed", snapshotTwiceMacro, () =>
mock.api.ref({ seed: 1 }),
);

test("can be configured to return a master ref", (t) => {
const actual = mock.api.ref({ isMasterRef: true });

t.is(actual.isMasterRef, true);
t.is(actual.label, "Master");
});

test("can be configured to return a scheduled ref", (t) => {
const actual = mock.api.ref({ isScheduled: true });

t.is(typeof actual.scheduledAt, "string");
});
32 changes: 32 additions & 0 deletions test/api-repository.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import test from "ava";

import { snapshotTwiceMacro } from "./__testutils__/snapshotTwiceMacro";

import * as mock from "../src";

test(
"creates a mock repository value",
snapshotTwiceMacro,
mock.api.repository,
);

test("supports custom seed", snapshotTwiceMacro, () =>
mock.api.repository({ seed: 1 }),
);

test("can be configured to include releases", (t) => {
const actual = mock.api.repository({ withReleases: true });

t.true(actual.refs.filter((ref) => !ref.isMasterRef).length > 0);
});

test("can be configured to include custom types", (t) => {
const customTypeModels = [mock.model.customType(), mock.model.customType()];

const actual = mock.api.repository({ customTypeModels });

t.deepEqual(actual.types, {
[customTypeModels[0].id]: customTypeModels[0].label,
[customTypeModels[1].id]: customTypeModels[1].label,
});
});
11 changes: 11 additions & 0 deletions test/api-tags.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import test from "ava";

import { snapshotTwiceMacro } from "./__testutils__/snapshotTwiceMacro";

import * as mock from "../src";

test("creates a mock tags value", snapshotTwiceMacro, mock.api.tags);

test("supports custom seed", snapshotTwiceMacro, () =>
mock.api.tags({ seed: 1 }),
);
24 changes: 24 additions & 0 deletions test/restApi-ref.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import test from "ava";

import { snapshotTwiceMacro } from "./__testutils__/snapshotTwiceMacro";

import * as mock from "../src";

test("creates a mock ref value", snapshotTwiceMacro, mock.api.ref);

test("supports custom seed", snapshotTwiceMacro, () =>
mock.api.ref({ seed: 1 }),
);

test("can be configured to return a master ref", (t) => {
const actual = mock.api.ref({ isMasterRef: true });

t.is(actual.isMasterRef, true);
t.is(actual.label, "Master");
});

test("can be configured to return a scheduled ref", (t) => {
const actual = mock.api.ref({ isScheduled: true });

t.is(typeof actual.scheduledAt, "string");
});
Loading

0 comments on commit 8e0ef1b

Please sign in to comment.