diff --git a/.github/workflows/js-ci.yml b/.github/workflows/js-ci.yml index eeb045601c..ea5c4c38dd 100644 --- a/.github/workflows/js-ci.yml +++ b/.github/workflows/js-ci.yml @@ -74,9 +74,9 @@ jobs: - uses: ./.github/actions/pnpm-setup - - run: pnpm --filter ${{ env.WORKSPACE }} lint + - run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} lint - - run: pnpm --filter ${{ env.WORKSPACE }} build + - run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} build keycloak-js: name: Keycloak JS @@ -90,23 +90,7 @@ jobs: - uses: ./.github/actions/pnpm-setup - - run: pnpm --filter ${{ env.WORKSPACE }} build - - keycloak-masthead: - name: Keycloak Masthead - needs: conditional - if: needs.conditional.outputs.js-ci == 'true' - runs-on: ubuntu-latest - env: - WORKSPACE: keycloak-masthead - steps: - - uses: actions/checkout@v4 - - - uses: ./.github/actions/pnpm-setup - - - run: pnpm --filter ${{ env.WORKSPACE }} lint - - - run: pnpm --filter ${{ env.WORKSPACE }} build + - run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} build ui-shared: name: UI Shared @@ -114,15 +98,15 @@ jobs: if: needs.conditional.outputs.js-ci == 'true' runs-on: ubuntu-latest env: - WORKSPACE: ui-shared + WORKSPACE: "@keycloak/keycloak-ui-shared" steps: - uses: actions/checkout@v4 - uses: ./.github/actions/pnpm-setup - - run: pnpm --filter ${{ env.WORKSPACE }} lint + - run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} lint - - run: pnpm --filter ${{ env.WORKSPACE }} build + - run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} build account-ui: name: Account UI @@ -130,15 +114,15 @@ jobs: if: needs.conditional.outputs.js-ci == 'true' runs-on: ubuntu-latest env: - WORKSPACE: account-ui + WORKSPACE: "@keycloak/keycloak-account-ui" steps: - uses: actions/checkout@v4 - uses: ./.github/actions/pnpm-setup - - run: pnpm --filter ${{ env.WORKSPACE }} lint + - run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} lint - - run: pnpm --filter ${{ env.WORKSPACE }} build + - run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} build admin-ui: name: Admin UI @@ -152,13 +136,13 @@ jobs: - uses: ./.github/actions/pnpm-setup - - run: pnpm --filter ${{ env.WORKSPACE }} lint + - run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} lint - - run: pnpm --filter ${{ env.WORKSPACE }} test + - run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} test - - run: pnpm --filter ${{ env.WORKSPACE }} build + - run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} build - - run: pnpm --filter ${{ env.WORKSPACE }} cy:check-types + - run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} cy:check-types account-ui-e2e: name: Account UI E2E @@ -168,7 +152,7 @@ jobs: if: needs.conditional.outputs.js-ci == 'true' runs-on: ubuntu-latest env: - WORKSPACE: account-ui + WORKSPACE: "@keycloak/keycloak-account-ui" steps: - uses: actions/checkout@v4 @@ -194,18 +178,19 @@ jobs: KEYCLOAK_ADMIN_PASSWORD: admin - name: Install Playwright browsers - run: pnpm --filter ${{ env.WORKSPACE }} exec playwright install --with-deps + run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} exec playwright install --with-deps - name: Run Playwright tests - run: pnpm --filter ${{ env.WORKSPACE }} test + run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} test env: KEYCLOAK_SERVER: http://localhost:8080 - - uses: actions/upload-artifact@v4 + - name: Upload Playwright report + uses: actions/upload-artifact@v4 if: always() with: name: account-ui-playwright-report - path: js/apps/${{ env.WORKSPACE }}/playwright-report + path: js/apps/account-ui/playwright-report retention-days: 30 - name: Upload server logs @@ -267,7 +252,7 @@ jobs: - uses: ./.github/actions/pnpm-setup - name: Compile Admin Client - run: pnpm --filter @keycloak/keycloak-admin-client build + run: pnpm --fail-if-no-match --filter @keycloak/keycloak-admin-client build - name: Download Keycloak server uses: actions/download-artifact@v4 @@ -289,7 +274,7 @@ jobs: KEYCLOAK_ADMIN_PASSWORD: admin - name: Start LDAP server - run: pnpm --filter ${{ env.WORKSPACE }} cy:ldap-server & + run: pnpm --fail-if-no-match --filter ${{ env.WORKSPACE }} cy:ldap-server & - name: Run Cypress uses: cypress-io/github-action@v6 @@ -329,7 +314,6 @@ jobs: - build-keycloak - admin-client - keycloak-js - - keycloak-masthead - ui-shared - account-ui - account-ui-e2e diff --git a/js/apps/account-ui/playwright.config.ts b/js/apps/account-ui/playwright.config.ts index 9e6701310f..706eeb49dd 100644 --- a/js/apps/account-ui/playwright.config.ts +++ b/js/apps/account-ui/playwright.config.ts @@ -11,6 +11,10 @@ export default defineConfig({ retries: process.env.CI ? 2 : 0, workers: 1, reporter: process.env.CI ? [["github"], ["html"]] : "list", + expect: { + timeout: 20 * 1000, + }, + use: { baseURL: `http://localhost:8080${getRootPath()}`, trace: "on-first-retry", @@ -31,6 +35,7 @@ export default defineConfig({ name: "chromium", use: { ...devices["Desktop Chrome"], + viewport: { width: 1920, height: 1200 }, }, dependencies: ["import realms"], }, diff --git a/js/apps/account-ui/src/resources/ShareTheResource.tsx b/js/apps/account-ui/src/resources/ShareTheResource.tsx index 509c8402fe..67d14b9aff 100644 --- a/js/apps/account-ui/src/resources/ShareTheResource.tsx +++ b/js/apps/account-ui/src/resources/ShareTheResource.tsx @@ -192,7 +192,11 @@ export const ShareTheResource = ({ )} - + { "account-security/linked-accounts", ); - expect(linkedAccountsNavItem).toBeVisible(); + await expect(linkedAccountsNavItem).toBeVisible(); await linkedAccountsNavItem.click(); await expect(page.getByTestId("page-heading")).toHaveText("Linked accounts"); }); diff --git a/js/apps/account-ui/test/applications.spec.ts b/js/apps/account-ui/test/applications.spec.ts index cca7591bfa..dee8bb9bfd 100644 --- a/js/apps/account-ui/test/applications.spec.ts +++ b/js/apps/account-ui/test/applications.spec.ts @@ -70,6 +70,7 @@ test.describe("Applications test", () => { await page.goto("/"); await expect(page).toHaveURL(getAdminUrl()); await page.waitForURL(getAdminUrl()); + await expect(page.getByTestId("realmSelector")).toBeVisible(); await page.goto(getRootPath()); await page.waitForURL(getAccountUrl()); diff --git a/js/apps/account-ui/test/my-resources.spec.ts b/js/apps/account-ui/test/my-resources.spec.ts index 5df565ca6d..89ea19f9d2 100644 --- a/js/apps/account-ui/test/my-resources.spec.ts +++ b/js/apps/account-ui/test/my-resources.spec.ts @@ -36,14 +36,20 @@ test.describe("My resources page", () => { "Share with alice", ); - await page.getByRole("button", { name: "Options menu" }).click(); + await page + .getByTestId("permissions") + .getByRole("button", { expanded: false }) + .click(); await page.getByRole("option", { name: "album:view" }).click(); - await page.getByRole("button", { name: "Options menu" }).click(); + await page + .getByTestId("permissions") + .getByRole("button", { expanded: true }) + .click(); await page.getByTestId("done").click(); await page.getByTestId("expand-one").click(); - expect(page.getByTestId("shared-with-alice")).toBeDefined(); + await expect(page.getByTestId("shared-with-alice")).toBeVisible(); }); test("One is shared with alice", async ({ page }) => { diff --git a/js/apps/account-ui/test/oid4vci/oid4vci.spec.ts b/js/apps/account-ui/test/oid4vci/oid4vci.spec.ts deleted file mode 100644 index bc18043afd..0000000000 --- a/js/apps/account-ui/test/oid4vci/oid4vci.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { expect, test } from "@playwright/test"; -import { login } from "../login"; - -test.describe("Verifiable Credentials page", () => { - test("Get offer for test-credential.", async ({ page }) => { - await login(page, "test-user", "test"); - await expect(page.getByTestId("qr-code")).toBeHidden(); - await page.getByTestId("oid4vci").click(); - await page.getByTestId("credential-select").click(); - await expect( - page.getByTestId("select-verifiable-credential"), - ).toBeVisible(); - await expect(page.getByTestId("select-natural-person")).toBeVisible(); - await page.getByTestId("select-natural-person").click(); - await expect(page.getByTestId("qr-code")).toBeVisible(); - }); -}); diff --git a/js/apps/account-ui/test/personal-info/personal-info.spec.ts b/js/apps/account-ui/test/personal-info/personal-info.spec.ts index 9c84f2380d..04efcb0b71 100644 --- a/js/apps/account-ui/test/personal-info/personal-info.spec.ts +++ b/js/apps/account-ui/test/personal-info/personal-info.spec.ts @@ -30,7 +30,7 @@ test.describe("Personal info page", () => { }); }); -test.describe("Personal info with userprofile enabled", async () => { +test.describe("Personal info with userprofile enabled", () => { let user: string; test.beforeAll(async () => { await importUserProfile(userProfileConfig as UserProfileConfig, realm); @@ -54,9 +54,9 @@ test.describe("Personal info with userprofile enabled", async () => { await expect(page.locator("#select")).toBeVisible(); await expect(page.getByTestId("help-label-select")).toBeVisible(); - expect(page.getByText("Alternative email")).toHaveCount(1); - expect(page.getByPlaceholder("Deutsch")).toHaveCount(1); - page.getByTestId("help-label-email2").click(); + await expect(page.getByText("Alternative email")).toHaveCount(1); + await expect(page.getByPlaceholder("Deutsch")).toHaveCount(1); + await page.getByTestId("help-label-email2").click(); await expect(page.getByText("EspaƱol")).toHaveCount(1); }); @@ -66,11 +66,12 @@ test.describe("Personal info with userprofile enabled", async () => { await page.getByText("Alternate Language").click(); await page.waitForSelector("text=Italiano"); + await page.getByText("Alternate Language").click(); await page.locator("*:focus").press("Control+A"); await page.locator("*:focus").pressSequentially("S"); await expect(page.getByText("Italiano")).toHaveCount(0); - expect(page.getByText("Suomi")).toBeVisible(); - expect(page.getByText('Create "S"')).not.toBeVisible(); + await expect(page.getByText("Slovak")).toBeVisible(); + await expect(page.getByText('Create "S"')).toBeHidden(); }); test("render long list of locales as typeahead", async ({ page }) => { @@ -79,11 +80,12 @@ test.describe("Personal info with userprofile enabled", async () => { await page.locator("#locale").click(); await page.waitForSelector("text=Italiano"); + await page.locator("#locale").click(); await page.locator("*:focus").press("Control+A"); await page.locator("*:focus").pressSequentially("S"); await expect(page.getByText("Italiano")).toHaveCount(0); - expect(page.getByText("Suomi")).toBeVisible(); - expect(page.getByText('Create "S"')).not.toBeVisible(); + await expect(page.getByText("Slovak")).toBeVisible(); + await expect(page.getByText('Create "S"')).toBeHidden(); }); test("save user profile", async ({ page }) => { @@ -112,7 +114,7 @@ test.describe("Personal info with userprofile enabled", async () => { }); // skip currently the locale is not part of the response -test.describe.skip("Realm localization", async () => { +test.describe.skip("Realm localization", () => { test.beforeAll(() => enableLocalization()); test("change locale", async ({ page }) => { diff --git a/js/apps/account-ui/test/realm.setup.ts b/js/apps/account-ui/test/realm.setup.ts index 1d7a709775..f88097230b 100644 --- a/js/apps/account-ui/test/realm.setup.ts +++ b/js/apps/account-ui/test/realm.setup.ts @@ -3,9 +3,12 @@ import { test as setup } from "@playwright/test"; import { importRealm } from "./admin-client"; import groupsRealm from "./realms/groups-realm.json" assert { type: "json" }; -import resourcesRealm from "./realms/resources-realm.json" assert { type: "json" }; -import userProfileRealm from "./realms/user-profile-realm.json" assert { type: "json" }; -import verifiableCredentialsRealm from "./realms/verifiable-credentials-realm.json" assert { type: "json" }; +import resourcesRealm from "./realms/resources-realm.json" assert { type: + "json" }; +import userProfileRealm from "./realms/user-profile-realm.json" assert { type: + "json" }; +import verifiableCredentialsRealm from "./realms/verifiable-credentials-realm.json" assert { type: + "json" }; setup("import realm", async () => { await importRealm(groupsRealm as RealmRepresentation); diff --git a/js/apps/keycloak-server/scripts/start-server.js b/js/apps/keycloak-server/scripts/start-server.js index cbcf2a06b1..513358ce48 100755 --- a/js/apps/keycloak-server/scripts/start-server.js +++ b/js/apps/keycloak-server/scripts/start-server.js @@ -40,7 +40,7 @@ async function startServer() { [ "start-dev", "--http-port=8180", - `--features="login2,account3,admin-fine-grained-authz,transient-users"`, + `--features="login2,account3,admin-fine-grained-authz,transient-users,oid4vc-vci"`, ...keycloakArgs, ], {