Convention for data-testid attributes in apps/jaguar for E2E testing.
Consistent data-testid attributes make E2E tests reliable and maintainable. They decouple tests from the UI structure, so tests keep working when markup or styling changes. Following this convention keeps selectors predictable across apps/jaguar.
{component}-{element}-{type}
Element data-testid
Login page login-page
Email input login-form-email-input
Password input login-form-password-input
Name input login-form-name-input
Submit button login-form-submit-button
Google button login-form-google-button
Toggle mode login-form-toggle-mode-button
Go to homepages button login-form-go-to-homepages-button
Element data-testid
Sidebar sidebar
Nav items nav-{slugified-title} (dynamic)
Nav sub-items nav-{parent}-{child} (dynamic)
User Menu nav-user-{action} (menu, trigger, account-item, notifications-item, logout-button)
Team Switcher team-switcher-{action} (menu, trigger, add-team-button, item-{team-name})
Projects nav-projects-{action} (nav-projects, item-{project-name}, more-button-{project-name}, view-action, share-action, delete-action)
Brand Logo hungryhub-logo
Component data-testid Pattern
HomepageList homepage-list-{action} (search-input, create-button, clear-filters-button, refresh-button)
BannerList banner-list-{action} (search-input, create-button, clear-filters-button, refresh-button)
SectionList section-list-{action} (search-input, create-button, clear-filters-button, refresh-button)
HomeIconList homeicon-list-{action} (search-input, create-button, clear-filters-button, refresh-button)
TagSectionList tagsection-list-{action} (search-input, create-button, refresh-button)
CityList city-list-{action} (search-input, sync-button, clear-filters-button, refresh-button, popover-trigger, popover-content)
CountryList country-list-{action} (search-input, sync-button, clear-filters-button, refresh-button)
LanguageList language-list-{action} (search-input, create-button, clear-filters-button, refresh-button)
RestaurantTagList restauranttag-list-{action} (search-input, sync-button, clear-filters-button, refresh-button)
Component data-testid Pattern
HomepageForm homepage-form-{action} (name-input, description-input, city-select, country-select, active-checkbox, submit-button, cancel-button, delete-button)
BannerForm banner-form-{action} (position-input, location-select, city-selector, country-select, active-checkbox, ai-translate-button, submit-button, cancel-button, delete-button)
SectionForm section-form-{action} (slug-input, section-number-select, type-select, template-select, banner-type-select, data-source-type-select, endpoint-api-input, tag-section-button, group-landing-page-button, ai-translate-button, submit-button, cancel-button, delete-button)
HomeIconForm homeicon-form-{action} (icon-type-select, image-upload, link-url-input, position-input, city-selector, restaurant-tag-button, active-checkbox, ai-translate-button, submit-button, cancel-button, delete-button)
TagSectionForm tagsection-form-{action} (name-input, add-item-button, submit-button, cancel-button, delete-button); tagsection-form-item-{index}-{action} (restaurant-tag-button, active-checkbox)
Component data-testid Pattern
HomepageView homepage-view (container) and homepage-view-{action} (edit-button, visit-button, {device}-section-{id}-{action} for edit/toggle/view)
SectionView section-view (container) and section-view-{action} (edit-button, back-button, retry-button, visit-link-button, manage-group-landing-page-button, manage-tag-section-button)
BannerView banner-view (container) and banner-view-{action} (edit-button, back-button, retry-button)
HomeIconView homeicon-view (container) and homeicon-view-{action} (edit-button, back-button, retry-button)
TagSectionView tagsection-view (container) and tagsection-view-{action} (edit-button, back-button, retry-button)
Component data-testid Pattern
GroupLandingPageSearchDialog grouplanding-search-{action} (input, clear-button, cancel-button)
RestaurantSearchDialog restaurant-search-{action} (input, clear-button, close-button)
RestaurantTagSearchDialog restauranttag-search-{action} (input, clear-button, cancel-button)
SectionSearchDialog section-search-{action} (input, clear-button, cancel-button)
TagSectionSearchDialog tagsection-search-{action} (input, clear-button, cancel-button)
BranchSearchDialog branch-search-{action} (input, clear-button, cancel-button, result-{id})
Component data-testid Pattern
LanguageFormDialog language-form-{action} (dialog, language-select, direction-select, active-checkbox, cancel-button, submit-button)
HomepageLinkDialog homepage-link-dialog (content), homepage-link-dialog-{action} (base-url-input, use-default-button, cancel-button, continue-button)
HomepagesListDialog homepages-list-dialog (content), homepages-list-dialog-homepage-link-{id}
Component data-testid Pattern
AITranslationConfigureStep ai-translation-{action} (cancel-button, translate-button, review-button)
AITranslationProgressStep ai-translation-{action} (retry-button, retry-all-button, continue-button)
AITranslationReviewStep ai-translation-{action} (close-button, translate-more-button, apply-all-button, discard-all-button)
AITranslationEntityCard ai-translation-{action} (edit-button, discard-button, apply-button, save-button, edit-cancel-button)
AITranslationSheet ai-translation-sheet
Component data-testid Pattern
Banner translations banner-form-translation-{index}-{field} (name-input, caption-input, description-textarea, description-ext-textarea, link-input, image-desktop-upload, image-mobile-upload)
Section translations section-form-translation-{index}-{field} (title-input, tagline-input, punchline-input, description-textarea, footer-textarea, image-upload)
HomeIcon translations homeicon-form-translation-{index}-title-input
TranslationManager translation-manager-{action} (add-translation-button, translation-{index}-{action} [toggle, language-select, default-checkbox, remove-button])
TranslationPreview translation-preview (item-{index})
Component data-testid Pattern
DeviceSections device-sections-{device} (item-{sectionId}-{action}: drag-handle, active-switch, select-button, actions-button, edit, view, remove; add-button)
Element data-testid
Delete confirmation dialog delete-dialog-confirm-button
Delete cancel dialog delete-dialog-cancel-button
Confirmation dialog confirmation-dialog-{action}
File Upload file-upload-{action} (input, url-input, dropzone, remove-button)
City Selector city-selector-{action} (select-all-checkbox, country-{id}-toggle, country-{id}-select-toggle, city-{id}-checkbox)
Dropdown Action dropdown-action-{action} (trigger-button, view-link, edit-link, duplicate-link, delete-button)
Component data-testid
BannerPreview banner-preview
HomeIconsPreview home-icons-preview
HhSpecialsPreview hh-specials-preview
Apply data-testid only to stable elements that persist across page states. Dynamic or transient elements (e.g., loading spinners, ephemeral popovers) should not be used as test anchors; use wait conditions or target a stable parent instead.
Use only lowercase alphanumeric characters and hyphens (a-z, 0-9, -).
Keep values under ~64 characters to preserve readability.
Dynamic values such as {index}, {id}, and {action} are placeholders that are replaced at runtime. Document the resolved pattern (e.g., section-form-translation-0-name-input) in code comments so selectors stay traceable.
// Playwright
await page.getByTestId("homepage-list-create-button").click();
await page.getByTestId("homepage-form-submit-button").click();
// Cypress
cy.get("[data-testid=\"login-form-submit-button\"]").click();