Skip to main content

generate-storefront-e2e-test

Generate E2E tests for Salesforce Commerce Cloud storefront using CodeceptJS with AI-powered features. Specializes in commerce-specific test patterns, page objects, and interactive development workflows.

معلومات المصدر

المستودع
SalesforceCommerceCloud/storefront-next-template
آخر نشاط في المصدر
٢٩ أبريل ٢٠٢٦ في ٢٢:٥٤
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٥٥
التفرعات
١٦

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
generate-storefront-e2e-test
description
Generate E2E tests for Salesforce Commerce Cloud storefront using CodeceptJS with AI-powered features. Specializes in commerce-specific test patterns, page objects, and interactive development workflows.
# Generate Storefront E2E Test Systematically create end-to-end tests for Salesforce Commerce Cloud storefront using CodeceptJS Framework with AI-powered features. This skill focuses on commerce-specific test patterns and leverages DOM-driven AI capabilities. ## Prerequisites Before starting, ensure you understand the storefront context: ```bash view CLAUDE.md # in current package directory ``` ## Workflow ### 1. Define Test Requirements **Understand the commerce scenario**: - Identify storefront functionality to test (search, cart, checkout, etc.) - List specific test cases for commerce flows - **IMPORTANT**: Always ask clarifying questions about the test scope - **IMPORTANT**: Focus on actual storefront, NOT demo/workbench environments **Example questions**: - "Should this test cover both desktop and mobile views?" - "Do you want to test guest checkout or authenticated user flow?" - "Should we validate SFCC cookies and session management?" - "Which product categories or search terms should we use?" ### 1.5. Audit Existing Unit Tests and Storybook Stories **Review existing tests before writing E2E scenarios** to understand what's already covered and ensure E2E tests focus on integration value. **Key principle**: E2E tests should validate flows that require real authentication, routing, cross-component interactions, or live API data — not duplicate component-isolated unit tests or Storybook `play` functions. ### 2. Verify Storefront Context **Check storefront implementation** to understand: **Environment Configuration**: - Verify `.env` exists (gitignored, maintained by developers/CI) - If `.env` doesn't exist, copy from `.env.sample` - Confirm BASE_URL points to actual storefront (not demo) - Check SITE_ID configuration for cookie validation **Existing Page Objects**: ```bash view src/pages/**/*.page.ts ``` - Look for reusable page objects (storefrontPage, cartPage, etc.) - Identify if new page objects are needed **Existing Tests**: ```bash view src/specs/**/*.spec.ts ``` - Review existing test patterns and tags - Ensure no duplication of test scenarios ### 3. Get User Approval **Present test plan** to user: - List specific test scenarios you'll create - Explain commerce flows to be covered - Confirm AI features to be demonstrated (pause(), I.askForPageObject()) - Specify tags and organization strategy **Wait for approval** before proceeding to implementation. ### 4. Implement Tests with AI Features **Create tests using Scenario-Mocha style**: ```typescript Feature('Storefront Commerce Tests').tag('@commerce'); const { I, storefrontPage, cartPage } = inject(); import { expect } from 'chai'; Scenario('Search and add product to cart', async () => { storefrontPage.navigate(); storefrontPage.searchForProduct('shoes'); storefrontPage.clickFirstProduct(); // Example of interactive AI development // pause(); // Uncomment to try: "Add product to cart and verify" cartPage.addToCart(); cartPage.validateItemAdded(); }).tag('@search').tag('@cart'); export {}; ``` **Key Requirements**: - Use `Feature()` and `Scenario()` structure - Add meaningful tags for filtering - Include `export {};` at end - Import page objects via `inject()` - Import Chai `expect` for value assertions: `import { expect } from 'chai';` - Use Chai `expect()` assertions for validating retrieved values (counts, text, URLs, etc.) - Use CodeceptJS page object methods for UI interactions - Focus on commerce-specific flows - **CRITICAL**: Never call `I.*` methods directly inside a Scenario — all `I.*` usage (`I.click`, `I.amOnPage`, `I.seeElement`, etc.) must live in page objects or flows. Scenarios only call page object / flow methods and Chai assertions. - **CRITICAL**: Each scenario must be independent — create necessary test data within the scenario, never depend on other scenarios running first or in specific order - **CRITICAL**: Wrap all test-authored paths in `buildSitePath()` before passing to `I.amOnPage()` — import from `../utils/url-utils` (or `../../utils/url-utils` from specs). Do NOT apply `buildSitePath()` to URLs extracted from the page DOM (they already contain the url prefix). ### 5. Generate Page Objects with AI **Use AI-powered page object generation**: ```typescript import { buildSitePath } from '../../utils/url-utils'; Scenario('Generate product page object', async () => { I.amOnPage(buildSitePath('/product/sample-product')); pause(); // In console: I.askForPageObject("productDetail") // AI reads runtime DOM and generates complete page object }).tag('@page-object-generation'); ``` > **Multi-site note:** All `I.amOnPage()` calls with test-authored paths must use `buildSitePath()` to prepend the optional `/{siteAlias}/{locale}` prefix. See `CLAUDE.md` "Multi-site URL Prefixing" for details. **Locator Strategy**: Playwright's recommended priority is user-facing attributes first, `data-testid` last: 1. `getByRole` / `[role]` + accessible name — closest to how users and screen readers perceive the page 2. `getByLabel` / associated `<label>` — preferred for form fields 3. `getByText` / `.withText()` — visible text content 4. `getByPlaceholder`, `getByAltText`, `getByTitle` — other user-facing attributes 5. `data-testid` — last resort; resilient but not user-facing **Rule of thumb:** if `locate('[role="button"]').withText('Add to Cart')` uniquely identifies the element, prefer it. If there are multiple matches, reach for `data-testid`. **No good locator? Add one.** If no semantic attribute or `data-testid` exists on the element, add `data-testid` directly to the source component in the template. Don't settle for a brittle CSS class or positional selector — a two-minute source edit produces a stable, self-documenting locator. ```tsx // ✅ Add data-testid to the source component <button data-testid="add-to-cart-button" onClick={handleAddToCart}> Add to Cart </button> // Then reference it in the page object addToCartButton: locate('[data-testid="add-to-cart-button"]').as('Add to Cart Button'), ``` **⚠️ Avoid UI-Only Classes:** **NEVER** use styling classes (Tailwind utilities like `text-muted-foreground`, `text-destructive`, `bg-*`, `flex`, etc.) as locators — they're non-semantic and match multiple elements. ```typescript // ❌ Bad: UI-only classes locate('p.text-muted-foreground').as('Subtitle') locate('.text-destructive').as('Error') // ✅ Better: Add semantic context or use data attributes locate('[data-slot="card"] p.text-muted-foreground').first().as('Subtitle') locate('[role="dialog"] .text-destructive').as('Form Error') locate('[role="alert"]').as('Error Message') ``` **Page Object Pattern**: ```typescript const { I } = inject(); import { buildSitePath } from '../utils/url-utils'; class ProductDetailPage { locators = { productTitle: locate('[data-testid*="product-title"]').as('Product Title'), addToCartButton: locate('[data-testid*="add-to-cart"]').as('Add to Cart'), priceDisplay: locate('[data-testid*="price"]').as('Price'), sizeSelector: locate('[data-testid*="size"]').as('Size Selector'), }; navigate(productSlug: string): void { I.amOnPage(buildSitePath(`/product/${productSlug}`)); } async selectSize(size: string): Promise<void> { I.click(this.locators.sizeSelector); I.click(`option:has-text("${size}")`); } addToCart(): void { I.click(this.locators.addToCartButton); I.waitForText('Added to cart', 10); } } module.exports = new ProductDetailPage(); ``` **After creating page object**: 1. **Register in `src/pages/index.ts`** → Add entry to `pageObjects` object (or `src/flows/index.ts` for flows) 2. **Update `helpers/self-healing/recipes.ts`** → Add healing recipes for each locator (see step 7.5) See **step 6** below for full registration details and examples. ### 6. Register Page Objects and Flows **IMPORTANT**: All page objects and flows must be registered in their respective index files to be available via `inject()`. #### Registry System Architecture The project uses a centralized registry system to keep `codecept.conf.cjs` clean and maintainable: **Page Objects Registry** (`src/pages/index.ts`): ```typescript export const pageObjects = { storefrontPage: './src/pages/storefront.page.ts', cartPage: './src/pages/cart.page.ts', megaMenuPage: './src/pages/mega-menu.page.ts', productListPage: './src/pages/product-list.page.ts', productDetailPage: './src/pages/product-detail.page.ts', }; ``` **Flows Registry** (`src/flows/index.ts`): ```typescript export const flows = { addToCartFlow: './src/flows/add-to-cart.flow.ts', checkoutFlow: './src/flows/checkout.flow.ts', }; ``` **How it works**: - `codecept.conf.cjs` imports both registries: `const { pageObjects } = require('./src/pages/index.ts');` - Config spreads them into `include`: `include: { ...pageObjects, ...flows }` - All registered objects are available via `inject()` in tests - **No need to modify** `codecept.conf.cjs` when adding new page objects/flows **When to register**: - ✅ **Always** register page objects in `src/pages/index.ts` - ✅ **Always** register flows in `src/flows/index.ts` - ❌ **Never** add them directly to `codecept.conf.cjs` **Benefits**: - Clean separation of concerns - Easy to find and manage all page objects/flows - No mixed imports in config file - Scales well as project grows ### 7. Register TypeScript Definitions **AUTOMATIC**: TypeScript definitions are now auto-generated before each test run. ```bash # Definitions auto-generated when running tests pnpm e2e --grep "@your-test" # Manual generation (optional) pnpm def ``` The `steps.d.ts` file is automatically updated with new page object registrations. ### 7.5. Update Self-Healing Recipes **REQUIRED**: When creating page objects, adding new locators, or modifying existing locator selectors, always update `helpers/self-healing/recipes.ts` to keep healing recipes in sync. **Purpose**: Healing recipes provide fallback selectors for AI self-healing when primary locators break. They help the AI understand context and suggest alternative selectors. **Process**: 1. **Extract locators** from the new/updated page object 2. **Create a `HealingRecipe`** for each important locator: ```typescript export const newLocatorRecipe: HealingRecipe = { name: 'locatorName', // Match the page object locator name description: 'Human-readable description of the element', selectors: [ 'primary-selector', // From page object locators 'semantic-selector', // Semantic HTML fallback '[aria-label*="text" i]', // Accessibility fallback 'location-based-selector', // Context-aware selector ], context: 'Where the element appears and its purpose', fallbackStrategy: 'What to look for if primary selector fails', }; ``` 3. **Add to `healingRecipes` array**: ```typescript export const healingRecipes: HealingRecipe[] = [ // ... existing recipes newLocatorRecipe, ]; ``` **Recipe Pattern**: - **Primary selector**: Exact selector from page object (highest priority) - **Semantic selectors**: HTML element types, ARIA roles, semantic attributes - **Context selectors**: Location-based (e.g., `header input`, `footer a`) - **Accessibility selectors**: `aria-label`, `role`, `placeholder` attributes - **Fallback strategy**: Human-readable guidance for AI when all selectors fail **Example** (from `storefront.page.ts`): ```typescript // Page object locator: searchInput: locate('input[data-testid*="search"]').as('Search Input'), // Corresponding recipe: export const searchInputRecipe: HealingRecipe = { name: 'searchInput', description: 'Search input field in storefront header', selectors: [ 'input[data-testid*="search"]', // Primary from page object 'input[type="search"]', // Semantic HTML 'input[placeholder*="search" i]', // Placeholder text 'input[aria-label*="search" i]', // Accessibility label 'header input[type="text"]', // Location + type '[role="searchbox"]', // ARIA role ], context: 'Located in storefront header, used for product search', fallbackStrategy: 'Look for input field in header navigation area', }; ``` **When to update**: - ✅ Creating a new page object → Add recipes for all locators - ✅ Adding new locators to existing page object → Add recipes for new locators - ✅ **Modifying existing locator selectors → Update corresponding recipe immediately** - ❌ Don't create recipes for temporary/test-specific locators
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub