Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(nova-react-test-utils): refactor for better dependency split #119

Merged
Merged
Show file tree
Hide file tree
Changes from 5 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "refactor for better dependency separation",
"packageName": "@nova/react-test-utils",
"email": "[email protected]",
"dependentChangeType": "patch"
}
21 changes: 11 additions & 10 deletions packages/nova-react-test-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@
"@types/react-relay": {
"optional": true,
"reason": "This package is only required if you are using Relay as your GraphQL client."
},
"@types/relay-test-utils": {
"optional": true,
"reason": "This package is only required if you are using Relay as your GraphQL client."
}
},
"dependencies": {
Expand All @@ -62,7 +66,6 @@
"@nova/types": "^1.5.1",
"@types/react": "^17 || ^18",
"@types/relay-runtime": ">16",
"@types/relay-test-utils": ">16",
"graphql": "^15.5.0",
"invariant": "^2.2.4",
"tslib": "^2.2.0",
Expand All @@ -79,6 +82,7 @@
"@testing-library/react": "^15.0.0",
"@types/jest": "^29.2.0",
"@types/react-relay": "^16.0.0",
"@types/relay-test-utils": "^17.0.0",
"@types/react-test-renderer": "^18.3.0",
"monorepo-scripts": "*",
"react": "^18.3.1",
Expand All @@ -96,19 +100,16 @@
"access": "public",
"exports": {
".": {
"import": "./lib/relay/index.mjs",
"require": "./lib/relay/index.js",
"types": "./lib/relay/index.d.ts"
"import": "./src/relay/index.ts",
"require": "./lib/relay/index.js"
},
"./relay": {
"import": "./lib/relay/index.mjs",
"require": "./lib/relay/index.js",
"types": "./lib/relay/index.d.ts"
"import": "./src/relay/index.ts",
"require": "./lib/relay/index.js"
},
"./apollo": {
"import": "./lib/apollo/index.mjs",
"require": "./lib/apollo/index.js",
"types": "./lib/apollo/index.d.ts"
"import": "./src/apollo/index.ts",
"require": "./lib/apollo/index.js"
}
},
"publishConfig": {
Expand Down
4 changes: 2 additions & 2 deletions packages/nova-react-test-utils/src/apollo/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { NovaMockEnvironmentProvider } from "../shared/nova-mock-environment";
export type { NovaMockEnvironment } from "../shared/nova-mock-environment";
export { NovaMockEnvironmentProvider } from "./nova-mock-environment";
export type { NovaMockEnvironment } from "./nova-mock-environment";
export { prepareStoryContextForTest } from "../shared/storybook-nova-decorator-shared";
export type {
WithNovaEnvironment,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as React from "react";
import {
type NovaMockEnvironment as GenericNovaMockEnvironment,
NovaMockEnvironmentProvider as GenericNovaMockEnvironmentProvider,
type NovaMockEnvironmentProviderProps,
} from "../shared/nova-mock-environment";
import type { NovaGraphQL } from "@nova/types";
import type { MockFunctions } from "@graphitation/apollo-mock-client";
import type { Environment } from "../shared/shared-utils";
import type { GraphQLTaggedNode } from "@nova/react";

type ApolloNovaGraphQL = NovaGraphQL & {
mock: MockFunctions<unknown, GraphQLTaggedNode>;
};

export type NovaMockEnvironment<T extends Environment = "test"> =
GenericNovaMockEnvironment<T, ApolloNovaGraphQL> & { type: "apollo" };

export const NovaMockEnvironmentProvider = <T extends Environment = "test">({
children,
environment,
}: React.PropsWithChildren<
NovaMockEnvironmentProviderProps<T, ApolloNovaGraphQL>
>) => {
return (
<GenericNovaMockEnvironmentProvider environment={environment}>
{children}
</GenericNovaMockEnvironmentProvider>
);
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { NovaMockEnvironment } from "../shared/nova-mock-environment";
import { ApolloMockPayloadGenerator } from "./test-utils";
import type { GraphQLSchema } from "graphql";
import * as React from "react";
Expand All @@ -16,6 +15,7 @@ import type { MakeDecoratorResult } from "../shared/shared-utils";
import { defaultTrigger, defaultBubble } from "../shared/shared-utils";
import type { ReactRenderer } from "@storybook/react";
import type { PlayFunctionContext } from "@storybook/types";
import type { NovaMockEnvironment } from "./nova-mock-environment";

type MockClientOptions = Parameters<typeof createMockClient>[1];

Expand All @@ -30,7 +30,7 @@ export const getNovaApolloDecorator: (
const createEnvironment = () => createNovaEnvironment(schema, rest);
const initializeGenerator = (
parameters: WithNovaEnvironment["novaEnvironment"],
environment: NovaMockEnvironment<"apollo", "storybook">,
environment: NovaMockEnvironment<"storybook">,
) => {
const mockResolvers = parameters?.resolvers;
const generate = generateFunction ?? ApolloMockPayloadGenerator.generate;
Expand All @@ -46,9 +46,9 @@ export const getNovaApolloDecorator: (
function createNovaEnvironment(
schema: GraphQLSchema,
options?: MockClientOptions,
): NovaMockEnvironment<"apollo", "storybook"> {
): NovaMockEnvironment<"storybook"> {
const client = createMockClient(schema, options);
const env: NovaMockEnvironment<"apollo", "storybook"> = {
const env: NovaMockEnvironment<"storybook"> = {
type: "apollo",
graphql: {
...(GraphQLHooks as NovaGraphQL),
Expand All @@ -69,10 +69,16 @@ function createNovaEnvironment(

export const getNovaApolloEnvironmentForStory = (
context: PlayFunctionContext<ReactRenderer>,
): NovaMockEnvironment<"apollo", "storybook"> => {
): NovaMockEnvironment<"storybook"> => {
const env = getNovaEnvironmentForStory(context);
if (env.type !== "apollo") {
if (!isApolloMockEnv(env)) {
throw new Error("Expected relay environment to be present on context");
}
return env;
};

const isApolloMockEnv = (
env: ReturnType<typeof getNovaEnvironmentForStory>,
): env is NovaMockEnvironment<"storybook"> => {
return env.type === "apollo";
};
2 changes: 1 addition & 1 deletion packages/nova-react-test-utils/src/apollo/test-utils.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from "react";
import type { NovaMockEnvironment } from "../shared/nova-mock-environment";
import { ApolloProvider } from "@apollo/client";
import type { GraphQLSchema } from "graphql";
import type { MockFunctions } from "@graphitation/apollo-mock-client";
Expand All @@ -9,6 +8,7 @@ import type { OperationDescriptor } from "@graphitation/graphql-js-operation-pay
import { generate as payloadGenerator } from "@graphitation/graphql-js-operation-payload-generator";
import type { GraphQLTaggedNode } from "@nova/react";
import type { NovaGraphQL } from "@nova/types";
import type { NovaMockEnvironment } from "./nova-mock-environment";

type MockClientOptions = Parameters<typeof createMockClient>[1];

Expand Down
24 changes: 18 additions & 6 deletions packages/nova-react-test-utils/src/apollo/utils.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ import type { ReactTestRenderer } from "react-test-renderer";
import { act, create as createTestRenderer } from "react-test-renderer";
import type { EntityCommand, EventWrapper } from "@nova/types";
import { graphql, useLazyLoadQuery } from "@nova/react";
import { createNovaApolloEnvironment, ApolloMockPayloadGenerator } from "./test-utils";
import type { NovaMockEnvironment } from "../shared/nova-mock-environment";
import { NovaMockEnvironmentProvider } from "../shared/nova-mock-environment";
import { getApolloOperationName, getApolloOperationType } from "./operation-utils";
import {
createNovaApolloEnvironment,
ApolloMockPayloadGenerator,
} from "./test-utils";
import {
getApolloOperationName,
getApolloOperationType,
} from "./operation-utils";
import {
type NovaMockEnvironment,
NovaMockEnvironmentProvider,
} from "./nova-mock-environment";

const schema = buildASTSchema(
parse(`
Expand Down Expand Up @@ -116,7 +124,9 @@ describe(createNovaApolloEnvironment, () => {
});

expect(
getApolloOperationName(environment.graphql.mock.getMostRecentOperation()),
getApolloOperationName(
environment.graphql.mock.getMostRecentOperation(),
),
).toEqual("MockTestQuery");
});

Expand All @@ -130,7 +140,9 @@ describe(createNovaApolloEnvironment, () => {
});

expect(
getApolloOperationType(environment.graphql.mock.getMostRecentOperation()),
getApolloOperationType(
environment.graphql.mock.getMostRecentOperation(),
),
).toEqual("query");
});
});
Expand Down
4 changes: 2 additions & 2 deletions packages/nova-react-test-utils/src/relay/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { NovaMockEnvironmentProvider } from "../shared/nova-mock-environment";
export type { NovaMockEnvironment } from "../shared/nova-mock-environment";
export { NovaMockEnvironmentProvider } from "./nova-mock-environment";
export type { NovaMockEnvironment } from "./nova-mock-environment";
export { prepareStoryContextForTest } from "../shared/storybook-nova-decorator-shared";
export type {
WithNovaEnvironment,
Expand Down
29 changes: 29 additions & 0 deletions packages/nova-react-test-utils/src/relay/nova-mock-environment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as React from "react";
import {
type NovaMockEnvironment as GenericNovaMockEnvironment,
NovaMockEnvironmentProvider as GenericNovaMockEnvironmentProvider,
type NovaMockEnvironmentProviderProps,
} from "../shared/nova-mock-environment";
import type { NovaGraphQL } from "@nova/types";
import type { MockEnvironment } from "relay-test-utils";
import type { Environment } from "../shared/shared-utils";

type RelayNovaGraphQL = NovaGraphQL & {
mock: MockEnvironment["mock"];
};

export type NovaMockEnvironment<T extends Environment = "test"> =
GenericNovaMockEnvironment<T, RelayNovaGraphQL> & { type: "relay" };

export const NovaMockEnvironmentProvider = <T extends Environment = "test">({
children,
environment,
}: React.PropsWithChildren<
NovaMockEnvironmentProviderProps<T, RelayNovaGraphQL>
>) => {
return (
<GenericNovaMockEnvironmentProvider environment={environment}>
{children}
</GenericNovaMockEnvironmentProvider>
);
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { NovaMockEnvironment } from "../shared/nova-mock-environment";
import {
getDecorator,
getNovaEnvironmentForStory,
Expand All @@ -14,6 +13,7 @@ import { createMockEnvironment } from "relay-test-utils";
import type { GraphQLSchema } from "graphql";
import type { ReactRenderer } from "@storybook/react";
import type { PlayFunctionContext } from "@storybook/types";
import type { NovaMockEnvironment } from "./nova-mock-environment";

type RelayEnvironmentOptions = Parameters<typeof createMockEnvironment>[0];

Expand All @@ -29,7 +29,7 @@ export const getNovaRelayDecorator: (
const relayMockPayloadGenerator = new RelayMockPayloadGenerator(schema);
const initializeGenerator = (
parameters: WithNovaEnvironment["novaEnvironment"],
environment: NovaMockEnvironment<"relay", "storybook">,
environment: NovaMockEnvironment<"storybook">,
) => {
const mockResolvers = parameters?.resolvers;
const generate =
Expand All @@ -46,9 +46,9 @@ export const getNovaRelayDecorator: (

function createNovaRelayEnvironment(
options?: RelayEnvironmentOptions,
): NovaMockEnvironment<"relay", "storybook"> {
): NovaMockEnvironment<"storybook"> {
const relayEnvironment = createMockEnvironment(options);
const env: NovaMockEnvironment<"relay", "storybook"> = {
const env: NovaMockEnvironment<"storybook"> = {
type: "relay",
graphql: {
...novaGraphql,
Expand All @@ -73,10 +73,14 @@ function createNovaRelayEnvironment(

export const getNovaRelayEnvironmentForStory = (
context: PlayFunctionContext<ReactRenderer>,
): NovaMockEnvironment<"relay", "storybook"> => {
): NovaMockEnvironment<"storybook"> => {
const env = getNovaEnvironmentForStory(context);
if (env.type !== "relay") {
if (!isRelayMockEnv(env)) {
throw new Error("Expected relay environment to be present on context");
}
return env;
};

const isRelayMockEnv = (
env: ReturnType<typeof getNovaEnvironmentForStory>,
): env is NovaMockEnvironment<"storybook"> => env.type === "relay";
4 changes: 2 additions & 2 deletions packages/nova-react-test-utils/src/relay/test-utils.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from "react";
import type { NovaMockEnvironment } from "../shared/nova-mock-environment";
import { type GraphQLSchema, parse as parseGraphQL } from "graphql";
import {
type MockResolvers,
Expand All @@ -9,6 +8,7 @@ import type { OperationDescriptor as RelayOperationDescriptor } from "relay-runt
import { novaGraphql } from "./nova-relay-graphql";
import { createMockEnvironment } from "relay-test-utils";
import { RelayEnvironmentProvider } from "react-relay";
import type { NovaMockEnvironment } from "./nova-mock-environment";

export class RelayMockPayloadGenerator {
public gqlSchema: GraphQLSchema;
Expand Down Expand Up @@ -54,7 +54,7 @@ export function createNovaRelayMockEnvironment(
options?: RelayEnvironmentOptions,
) {
const relayEnvironment = createMockEnvironment(options);
const env: NovaMockEnvironment<"relay", "test"> = {
const env: NovaMockEnvironment<"test"> = {
commanding: {
trigger: jest.fn(),
},
Expand Down
38 changes: 12 additions & 26 deletions packages/nova-react-test-utils/src/shared/nova-mock-environment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,13 @@ import type {
NovaGraphQL,
NovaEventing,
} from "@nova/types";
import type { MockFunctions as ApolloMockFunctions } from "@graphitation/apollo-mock-client";
import type { MockEnvironment } from "relay-test-utils";
import type { GraphQLTaggedNode } from "@nova/react";
import {
mapEventMetadata,
NovaCentralizedCommandingProvider,
NovaEventingProvider,
NovaGraphQLProvider,
} from "@nova/react";
import type { Variant } from "./shared-utils";

type RelayMockFunctions = MockEnvironment["mock"];
type Environment = "test" | "storybook";
import type { Environment, Variant } from "./shared-utils";

type Commanding<T extends Environment> = T extends "test"
? jest.Mocked<NovaCentralizedCommanding>
Expand All @@ -27,43 +21,35 @@ type Eventing<T extends Environment> = T extends "test"
? jest.Mocked<NovaEventing>
: NovaEventing;

export type NovaMockEnvironment<
V extends Variant = "apollo",
export interface NovaMockEnvironment<
T extends Environment = "test",
> = {
E extends NovaGraphQL = NovaGraphQL,
> {
commanding: Commanding<T>;
eventing: Eventing<T>;
/**
* A React component that will be used to wrap the NovaFacadeProvider children. This is used by the test-utils to
* inject a ApolloProvider.
*/
providerWrapper: ComponentType<PropsWithChildren>;
} & (V extends "apollo"
? {
type: "apollo";
graphql: NovaGraphQL & {
mock: ApolloMockFunctions<unknown, GraphQLTaggedNode>;
};
}
: {
type: "relay";
graphql: NovaGraphQL & { mock: RelayMockFunctions };
});
graphql: E;
type: Variant;
}

interface NovaMockEnvironmentProviderProps<
V extends Variant,
export interface NovaMockEnvironmentProviderProps<
T extends Environment,
E extends NovaGraphQL,
> {
environment: NovaMockEnvironment<V, T>;
environment: NovaMockEnvironment<T, E>;
}

export const NovaMockEnvironmentProvider = <
V extends Variant = "apollo",
T extends Environment = "test",
E extends NovaGraphQL = NovaGraphQL,
>({
children,
environment,
}: React.PropsWithChildren<NovaMockEnvironmentProviderProps<V, T>>) => {
}: React.PropsWithChildren<NovaMockEnvironmentProviderProps<T, E>>) => {
return (
<NovaEventingProvider
eventing={environment.eventing}
Expand Down
Loading
Loading