Enable E2E Tests with Playwright in Remix Shopify App #890
Description
I want to integrate E2E tests using Playwright in my Remix Shopify app, which is currently set up with Vite and Vitest for component-level testing. I'm using jsdom with React Testing Library for testing frontend components, and Remix Stub for testing actions and loaders.
Here's the current setup:
Vite + Vitest for environment configuration and testing setup
jsdom + React Testing Library for frontend component testing
Remix Stub for testing Remix actions and loaders
Vite's setupFiles for mocking the Shopify app and Polaris provider
Shopify App setup using @shopify/shopify-app-remix for session storage and app configuration
I been getting a few errors such as:
Error: The shopify global is not defined. This likely means the App Bridge script tag was not added correctly to this page
TestingLibraryElementError: Unable to find an element with the text: 🤖 Dev & Testing. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
I based most of my setup on this pr:
https://github.com/Shopify/shopify-app-js/pull/1207/commits
Do you think it would be possible to create an official guide on setting up end-to-end (E2E) tests with this stack in the future? I understand that many people use different configurations based on their specific needs, but having an official reference configuration would be incredibly beneficial for the community. It could serve as a solid starting point for others looking to implement E2E tests with this setup.
Thanks!
export default mergeConfig(
rootConfig,
defineConfig({
test: {
environment: 'jsdom',
include: ['*.test.?(c|m)[jt]s?(x)', '**/*.test.?(c|m)[jt]s?(x)'],
setupFiles: ['../test-utilities/shopify.setup.js'],
},
})
);
import { shopifyApp } from '@shopify/shopify-app-remix/server';
import { testConfig } from '@shopify/shopify-app-remix/test-helpers';
import { MemorySessionStorage } from '@shopify/shopify-app-session-storage-memory';
const SHOPIFY_API_KEY = process.env.SHOPIFY_API_KEY || 'random';
const SHOPIFY_API_SECRET = process.env.SHOPIFY_API_SECRET || 'random';
const APPURL = process.env.SHOPIFY_APP_URL || 'https://www.random.com';
const SHOPIFY_TESTING = true;
export const config = {
shopifyApiKey: SHOPIFY_API_KEY,
apiKey: SHOPIFY_API_KEY,
sessionStorage: new MemorySessionStorage(),
appUrl: APPURL,
apiSecretKey: SHOPIFY_API_SECRET,
};
if (SHOPIFY_TESTING) {
Object.assign(config, testConfig());
}
export const shopifyAppTest = shopifyApp(config);
test('renders loader data', async () => {
function MyComponent() {
const data = useLoaderData() as { message: string };
return <p>Message: {data.message}</p>;
}
const RemixStub = createRemixStub([
{
path: '/',
Component: MyComponent,
loader() {
return json({ message: 'hello' });
},
},
]);
render(<RemixStub />);
await waitFor(() => screen.findByText('Message: hello'));
});
/** @param {{ children: import("react").ReactElement }} args */
function ShopifyAppProvider({ children }) {
return <PolarisTestProvider i18n={translations}>{children}</PolarisTestProvider>;
}
/**
*
* @param {import("react").ReactElement} ui
* @param {import("@testing-library/react").RenderOptions} [options]
* @returns
*/
const shopifyRender = (ui, options) => render(ui, { wrapper: ShopifyAppProvider, ...options });
// re-export everything
export * from '@testing-library/react';
export { shopifyRender as render };