initial commit after project creation

This commit is contained in:
Gerhard Scheikl
2026-04-01 09:38:50 +02:00
commit b02af637d4
292 changed files with 61408 additions and 0 deletions

View File

@@ -0,0 +1,218 @@
import type { Page } from "@playwright/test";
import { expect } from "@playwright/test";
export class SearchHelper {
constructor(private page: Page) {}
/**
* Navigate to the docs page and wait for it to load
*/
async navigateToDocs() {
await this.page.goto(`${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/docs`);
await this.page.waitForLoadState("networkidle");
// Wait for the search input to be available (client component hydration)
const searchInput = this.getSearchInput();
await searchInput.waitFor({ state: "attached", timeout: 10000 });
}
/**
* Find and return the search input field
*/
getSearchInput() {
return this.page.locator('input[placeholder="Search..."]');
}
/**
* Perform a search with the given term
*/
async performSearch(searchTerm: string) {
const searchInput = this.getSearchInput();
// Wait for the search input to be visible and attached to the DOM
// This is important because the Search component is a client component that needs to hydrate
await searchInput.waitFor({ state: "visible", timeout: 10000 });
await searchInput.fill(searchTerm);
await searchInput.press("Enter");
// Wait for search to complete
await this.page.waitForTimeout(1000);
}
/**
* Wait for search results to appear
*/
async waitForSearchResults() {
await this.page.waitForTimeout(1000);
}
/**
* Get search results container
*/
getSearchResultsContainer() {
// Use the data-testid attribute for reliable selection
return this.page.locator('[data-testid="search-results-container"]');
}
/**
* Get "No Llamas Found" message
*/
getNoResultsMessage() {
// Use the data-testid attribute for reliable selection
return this.page.locator('[data-testid="no-results-message"]');
}
/**
* Get loading message
*/
getLoadingMessage() {
// Look for the loading message within the search results container
return this.page.locator(
'[data-testid="search-results-container"] h4:has-text("Mustering all the Llamas")'
);
}
/**
* Get all search result links
*/
getSearchResultLinks() {
return this.page.locator('a[href*="/docs"]');
}
/**
* Verify search results are visible
*/
async expectSearchResultsVisible() {
const resultsContainer = this.getSearchResultsContainer();
const noResultsMessage = this.getNoResultsMessage();
await expect(resultsContainer.or(noResultsMessage)).toBeVisible();
}
/**
* Verify search results are not visible
*/
async expectSearchResultsNotVisible() {
const resultsContainer = this.getSearchResultsContainer();
await expect(resultsContainer).not.toBeVisible();
}
/**
* Verify search input is visible
*/
async expectSearchInputVisible() {
const searchInput = this.getSearchInput();
await expect(searchInput).toBeVisible();
}
/**
* Verify search input has the expected value
*/
async expectSearchInputValue(expectedValue: string) {
const searchInput = this.getSearchInput();
await expect(searchInput).toHaveValue(expectedValue);
}
/**
* Clear search by clicking the copy button
*/
async clearSearch() {
await this.page.click('button:has-text("Copy")');
}
/**
* Verify Pagefind files are accessible
*/
async verifyPagefindFilesAccessible() {
const isDev = this.page.url().includes("localhost");
// Check Pagefind JavaScript file
const pagefindJsResponse = await this.page.request.get(
isDev
? "http://localhost:3000/pagefind/pagefind.js"
: `${process.env.BASE_URL}${
process.env.NEXT_PUBLIC_BASE_PATH ?? ""
}/_next/static/pagefind/pagefind.js`
);
expect(pagefindJsResponse.status()).toBe(200);
// Check Pagefind index file
const pagefindIndexResponse = await this.page.request.get(
isDev
? "http://localhost:3000/pagefind/pagefind-ui.js"
: `${process.env.BASE_URL}${
process.env.NEXT_PUBLIC_BASE_PATH ?? ""
}/_next/static/pagefind/pagefind-ui.js`
);
expect(pagefindIndexResponse.status()).toBe(200);
}
/**
* Measure search performance
*/
async measureSearchPerformance(searchTerm: string): Promise<number> {
const startTime = Date.now();
await this.performSearch(searchTerm);
const endTime = Date.now();
return endTime - startTime;
}
/**
* Test search with multiple terms
*/
async testMultipleSearches(searchTerms: string[]) {
for (const term of searchTerms) {
await this.performSearch(term);
await this.expectSearchResultsVisible();
await this.clearSearch();
}
}
/**
* Test search on mobile viewport
*/
async testMobileSearch() {
await this.page.setViewportSize({ width: 375, height: 667 });
await this.expectSearchInputVisible();
await this.performSearch("TinaDocs");
await this.expectSearchResultsVisible();
}
}
/**
* Test data for search tests
*/
export const SEARCH_TEST_DATA = {
knownTerms: [
"TinaDocs",
"documentation",
"search",
"API",
"TinaCMS",
"deployment",
"theming",
"components",
],
nonExistentTerms: [
"xyz123nonexistent",
"completelyrandomterm",
"shouldnotexist",
],
specialCharacters: [
"@#$%",
"test@example.com",
"user-name",
"file/path",
"test&query",
"test+query",
],
};
/**
* Create a search helper instance
*/
export function createSearchHelper(page: Page): SearchHelper {
return new SearchHelper(page);
}