Skip to content

Commit 0423d58

Browse files
committed
Update hydrogen RR7 e2e test app to Hydrogen 2025.7.3 and RR 7.12.0
1 parent 1c024dc commit 0423d58

11 files changed

Lines changed: 91 additions & 160 deletions

File tree

dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/app/entry.server.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import '../instrument.server';
2+
import type { EntryContext } from 'react-router';
23
import { HandleErrorFunction, ServerRouter } from 'react-router';
34
import { createContentSecurityPolicy } from '@shopify/hydrogen';
4-
import type { EntryContext } from '@shopify/remix-oxygen';
55
import { renderToReadableStream } from 'react-dom/server';
66
import * as Sentry from '@sentry/react-router/cloudflare';
77

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { createHydrogenContext } from '@shopify/hydrogen';
2+
import { AppSession } from '~/lib/session';
3+
import { CART_QUERY_FRAGMENT } from '~/lib/fragments';
4+
5+
/**
6+
* Creates Hydrogen context for React Router 7.x
7+
*/
8+
export async function createHydrogenRouterContext(request: Request, env: Env, executionContext: ExecutionContext) {
9+
if (!env?.SESSION_SECRET) {
10+
throw new Error('SESSION_SECRET environment variable is not set');
11+
}
12+
13+
const waitUntil = executionContext.waitUntil.bind(executionContext);
14+
const [cache, session] = await Promise.all([caches.open('hydrogen'), AppSession.init(request, [env.SESSION_SECRET])]);
15+
16+
const hydrogenContext = createHydrogenContext({
17+
env,
18+
request,
19+
cache,
20+
waitUntil,
21+
session,
22+
i18n: { language: 'EN', country: 'US' },
23+
cart: {
24+
queryFragment: CART_QUERY_FRAGMENT,
25+
},
26+
});
27+
28+
return hydrogenContext;
29+
}

dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/app/lib/session.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { HydrogenSession } from '@shopify/hydrogen';
2-
import { type Session, type SessionStorage, createCookieSessionStorage } from '@shopify/remix-oxygen';
2+
import { type Session, type SessionStorage, createCookieSessionStorage } from 'react-router';
33

44
/**
55
* This is a custom session implementation for your Hydrogen shop.
@@ -9,12 +9,17 @@ import { type Session, type SessionStorage, createCookieSessionStorage } from '@
99
export class AppSession implements HydrogenSession {
1010
#sessionStorage;
1111
#session;
12+
#isPending = false;
1213

1314
constructor(sessionStorage: SessionStorage, session: Session) {
1415
this.#sessionStorage = sessionStorage;
1516
this.#session = session;
1617
}
1718

19+
get isPending() {
20+
return this.#isPending;
21+
}
22+
1823
static async init(request: Request, secrets: string[]) {
1924
const storage = createCookieSessionStorage({
2025
cookie: {
@@ -48,6 +53,7 @@ export class AppSession implements HydrogenSession {
4853
}
4954

5055
get set() {
56+
this.#isPending = true;
5157
return this.#session.set;
5258
}
5359

dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/app/root.tsx

Lines changed: 8 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as Sentry from '@sentry/react-router/cloudflare';
2-
import { type LoaderFunctionArgs } from '@shopify/remix-oxygen';
2+
import type { LoaderFunctionArgs } from 'react-router';
33
import {
44
Outlet,
55
isRouteErrorResponse,
@@ -9,8 +9,6 @@ import {
99
Scripts,
1010
ScrollRestoration,
1111
} from 'react-router';
12-
import { FOOTER_QUERY, HEADER_QUERY } from '~/lib/fragments';
13-
1412
import { useNonce } from '@shopify/hydrogen';
1513

1614
export type RootLoader = typeof loader;
@@ -57,17 +55,14 @@ export function links() {
5755
}
5856

5957
export async function loader(args: LoaderFunctionArgs) {
60-
// Start fetching non-critical data without blocking time to first byte
61-
const deferredData = loadDeferredData(args);
62-
63-
// Await the critical data required to render initial state of the page
64-
const criticalData = await loadCriticalData(args);
65-
6658
const { env } = args.context;
6759

60+
// Simplified loader for Sentry SDK testing - skip storefront queries
6861
return {
69-
...deferredData,
70-
...criticalData,
62+
header: null,
63+
cart: null,
64+
isLoggedIn: false,
65+
footer: null,
7166
ENV: {
7267
sentryTrace: env.SENTRY_TRACE,
7368
sentryBaggage: env.SENTRY_BAGGAGE,
@@ -77,61 +72,12 @@ export async function loader(args: LoaderFunctionArgs) {
7772
checkoutDomain: env.PUBLIC_CHECKOUT_DOMAIN,
7873
storefrontAccessToken: env.PUBLIC_STOREFRONT_API_TOKEN,
7974
withPrivacyBanner: false,
80-
// localize the privacy banner
81-
country: args.context.storefront.i18n.country,
82-
language: args.context.storefront.i18n.language,
75+
country: 'US',
76+
language: 'EN',
8377
},
8478
};
8579
}
8680

87-
/**
88-
* Load data necessary for rendering content above the fold. This is the critical data
89-
* needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
90-
*/
91-
async function loadCriticalData({ context }: LoaderFunctionArgs) {
92-
const { storefront } = context;
93-
94-
const [header] = await Promise.all([
95-
storefront.query(HEADER_QUERY, {
96-
cache: storefront.CacheLong(),
97-
variables: {
98-
headerMenuHandle: 'main-menu', // Adjust to your header menu handle
99-
},
100-
}),
101-
// Add other queries here, so that they are loaded in parallel
102-
]);
103-
104-
return { header };
105-
}
106-
107-
/**
108-
* Load data for rendering content below the fold. This data is deferred and will be
109-
* fetched after the initial page load. If it's unavailable, the page should still 200.
110-
* Make sure to not throw any errors here, as it will cause the page to 500.
111-
*/
112-
function loadDeferredData({ context }: LoaderFunctionArgs) {
113-
const { storefront, customerAccount, cart } = context;
114-
115-
// defer the footer query (below the fold)
116-
const footer = storefront
117-
.query(FOOTER_QUERY, {
118-
cache: storefront.CacheLong(),
119-
variables: {
120-
footerMenuHandle: 'footer', // Adjust to your footer menu handle
121-
},
122-
})
123-
.catch((error: any) => {
124-
// Log query errors, but don't throw them so the page can still render
125-
console.error(error);
126-
return null;
127-
});
128-
return {
129-
cart: cart.get(),
130-
isLoggedIn: customerAccount.isLoggedIn(),
131-
footer,
132-
};
133-
}
134-
13581
export function Layout({ children }: { children?: React.ReactNode }) {
13682
const nonce = useNonce();
13783

dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/app/routes/loader-error.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useLoaderData } from 'react-router';
2-
import type { LoaderFunction } from '@shopify/remix-oxygen';
2+
import type { LoaderFunction } from 'react-router';
33

44
export default function LoaderError() {
55
useLoaderData();

dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/app/routes/navigate.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { useLoaderData } from 'react-router';
2-
import type { LoaderFunction } from '@shopify/remix-oxygen';
2+
import type { LoaderFunction } from 'react-router';
33

4-
export const loader: LoaderFunction = async ({ params: { id } }) => {
4+
export const loader: LoaderFunction = async ({ params }) => {
5+
const { id } = params as { id: string };
56
if (id === '-1') {
67
throw new Error('Unexpected Server Error');
78
}

dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/env.d.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
/// <reference types="vite/client" />
2-
/// <reference types="@shopify/remix-oxygen" />
32
/// <reference types="@shopify/oxygen-workers-types" />
43

54
// Enhance TypeScript's built-in typings.
@@ -26,12 +25,15 @@ declare global {
2625
PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID: string;
2726
PUBLIC_CUSTOMER_ACCOUNT_API_URL: string;
2827
PUBLIC_CHECKOUT_DOMAIN: string;
28+
SHOP_ID: string;
29+
SENTRY_TRACE?: string;
30+
SENTRY_BAGGAGE?: string;
2931
}
3032
}
3133

3234
declare module 'react-router' {
3335
/**
34-
* Declare local additions to the Remix loader context.
36+
* Declare local additions to the React Router loader context.
3537
*/
3638
interface AppLoadContext {
3739
env: Env;

dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/package.json

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,20 @@
1717
"@sentry/cloudflare": "latest || *",
1818
"@sentry/react-router": "latest || *",
1919
"@sentry/vite-plugin": "^4.6.2",
20-
"@shopify/hydrogen": "2025.5.0",
21-
"@shopify/remix-oxygen": "^3.0.0",
20+
"@shopify/hydrogen": "2025.7.3",
2221
"graphql": "^16.10.0",
2322
"graphql-tag": "^2.12.6",
2423
"isbot": "^5.1.22",
25-
"react": "^18.2.0",
26-
"react-dom": "^18.2.0",
27-
"react-router": "7.6.0",
28-
"react-router-dom": "7.6.0"
24+
"react": "^18.3.1",
25+
"react-dom": "^18.3.1",
26+
"react-router": "7.12.0",
27+
"react-router-dom": "7.12.0"
2928
},
3029
"devDependencies": {
3130
"@graphql-codegen/cli": "5.0.2",
3231
"@playwright/test": "~1.56.0",
33-
"@react-router/dev": "7.6.0",
34-
"@react-router/fs-routes": "7.6.0",
32+
"@react-router/dev": "7.12.0",
33+
"@react-router/fs-routes": "7.12.0",
3534
"@sentry-internal/test-utils": "link:../../../test-utils",
3635
"@shopify/cli": "3.80.4",
3736
"@shopify/hydrogen-codegen": "^0.3.3",

dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/server.ts

Lines changed: 16 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
1-
import {
2-
cartGetIdDefault,
3-
cartSetIdDefault,
4-
createCartHandler,
5-
createCustomerAccountClient,
6-
createStorefrontClient,
7-
storefrontRedirect,
8-
} from '@shopify/hydrogen';
9-
import { type AppLoadContext, createRequestHandler, getStorefrontHeaders } from '@shopify/remix-oxygen';
10-
import { CART_QUERY_FRAGMENT } from '~/lib/fragments';
11-
import { AppSession } from '~/lib/session';
1+
import * as serverBuild from 'virtual:react-router/server-build';
2+
import { createRequestHandler, storefrontRedirect } from '@shopify/hydrogen';
3+
import { createHydrogenRouterContext } from '~/lib/context';
124
import { wrapRequestHandler } from '@sentry/cloudflare';
135
// Virtual entry point for the app
146
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -18,17 +10,6 @@ import * as serverBuild from 'virtual:react-router/server-build';
1810
/**
1911
* Export a fetch handler in module format.
2012
*/
21-
type Env = {
22-
SESSION_SECRET: string;
23-
PUBLIC_STOREFRONT_API_TOKEN: string;
24-
PRIVATE_STOREFRONT_API_TOKEN: string;
25-
PUBLIC_STORE_DOMAIN: string;
26-
PUBLIC_STOREFRONT_ID: string;
27-
PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID: string;
28-
PUBLIC_CUSTOMER_ACCOUNT_API_URL: string;
29-
// Add any other environment variables your app expects here
30-
};
31-
3213
export default {
3314
async fetch(request: Request, env: Env, executionContext: ExecutionContext): Promise<Response> {
3415
return wrapRequestHandler(
@@ -45,82 +26,35 @@ export default {
4526
},
4627
async () => {
4728
try {
48-
/**
49-
* Open a cache instance in the worker and a custom session instance.
50-
*/
51-
if (!env?.SESSION_SECRET) {
52-
throw new Error('SESSION_SECRET environment variable is not set');
53-
}
54-
55-
const waitUntil = executionContext.waitUntil.bind(executionContext);
56-
const [cache, session] = await Promise.all([
57-
caches.open('hydrogen'),
58-
AppSession.init(request, [env.SESSION_SECRET]),
59-
]);
60-
61-
/**
62-
* Create Hydrogen's Storefront client.
63-
*/
64-
const { storefront } = createStorefrontClient({
65-
cache,
66-
waitUntil,
67-
i18n: { language: 'EN', country: 'US' },
68-
publicStorefrontToken: env.PUBLIC_STOREFRONT_API_TOKEN,
69-
privateStorefrontToken: env.PRIVATE_STOREFRONT_API_TOKEN,
70-
storeDomain: env.PUBLIC_STORE_DOMAIN,
71-
storefrontId: env.PUBLIC_STOREFRONT_ID,
72-
storefrontHeaders: getStorefrontHeaders(request),
73-
});
74-
75-
/**
76-
* Create a client for Customer Account API.
77-
*/
78-
const customerAccount = createCustomerAccountClient({
79-
waitUntil,
80-
request,
81-
session,
82-
customerAccountId: env.PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID,
83-
shopId: env.PUBLIC_STORE_DOMAIN,
84-
});
85-
86-
/*
87-
* Create a cart handler that will be used to
88-
* create and update the cart in the session.
89-
*/
90-
const cart = createCartHandler({
91-
storefront,
92-
customerAccount,
93-
getCartId: cartGetIdDefault(request.headers),
94-
setCartId: cartSetIdDefault(),
95-
cartQueryFragment: CART_QUERY_FRAGMENT,
96-
});
29+
const hydrogenContext = await createHydrogenRouterContext(request, env, executionContext);
9730

9831
/**
99-
* Create a Remix request handler and pass
100-
* Hydrogen's Storefront client to the loader context.
32+
* Create a Hydrogen request handler that internally
33+
* delegates to React Router for routing and rendering.
10134
*/
10235
const handleRequest = createRequestHandler({
10336
build: serverBuild,
10437
mode: process.env.NODE_ENV,
105-
getLoadContext: (): AppLoadContext => ({
106-
session,
107-
storefront,
108-
customerAccount,
109-
cart,
110-
env,
111-
waitUntil,
112-
}),
38+
getLoadContext: () => hydrogenContext,
11339
});
11440

11541
const response = await handleRequest(request);
11642

43+
if (hydrogenContext.session.isPending) {
44+
response.headers.set('Set-Cookie', await hydrogenContext.session.commit());
45+
}
46+
11747
if (response.status === 404) {
11848
/**
11949
* Check for redirects only when there's a 404 from the app.
12050
* If the redirect doesn't exist, then `storefrontRedirect`
12151
* will pass through the 404 response.
12252
*/
123-
return storefrontRedirect({ request, response, storefront });
53+
return storefrontRedirect({
54+
request,
55+
response,
56+
storefront: hydrogenContext.storefront,
57+
});
12458
}
12559

12660
return response;

dev-packages/e2e-tests/test-applications/hydrogen-react-router-7/tsconfig.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
{
2-
"include": ["server.ts", "./app/**/*.d.ts", "./app/**/*.ts", "./app/**/*.tsx", ".react-router/types/**/*"],
2+
"include": [
3+
"env.d.ts",
4+
"globals.d.ts",
5+
"virtual-modules.d.ts",
6+
"server.ts",
7+
"./app/**/*.d.ts",
8+
"./app/**/*.ts",
9+
"./app/**/*.tsx",
10+
".react-router/types/**/*"
11+
],
312
"compilerOptions": {
413
"lib": ["DOM", "DOM.Iterable", "ES2022"],
514
"isolatedModules": true,

0 commit comments

Comments
 (0)