Skip to content
This repository was archived by the owner on Apr 11, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1047 from Shopify/ml-sfapi-client-segment-tests
Browse files Browse the repository at this point in the history
[Storefront API Client] Reorganize tests for server and browser testing
melissaluu authored Nov 9, 2023
2 parents 1b6e032 + e16ff22 commit ba1e0bc
Showing 11 changed files with 212 additions and 147 deletions.
2 changes: 2 additions & 0 deletions .changeset/pretty-windows-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
4 changes: 2 additions & 2 deletions packages/storefront-api-client/README.md
Original file line number Diff line number Diff line change
@@ -78,9 +78,9 @@ const client = createStorefrontApiClient({

| Name | Type | Description |
| -------------- | ------------------------ | ---------------------------------------------------- |
| variables? | `Record<string, any>` | Variable values needed in the graphQL operation |
| variables? | `{[key: string]: string}` | Variable values needed in the graphQL operation |
| apiVersion? | `string` | The Storefront API version to use in the API request |
| customHeaders? | `Record<string, string>` | Customized headers to be included in the API request |
| customHeaders? | `{[key: string]: string}` | Customized headers to be included in the API request |
| retries? | `number` | Alternative number of retries for the request. Retries only occur for requests that were abandoned or if the server responds with a `Too Many Request (429)` or `Service Unavailable (503)` response. Minimum value is `0` and maximum value is `3`.|

## `ClientResponse<TData>`
12 changes: 10 additions & 2 deletions packages/storefront-api-client/package.json
Original file line number Diff line number Diff line change
@@ -47,13 +47,21 @@
"name": "test:browser",
"color": "blue"
},
"testEnvironment": "jsdom"
"testEnvironment": "jsdom",
"testPathIgnorePatterns": [
".rollup.cache/",
"(/__tests__/.*|(\\.|/)server(\\.|/)(test|spec))\\.[jt]sx?$"
]
},
{
"displayName": {
"name": "test:server",
"color": "yellow"
}
},
"testPathIgnorePatterns": [
".rollup.cache/",
"(/__tests__/.*|(\\.|/)browser(\\.|/)(test|spec))\\.[jt]sx?$"
]
}
],
"setupFilesAfterEnv": [
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { GraphQLClient } from "@shopify/graphql-client";

export const mockApiVersions = [
"2023-01",
"2023-04",
"2023-07",
"2023-10",
"2024-01",
"unstable",
];

export const domain = "test-store.myshopify.io";
export const config = {
storeDomain: `https://${domain}`,
apiVersion: "2023-10",
publicAccessToken: "public-token",
};

export const mockApiUrl = `${config.storeDomain}/api/2023-10/graphql.json`;

export const graphqlClientMock: GraphQLClient = {
config: {
url: mockApiUrl,
headers: {},
retries: 0,
},
fetch: jest.fn(),
request: jest.fn(),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { createGraphQLClient } from "@shopify/graphql-client";

import { createStorefrontApiClient } from "../../storefront-api-client";

import { mockApiVersions, graphqlClientMock, config } from "./fixtures";

jest.mock("@shopify/graphql-client", () => {
return {
...jest.requireActual("@shopify/graphql-client"),
createGraphQLClient: jest.fn(),
getCurrentSupportedAPIVersions: () => mockApiVersions,
};
});

describe("Storefront API Client: Browser", () => {
describe("createStorefrontApiClient()", () => {
beforeEach(() => {
(createGraphQLClient as jest.Mock).mockReturnValue(graphqlClientMock);
});

afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});

describe("client initialization", () => {
describe("validations", () => {
it("throws an error when a private access token is provided in a browser environment", () => {
expect(() =>
createStorefrontApiClient({
...config,
publicAccessToken: undefined as any,
privateAccessToken: "private-access-token",
})
).toThrow(
new Error(
"Storefront API Client: private access tokens and headers should only be used in a server-to-server implementation. Use the public API access token in nonserver environments."
)
);
});
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { createGraphQLClient } from "@shopify/graphql-client";

import { createStorefrontApiClient } from "../../storefront-api-client";
import { PRIVATE_ACCESS_TOKEN_HEADER } from "../../constants";

import { mockApiVersions, graphqlClientMock, config } from "./fixtures";

jest.mock("@shopify/graphql-client", () => {
return {
...jest.requireActual("@shopify/graphql-client"),
createGraphQLClient: jest.fn(),
getCurrentSupportedAPIVersions: () => mockApiVersions,
};
});

describe("Storefront API Client: Server", () => {
describe("createStorefrontApiClient()", () => {
beforeEach(() => {
(createGraphQLClient as jest.Mock).mockReturnValue(graphqlClientMock);
});

afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});

describe("client initialization", () => {
describe("validations", () => {
it("throws an error when both public and private access tokens are provided in a server environment", () => {
expect(() =>
createStorefrontApiClient({
...config,
privateAccessToken: "private-token",
} as any)
).toThrow(
new Error(
`Storefront API Client: only provide either a public or private access token`
)
);
});
});
});

describe("client config", () => {
it("returns a config object that includes the provided private access token and not a public access token when in a server environment", () => {
const privateAccessToken = "private-token";

const client = createStorefrontApiClient({
...config,
publicAccessToken: undefined,
privateAccessToken,
});
expect(client.config.privateAccessToken).toBe(privateAccessToken);
expect(client.config.publicAccessToken).toBeUndefined();
});

describe("config headers", () => {
it("returns a header object that includes the private headers when a private access token is provided", () => {
const privateAccessToken = "private-token";

const client = createStorefrontApiClient({
...config,
publicAccessToken: undefined,
privateAccessToken,
});

expect(client.config.headers[PRIVATE_ACCESS_TOKEN_HEADER]).toEqual(
privateAccessToken
);
});
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
import { createGraphQLClient, GraphQLClient } from "@shopify/graphql-client";
import { createGraphQLClient } from "@shopify/graphql-client";

import { createStorefrontApiClient } from "../storefront-api-client";
import { StorefrontApiClient } from "../types";
import { createStorefrontApiClient } from "../../storefront-api-client";
import { StorefrontApiClient } from "../../types";
import {
SDK_VARIANT_HEADER,
DEFAULT_SDK_VARIANT,
SDK_VERSION_HEADER,
DEFAULT_CLIENT_VERSION,
SDK_VARIANT_SOURCE_HEADER,
PUBLIC_ACCESS_TOKEN_HEADER,
PRIVATE_ACCESS_TOKEN_HEADER,
DEFAULT_CONTENT_TYPE,
} from "../constants";
} from "../../constants";

const mockApiVersions = [
"2023-01",
"2023-04",
"2023-07",
"2023-10",
"2024-01",
"unstable",
];
import {
mockApiVersions,
graphqlClientMock,
config,
domain,
mockApiUrl,
} from "./fixtures";

jest.mock("@shopify/graphql-client", () => {
return {
@@ -32,29 +30,11 @@ jest.mock("@shopify/graphql-client", () => {

describe("Storefront API Client", () => {
describe("createStorefrontApiClient()", () => {
const domain = "test-store.myshopify.io";
const config = {
storeDomain: `https://${domain}`,
apiVersion: "2023-10",
publicAccessToken: "public-token",
};
const mockApiUrl = `${config.storeDomain}/api/2023-10/graphql.json`;

const mockFetchResponse = { status: 200 };
const mockRequestResponse = {
data: {},
};

const graphqlClientMock: GraphQLClient = {
config: {
url: mockApiUrl,
headers: {},
retries: 0,
},
fetch: jest.fn(),
request: jest.fn(),
};

beforeEach(() => {
(createGraphQLClient as jest.Mock).mockReturnValue(graphqlClientMock);
});
@@ -237,41 +217,6 @@ describe("Storefront API Client", () => {
)
);
});

if (typeof window === "object") {
describe("browser environment", () => {
it("throws an error when a private access token is provided in a browser environment", () => {
expect(() =>
createStorefrontApiClient({
...config,
publicAccessToken: undefined as any,
privateAccessToken: "private-access-token",
})
).toThrow(
new Error(
"Storefront API Client: private access tokens and headers should only be used in a server-to-server implementation. Use the public API access token in nonserver environments."
)
);
});
});
}

if (typeof window === "undefined") {
describe("server enviornment", () => {
it("throws an error when both public and private access tokens are provided in a server environment", () => {
expect(() =>
createStorefrontApiClient({
...config,
privateAccessToken: "private-token",
} as any)
).toThrow(
new Error(
`Storefront API Client: only provide either a public or private access token`
)
);
});
});
}
});
});

@@ -287,22 +232,6 @@ describe("Storefront API Client", () => {
expect(client.config.privateAccessToken).toBeUndefined();
});

if (typeof window === "undefined") {
describe("server environment", () => {
it("returns a config object that includes the provided private access token and not a public access token when in a server environment", () => {
const privateAccessToken = "private-token";

const client = createStorefrontApiClient({
...config,
publicAccessToken: undefined,
privateAccessToken,
});
expect(client.config.privateAccessToken).toBe(privateAccessToken);
expect(client.config.publicAccessToken).toBeUndefined();
});
});
}

it("returns a config object that includes the provided client name", () => {
const clientName = "test-client";

@@ -382,24 +311,6 @@ describe("Storefront API Client", () => {
);
});

if (typeof window === "undefined") {
describe("server environment", () => {
it("returns a header object that includes the private headers when a private access token is provided", () => {
const privateAccessToken = "private-token";

const client = createStorefrontApiClient({
...config,
publicAccessToken: undefined,
privateAccessToken,
});

expect(
client.config.headers[PRIVATE_ACCESS_TOKEN_HEADER]
).toEqual(privateAccessToken);
});
});
}

it("returns a header object that includes the SDK variant source header when client name is provided", () => {
const clientName = "test-client";

43 changes: 0 additions & 43 deletions packages/storefront-api-client/src/tests/validations.test.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { validatePrivateAccessTokenUsage } from "../../validations";

describe("validatePrivateAccessTokenUsage(): Browser", () => {
it("throws an error when a private token is provided within a browser environment (window is defined)", () => {
const privateAccessToken = "private-token";

expect(() => validatePrivateAccessTokenUsage(privateAccessToken)).toThrow(
new Error(
"Storefront API Client: private access tokens and headers should only be used in a server-to-server implementation. Use the public API access token in nonserver environments."
)
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { validatePrivateAccessTokenUsage } from "../../validations";

describe("validatePrivateAccessTokenUsage(): Server", () => {
it("does not throw an error when only the private token is provided when within a server environment", () => {
const privateAccessToken = "private-token";

expect(() =>
validatePrivateAccessTokenUsage(privateAccessToken)
).not.toThrow();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { validateRequiredAccessTokens } from "../../validations";

describe("validateRequiredAccessToken()", () => {
it("throws an error when both public and private tokens are undefined", () => {
const publicAccessToken = undefined;
const privateAccessToken = undefined;

expect(() =>
validateRequiredAccessTokens(publicAccessToken, privateAccessToken)
).toThrow(
new Error(
"Storefront API Client: a public or private access token must be provided"
)
);
});
});

0 comments on commit ba1e0bc

Please sign in to comment.