test-ui-qsf
Original:🇺🇸 English
Translated
QSF project conventions for the Playwright E2E suite. Use when working in the QSF playwright-tests directory (authFixtures, ___ping-* handles, data-test-label test IDs, .auth storage states) — not for generic Playwright advice.
9installs
Sourcelgtm-hq/ai-skills
Added on
NPX Install
npx skill4agent add lgtm-hq/ai-skills test-ui-qsfTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →QSF Playwright E2E Conventions
Project-specific conventions for the QSF Playwright suite. For generic Playwright
best practices (locators, auto-waiting, POM, anti-patterns), follow the
skill — this skill only adds what is QSF-specific.
test-uiCommands
- — run all tests headless
source ./bin/load_env.sh && bunx playwright test - — skip auth-setup
source ./bin/load_env.sh && bunx playwright test --project=regression-tests - /
bun run test— interactive UI modebunx playwright test --ui - — verbose terminal output
bunx playwright test --reporter=list - — run tagged tests
bunx playwright test --grep '@smoke' - — inspect failure traces
bunx playwright show-trace <trace.zip>
Test IDs
getByTestIddata-test-labeltestIdAttributeplaywright.config.tstypescript
page.getByTestId("no-handles-available-indicator");
page.getByTestId("open-filter-section-button");Fixtures (Auth Pattern)
The project extends Playwright's with pre-authenticated user fixtures via
storage state files in .
testplaywright-tests/.auth/typescript
// fixtures/authFixtures.ts
const authFiles = {
guest: "guest_storage_state.json",
testView: "test_view_storage_state.json",
testNoRights: "test_no_rights_storage_state.json",
// ... more users
};
const baseFixtures = Object.fromEntries(
Object.entries(authFiles).map(([key, fileName]) => [
key,
async ({ browser }: { browser: Browser }, use: (page: Page) => Promise<void>) => {
const storageState = path.join(__dirname, "../.auth", fileName);
const context = await browser.newContext({ storageState });
const page = await context.newPage();
try {
await use(page);
} finally {
await context.close();
}
},
]),
);
export const test = baseTest.extend(baseFixtures);
export const expect = test.expect;Fixture Usage
typescript
// Tests import from authFixtures, NOT from @playwright/test
import { test, expect } from "../../fixtures/authFixtures";
test("user sees their handles", async ({ guest }) => {
await guest.goto("/"); // `guest` is a pre-authenticated Page — no login needed
});
test("restricted user cannot access admin", async ({ testNoRights }) => {
await testNoRights.goto("/admin");
});File Organization
text
playwright-tests/
├── .auth/ # Storage state JSON files (gitignored in CI)
├── enums/ # Constants: handle names, error messages, labels
├── fixtures/ # authFixtures.ts — extended test/expect
├── pageObjects/ # POM classes (BasePage, LoginPage, ...)
│ └── components/ # Reusable: Navigation, FileUpload, FormElements
├── setup/ # auth.setup.ts, cleanup.ts
└── tests/ # Spec files grouped by feature (handle/, ping-files/, ...)Page Objects
Pages extend (), which composes
, , and . Specialized pages (e.g.
) add components like and . Keep
under 300 lines — split by element type when it grows.
BasePagepageObjects/BasePage.tsNavigationFilterSectionIdleModalAppsDetailPageFileUploadIwaFormElements.tsEnums & Constants
Store UI labels, error messages, and handle identifiers in
. Never hard-code them in tests.
playwright-tests/enums/typescript
// enums/handles.ts
export const HandleNames = { PING_FILES: '___ping-files-en', ... };
export const HandleIds = { PING_FILES: 'qsf-handle-ping-files', ... };
// enums/errors.ts
export const Errors = { FILE_TOO_LARGE: 'produced an error while processing', ... };
// enums/elementLabels.ts
export const Buttons = { SUBMIT: 'Submit', UPLOAD: 'Upload files', ... };Typical Test Pattern
typescript
import { test, expect } from "../../fixtures/authFixtures";
import { AppsDetailPage } from "../../pageObjects/AppsDetailPage";
import { MyAppsPage } from "../../pageObjects/MyAppsPage";
import { HandleNames } from "../../enums/handles";
test.describe("Feature Area", () => {
test.beforeEach(async ({ guest }) => {
const myAppsPage = new MyAppsPage(guest);
await guest.goto("/");
await myAppsPage.navigation.openHandle(HandleNames.PING_FILES);
});
test("describes expected behavior", async ({ guest }) => {
const appsDetailPage = new AppsDetailPage(guest);
await appsDetailPage.doSomething();
await appsDetailPage.assertSomething(expected);
});
});When navigation + upload + dashboard assertion is duplicated across
describe blocks, extract a shared helper (e.g.
).
beforeEachsetupDashboardForTestcase(guest, handleName, testcase)Environment-Aware Tests
typescript
import { isOpenAmEnv } from "../../src/utils/environment";
test.skip(isOpenAmEnv, "This test is only for Keycloak environments");Env is loaded via ; the variable controls
the target URL (default: ).
source ./bin/load_env.shENVIRONMENTtestConfiguration Highlights (playwright.config.ts
)
playwright.config.tstypescript
export default defineConfig({
testDir: "./playwright-tests",
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.TEST_HANDLE ? 0 : 1,
workers: process.env.CI ? 1 : 5,
globalTimeout: 30 * 60 * 1000,
timeout: 60000,
expect: { timeout: 10000 },
use: {
ignoreHTTPSErrors: true,
testIdAttribute: "data-test-label",
baseURL: envUrl,
trace: "on-first-retry",
},
projects: [
{ name: "auth-setup", testMatch: "**/setup/auth.setup.ts" },
{
name: "regression-tests",
testMatch: ["**/tests/*.spec.ts", "**/tests/**/*.spec.ts"],
dependencies: process.env.CI ? ["auth-setup"] : [],
use: { trace: "retain-on-failure", headless: true },
},
],
});QSF Checklist
- /
testimported fromexpect, notauthFixtures@playwright/test - Named user fixtures (,
guest,testView) used for authtestNoRights - Browser context closed after each fixture via
context.close() - Handle names, errors, and labels imported from
enums/ - values match
getByTestIdattributesdata-test-label - used for environment-specific tests
test.skip(isOpenAmEnv, ...) - Env loaded via before running
source ./bin/load_env.sh - Generic rules from the skill also satisfied
test-ui