Loading...
Loading...
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.
npx skill4agent add lgtm-hq/ai-skills test-ui-qsftest-uisource ./bin/load_env.sh && bunx playwright testsource ./bin/load_env.sh && bunx playwright test --project=regression-testsbun run testbunx playwright test --uibunx playwright test --reporter=listbunx playwright test --grep '@smoke'bunx playwright show-trace <trace.zip>getByTestIddata-test-labeltestIdAttributeplaywright.config.tspage.getByTestId("no-handles-available-indicator");
page.getByTestId("open-filter-section-button");testplaywright-tests/.auth/// 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;// 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");
});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/, ...)BasePagepageObjects/BasePage.tsNavigationFilterSectionIdleModalAppsDetailPageFileUploadIwaFormElements.tsplaywright-tests/enums/// 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', ... };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);
});
});beforeEachsetupDashboardForTestcase(guest, handleName, testcase)import { isOpenAmEnv } from "../../src/utils/environment";
test.skip(isOpenAmEnv, "This test is only for Keycloak environments");source ./bin/load_env.shENVIRONMENTtestplaywright.config.tsexport 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 },
},
],
});testexpectauthFixtures@playwright/testguesttestViewtestNoRightscontext.close()enums/getByTestIddata-test-labeltest.skip(isOpenAmEnv, ...)source ./bin/load_env.shtest-ui