Writing Tests
The smoke spec
Generated smoke tests loop over urlsConfig.markets, call setupPage(), assert boilerplate-specific content, and attach a screenshot.
Minimal example:
import { expect, test } from '@playwright/test';
import { buttonText } from '../src/config.js';
import { urlsConfig } from './config.js';
import { experimentContainer, setupPage, takeScreenshot } from './helpers.js';
for (const market of urlsConfig.markets) {
test.describe(`ExperimentButton [${market.code}]`, () => {
test('renders the experiment button with correct text', async ({ page }, testInfo) => {
await setupPage(page, market, testInfo);
const container = page.locator(experimentContainer);
await expect(container.locator('button')).toBeVisible();
await expect(container.locator('button')).toContainText(buttonText);
await takeScreenshot(page, market.code, 'experiment-button', testInfo);
});
});
}The product-card version also verifies title, CTA, price, image attributes, and link output.
Helpers
setupPage(page, market, testInfo)
The helper:
- Registers the product API mock for product-card projects.
- Opens the market URL.
- Creates the primary target when it is a missing simple class selector.
- Injects the loaded
dist/v1/v1.jscode. - Waits for
experimentContainer. - Attaches the test URL to the report.
Generated target creation supports selectors such as .target-selector. Customize injectTargetElement() when the primary selector is an attribute, descendant, or other complex selector.
experimentContainer
The current boilerplates mount as the first child of selectors.primary:
export const experimentContainer = `${primarySelector} > div:first-child`;Use the exported selector instead of assuming the runtime adds a data attribute:
const container = page.locator(experimentContainer);
await expect(container).toBeVisible();takeScreenshot(page, marketCode, name, testInfo)
Captures the experiment container and attaches the image to the Playwright HTML report:
await takeScreenshot(page, market.code, 'after-render', testInfo);mockProductApi(page, overrides?)
Product-card helpers route **/product/card/detail/** and return a stable model. Override fields for focused cases:
await setupPage(page, market, testInfo, {
mockFn: (currentPage) =>
mockProductApi(currentPage, {
promotionPrice: 999.99,
displayName: 'Test device',
}),
});Multi-market parametrisation
The for...of loop creates one independent test per market. SEBN, for example, creates tests for Belgium Dutch, Belgium French, and Netherlands.
URL configuration
export const urlsConfig = {
baseUrl: 'https://samsung.com',
bundlePath: 'dist/v1/v1.js',
markets: [{ code: 'UK', urlPath: 'uk', name: 'United Kingdom' }],
getUrl(marketCode, pagePath) {
const market = this.markets.find((item) => item.code === marketCode);
if (!market) throw new Error(`Unknown market: ${marketCode}`);
const base = this.baseUrl.replace(/\/$/, '');
return `${base}/${market.urlPath}${pagePath}`;
},
};Change the page path in setupPage() when the experiment belongs to a route other than /.
Add assertions
test(`price renders - ${market.name}`, async ({ page }, testInfo) => {
await setupPage(page, market, testInfo);
const container = page.locator(experimentContainer);
const price = container.locator('[class*="price"]');
await expect(price).toBeVisible();
await expect(price).not.toBeEmpty();
});Keep assertions scoped to the injected container so unrelated host-page changes do not make the test noisy.