title | layout |
---|---|
Writing an integration tests with Playwright |
default |
This guide explains how to create and run integration tests using Playwright in your Alokai multistore project. Integration tests verify that different parts of your store work together correctly by:
- Running against composed stores (respecting inheritance)
- Using mocked API responses to test user flows without external services
- Verifying UI components and page behavior
- Running in the Playwright test runner
What You'll Learn
::list{type="success"}
- Understanding integration tests in multistore setup
- Creating and configuring test mocks
- Writing your first Playwright test
- Running tests locally ::
Before writing integration tests, make sure you understand:
- File-based inheritance - Tests follow the same inheritance rules as your stores
- Familiarity with managing the stores
- Basic knowledge of Playwright
Let's walk through adding integration tests to a fashion-brand
store that uses an LLM-based recommendations system to suggest products based on user behavior and preferences.
Here's our project structure:
apps/
├── storefront-unified-nextjs/ # Base shared code
└── stores/
├── fashion-brand/
│ ├── playwright/
│ ├── storefront-middleware/ # Middleware customizations
│ │ └── integrations/
│ │ └── <ecommerce>/
│ │ └── extensions/
│ │ └── recommendations.ts # LLM recommendations logic
│ └── storefront-unified-nextjs/ # Frontend customizations
│ └── app/
│ └── [locale]/
│ └── (default)/
│ └── recommendations/
│ └── page.tsx # Recommendations page
└── sports-brand/ # Another store
In this example:
- The
fashion-brand
store has a custom recommendations extension in the middleware - The extension adds a new
getRecommendations
method to the eCommerce API - A new
/recommendations
page displays personalized product recommendations - We would like to test that the recommendations are displayed correctly
::tip Adding features for all stores
If you want to add a new feature for all stores, you can add them to the base apps within the apps/storefront-unified-nextjs
, apps/storefront-unified-nuxt
and apps/storefront-middleware
folders. Similarly, tests that should run for all stores can be added to the apps/playwright
folder.
::
::steps
#step-1
Mocks simulate API responses during testing, making tests reliable and fast. Let's create the necessary mocks for our recommendations feature.
::info Mocks are set up with the h3 package. This is a lightweight HTTP server library that is used to create mock APIs for your tests. ::
First, create a mock handler for your API endpoint:
// apps/stores/fashion-brand/playwright/mocks/recommendations/endpoints/getRecommendations.ts
import type { MockFactoryContext } from '@core';
import { defineEventHandler } from 'h3';
export default function ({ router }: MockFactoryContext) {
return router.post(
`/getRecommendations`,
defineEventHandler(async (event) => {
return Promise.resolve([
// Mock data for recommendations
])
}),
);
}
Next, create a server factory to organize your mock endpoints:
// apps/stores/fashion-brand/playwright/mocks/recommendations/server.ts
import type { MockFactoryContext } from '@core';
import { getRecommendations } from './endpoints';
export const serverFactory = [(ctx: MockFactoryContext) => getRecommendations(ctx)];
Finally, register your mock server by overriding the mainRouterFactory
:
- Copy the
apps/playwright/mocks/init.ts
file to your store - Then, add your new mock server to the
mainRouterFactory
// apps/stores/fashion-brand/playwright/mocks/init.ts
import { pipe } from '@core';
import { createRouter, defineEventHandler, sendNoContent, useBase } from 'h3';
import { serverFactory as cmsEndpoints } from './cms/server';
import { serverFactory as unifiedEndpoints } from './unified/server';
import { serverFactory as recommendationsEndpoints } from './recommendations/server';
export const mainRouterFactory = () => {
const mainRouter = createRouter();
// ... existing router setup ...
// Add your new mock router
const recommendationsRouter = pipe(recommendationsEndpoints, createRouter());
mainRouter.use(
'/commerce/recommendations/**',
useBase('/commerce/recommendations', recommendationsRouter.handler)
);
return mainRouter;
};
#step-2
// apps/stores/fashion-brand/playwright/setup/pageObjects/recommendations.page.ts
import { BasePage } from '@core';
export class RecommendationsPage extends BasePage {
// Define page elements
public readonly recommendationsGrid = this.page.getByTestId('recommendations-grid');
override async prepare() {
// Add custom preparation logic if needed. For example, you
// can set up the mock database here.
return this;
}
}
::tip Learn more about Page Objects Models Page Object Models are a common pattern in test automation that can significantly improve test maintenance and readability. To learn more about implementing effective Page Objects, check out the Playwright Page Object Models documentation. ::
Playwright Test uses fixtures to establish isolated test environments. Fixtures provide tests with all necessary dependencies and are automatically cleaned up between test runs. Let's register our page object as a fixture. For this, we need to create a new test setup file that will extend the base test setup and add our page object as a fixture.
// apps/stores/fashion-brand/playwright/setup/fashion-brand-test.ts
import { RecommendationsPage } from '@setup/pageObjects/recommendations.page';
import { test as baseTest } from '@setup/test';
// Define the types for our fixtures
interface FashionBrandTestFixtures {
recommendationsPage: RecommendationsPage;
}
// Extend the base test with our fixtures
export const test = baseTest.extend<FashionBrandTestFixtures>({
// Each fixture is defined as an async function
recommendationsPage: async ({ dataFactory, db, framework, frontendUrl, page, utils }, use) => {
// Setup: Create and prepare the page object
const recommendationsPage = new RecommendationsPage({ dataFactory, db, framework, frontendUrl, page, utils });
await use(await recommendationsPage.prepare());
},
});
This setup:
- Defines TypeScript types for all our fixtures
- Creates page objects that are isolated between tests
- Handles automatic setup and cleanup
- Makes page objects available in test functions
::info Test Setup Inheritance
fashion-brand-test.ts
extends the base test setup with store-specific fixtures. This allows you to reuse base fixtures while adding store-specific test configurations. Instead of overriding apps/playwright/setup/test.ts
, you should extend it with your store-specific test setup.
::
You can now use the recommendationsPage
fixture in your tests:
import { test } from '@setup/fashion-brand-test';
test('should show recommendations', async ({ recommendationsPage }) => {
await recommendationsPage.navigate();
// ... rest of the test
});
::tip Learn more about Fixtures Fixtures are a powerful concept in Playwright Test that enable isolated test environments, shared setup code, automatic cleanup, and composition of complex test dependencies. To learn more about how fixtures can improve your testing workflow, check out the Playwright Test Fixtures documentation. ::
#step-3
Now you can write your test using the page object and mocks you've created:
// apps/stores/fashion-brand/playwright/tests/recommendations.test.ts
import { expect } from '@playwright/test';
import { test } from '@setup/fashion-brand-test';
test.describe('Recommendations page', () => {
test('should show recommendations', async ({ recommendationsPage }) => {
// Navigate to the page
await recommendationsPage.navigate();
// Verify recommendations are visible
await expect(recommendationsPage.recommendationsGrid).toBeVisible();
// Add more assertions as needed
});
});
#step-4
You can run tests in two modes:
# Run all tests for a specific store
yarn alokai store test --store-id=fashion-brand
# Open Playwright UI for interactive debugging
yarn alokai store test --store-id=fashion-brand --ui
::tip UI mode is great for debugging as it shows test execution step by step and lets you inspect the page state. ::
::
This guide covered the basics of writing integration tests. For more advanced topics, see:
::card{title="Next: Deployment - How it works?" icon="tabler:number-3-small" }
#description Learn how Alokai handles store deployment and what happens behind the scenes.
#cta :::docs-arrow-link{to="/guides/multistore/tooling-and-concepts/deployment"} Next ::: ::