From 248fcdd1bf14dfae9f0ec4bbb5850de7c45f06bb Mon Sep 17 00:00:00 2001 From: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com> Date: Mon, 2 Dec 2024 08:19:51 -0800 Subject: [PATCH] web: update tests for Chromedriver 131 (#12199) * web: Add InvalidationFlow to Radius Provider dialogues ## What - Bugfix: adds the InvalidationFlow to the Radius Provider dialogues - Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated to the Notification. - Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/` ## Note Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current dialogues at the moment. * web: fix selector warnings in WebdriverIO Despite the [promises made](https://webdriver.io/docs/selectors#deep-selectors) by the WebdriverIO team, we are still getting a lot of warnings and "falling back to pre-BIDI behavior" messages when we attempt to access ShadowDOM contexts without the "pierce" (`>>>`) syntax. So I've put it back wherever it occurred and the system now uses the BIDI controllers correctly. * web: update to Chromedriver 131 breaks a lot of stuff This annoying bit of janitorial work cleans up the failure messages and resolution bugs that arose when updating to the latest version of Chrome. Keeping track of all the weakness and breakage while the in-browser testing teams figure out how to live with the ShadowDOM is just really time-consuming. --- web/package-lock.json | 8 +++--- web/package.json | 2 +- .../ak-table/tests/ak-select-table.test.ts | 25 ++++++++++--------- .../ak-table/tests/ak-simple-table.test.ts | 3 ++- web/src/elements/tests/Divider.test.ts | 2 +- web/tests/pageobjects/admin.page.ts | 2 +- .../pageobjects/application-wizard.page.ts | 10 ++++---- .../pageobjects/applications-list.page.ts | 2 +- .../pageobjects/forms/application.form.ts | 6 ++--- .../pageobjects/forms/forward-proxy.form.ts | 4 +-- web/tests/pageobjects/forms/ldap.form.ts | 2 +- web/tests/pageobjects/forms/oauth.form.ts | 4 +-- web/tests/pageobjects/forms/radius.form.ts | 2 +- web/tests/pageobjects/forms/saml.form.ts | 4 +-- web/tests/pageobjects/forms/scim.form.ts | 4 +-- .../forms/transparent-proxy.form.ts | 6 ++--- web/tests/pageobjects/login.page.ts | 12 ++++----- web/tests/pageobjects/page.ts | 6 ++--- web/tests/pageobjects/user-library.page.ts | 4 +-- web/tests/specs/new-application-by-wizard.ts | 2 +- web/tests/specs/oauth-provider.ts | 8 +++--- 21 files changed, 60 insertions(+), 58 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 15bdc680c7..1ce1fedf70 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -84,7 +84,7 @@ "@wdio/cli": "^9.1.2", "@wdio/spec-reporter": "^9.1.2", "chokidar": "^4.0.1", - "chromedriver": "^130.0.4", + "chromedriver": "^131.0.1", "esbuild": "^0.24.0", "eslint": "^9.11.1", "eslint-plugin-lit": "^1.15.0", @@ -8699,9 +8699,9 @@ } }, "node_modules/chromedriver": { - "version": "130.0.4", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-130.0.4.tgz", - "integrity": "sha512-lpR+PWXszij1k4Ig3t338Zvll9HtCTiwoLM7n4pCCswALHxzmgwaaIFBh3rt9+5wRk9D07oFblrazrBxwaYYAQ==", + "version": "131.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-131.0.1.tgz", + "integrity": "sha512-LHRh+oaNU1WowJjAkWsviN8pTzQYJDbv/FvJyrQ7XhjKdIzVh/s3GV1iU7IjMTsxIQnBsTjx+9jWjzCWIXC7ug==", "dev": true, "hasInstallScript": true, "dependencies": { diff --git a/web/package.json b/web/package.json index 0db49610ce..a420df9f8b 100644 --- a/web/package.json +++ b/web/package.json @@ -72,7 +72,7 @@ "@wdio/cli": "^9.1.2", "@wdio/spec-reporter": "^9.1.2", "chokidar": "^4.0.1", - "chromedriver": "^130.0.4", + "chromedriver": "^131.0.1", "esbuild": "^0.24.0", "eslint": "^9.11.1", "eslint-plugin-lit": "^1.15.0", diff --git a/web/src/elements/ak-table/tests/ak-select-table.test.ts b/web/src/elements/ak-table/tests/ak-select-table.test.ts index ae92b615be..f9b7f4c37d 100644 --- a/web/src/elements/ak-table/tests/ak-select-table.test.ts +++ b/web/src/elements/ak-table/tests/ak-select-table.test.ts @@ -41,20 +41,20 @@ describe("Select Table", () => { }); it("the table should have as many entries as the data source", async () => { - const rows = await table.$("tbody").$$("tr"); + const rows = await table.$(">>>tbody").$$(">>>tr"); expect(rows.length).toBe(content.length); }); it(`the third item ought to have the name ${item3.name}`, async () => { - const rows = await table.$("tbody").$$("tr"); - const cells = await rows[2].$$("td"); + const rows = await table.$(">>>tbody").$$(">>>tr"); + const cells = await rows[2].$$(">>>td"); const cell1Text = await cells[1].getText(); expect(cell1Text).toEqual(item3.name); }); it("Selecting one item ought to result in the value of the table being set", async () => { - const rows = await table.$("tbody").$$("tr"); - const control = await rows[2].$$("td")[0].$("input"); + const rows = await table.$(">>>tbody").$$(">>>tr"); + const control = await rows[2].$$(">>>td")[0].$(">>>input"); await control.click(); expect(await selecttable.getValue()).toEqual(slug(item3.name)); }); @@ -88,26 +88,27 @@ describe("Multiselect Table", () => { }); it("it should render the select-all control", async () => { - const selall = await table.$("thead").$$("tr")[0].$$("td")[0]; + const thead = await table.$(">>>thead"); + const selall = await thead.$$(">>>tr")[0].$$(">>>td")[0]; if (selall === undefined) { throw new Error("Could not find table header"); } - const input = await selall.$("input"); + const input = await selall.$(">>>input"); expect(await input.getProperty("name")).toEqual("select-all-input"); }); it("it should set the value when one input is clicked", async () => { - const input = await table.$("tbody").$$("tr")[2].$$("td")[0].$("input"); + const input = await table.$(">>>tbody").$$(">>>tr")[2].$$(">>>td")[0].$(">>>input"); await input.click(); expect(await selecttable.getValue()).toEqual(slug(nutritionDbUSDA[2].name)); }); it("it should select all when that control is clicked", async () => { - const selall = await table.$("thead").$$("tr")[0].$$("td")[0]; + const selall = await table.$(">>>thead").$$(">>>tr")[0].$$(">>>td")[0]; if (selall === undefined) { throw new Error("Could not find table header"); } - const input = await selall.$("input"); + const input = await selall.$(">>>input"); await input.click(); const value = await selecttable.getValue(); const values = value.split(";").toSorted(alphaSort).join(";"); @@ -116,11 +117,11 @@ describe("Multiselect Table", () => { }); it("it should clear all when that control is clicked twice", async () => { - const selall = await table.$("thead").$$("tr")[0].$$("td")[0]; + const selall = await table.$(">>>thead").$$(">>>tr")[0].$$(">>>td")[0]; if (selall === undefined) { throw new Error("Could not find table header"); } - const input = await selall.$("input"); + const input = await selall.$(">>>input"); await input.click(); const value = await selecttable.getValue(); const values = value.split(";").toSorted(alphaSort).join(";"); diff --git a/web/src/elements/ak-table/tests/ak-simple-table.test.ts b/web/src/elements/ak-table/tests/ak-simple-table.test.ts index 0734dc5177..621e93f7f4 100644 --- a/web/src/elements/ak-table/tests/ak-simple-table.test.ts +++ b/web/src/elements/ak-table/tests/ak-simple-table.test.ts @@ -30,7 +30,8 @@ describe("Simple Table", () => { }); it("the table should have as many entries as the data source", async () => { - const rows = await table.$("tbody").$$("tr"); + const tbody = await table.$(">>>tbody"); + const rows = await tbody.$$(">>>tr"); expect(rows.length).toBe(content.length); }); diff --git a/web/src/elements/tests/Divider.test.ts b/web/src/elements/tests/Divider.test.ts index e431672537..30aa6b14b9 100644 --- a/web/src/elements/tests/Divider.test.ts +++ b/web/src/elements/tests/Divider.test.ts @@ -15,7 +15,7 @@ describe("ak-divider", () => { it("should render the divider with the specified text", async () => { render(html`Your Message Here`); - const span = await $("ak-divider").$("span"); + const span = await $("ak-divider").$(">>>span"); await expect(span).toExist(); await expect(span).toHaveText("Your Message Here"); }); diff --git a/web/tests/pageobjects/admin.page.ts b/web/tests/pageobjects/admin.page.ts index dd5d232eb0..9cb2676ce9 100644 --- a/web/tests/pageobjects/admin.page.ts +++ b/web/tests/pageobjects/admin.page.ts @@ -4,7 +4,7 @@ import Page from "../pageobjects/page.js"; export default class AdminPage extends Page { public async pageHeader() { - return await $("ak-page-header").$('slot[name="header"]'); + return await $(">>>ak-page-header").$('>>>slot[name="header"]'); } async openApplicationsListPage() { diff --git a/web/tests/pageobjects/application-wizard.page.ts b/web/tests/pageobjects/application-wizard.page.ts index 45f82b9101..948dc4f2f7 100644 --- a/web/tests/pageobjects/application-wizard.page.ts +++ b/web/tests/pageobjects/application-wizard.page.ts @@ -29,15 +29,15 @@ class ApplicationWizardView extends AdminPage { app = ApplicationForm; async wizardTitle() { - return await $("ak-wizard-frame").$(".pf-c-wizard__title"); + return await $(">>>ak-wizard-frame").$(">>>.pf-c-wizard__title"); } async providerList() { - return await $("ak-application-wizard-authentication-method-choice"); + return await $(">>>ak-application-wizard-authentication-method-choice"); } async nextButton() { - return await $("ak-wizard-frame").$("footer button.pf-m-primary"); + return await $(">>>ak-wizard-frame").$(">>>footer button.pf-m-primary"); } async getProviderType(type: string) { @@ -46,7 +46,7 @@ class ApplicationWizardView extends AdminPage { } async successMessage() { - return await $('[data-commit-state="success"]'); + return await $('>>>[data-commit-state="success"]'); } } @@ -70,7 +70,7 @@ providerValues.forEach(([value, name]: Pair) => { get: async function () { return await ( await this.providerList() - ).$(`div[data-ouid-component-name="${value}"]`); + ).$(`>>>div[data-ouid-component-name="${value}"]`); }, }, }); diff --git a/web/tests/pageobjects/applications-list.page.ts b/web/tests/pageobjects/applications-list.page.ts index 7e8234582a..9f718570bf 100644 --- a/web/tests/pageobjects/applications-list.page.ts +++ b/web/tests/pageobjects/applications-list.page.ts @@ -11,7 +11,7 @@ class ApplicationsListPage extends AdminPage { */ async startWizardButton() { - return await $("ak-application-wizard").$('button[slot="trigger"]'); + return await $(">>>ak-application-wizard").$('>>>button[slot="trigger"]'); } async open() { diff --git a/web/tests/pageobjects/forms/application.form.ts b/web/tests/pageobjects/forms/application.form.ts index b2824d3c51..63491f5ed4 100644 --- a/web/tests/pageobjects/forms/application.form.ts +++ b/web/tests/pageobjects/forms/application.form.ts @@ -4,15 +4,15 @@ import Page from "../page.js"; export class ApplicationForm extends Page { async name() { - return await $('ak-text-input[name="name"]').$("input"); + return await $('>>>ak-text-input[name="name"]').$(">>>input"); } async uiSettings() { - return await $("ak-form-group").$('button[aria-label="UI Settings"]'); + return await $(">>>ak-form-group").$('button[aria-label="UI Settings"]'); } async launchUrl() { - return await $('input[name="metaLaunchUrl"]'); + return await $('>>>input[name="metaLaunchUrl"]'); } } diff --git a/web/tests/pageobjects/forms/forward-proxy.form.ts b/web/tests/pageobjects/forms/forward-proxy.form.ts index 209e73a484..e83a96a7a1 100644 --- a/web/tests/pageobjects/forms/forward-proxy.form.ts +++ b/web/tests/pageobjects/forms/forward-proxy.form.ts @@ -5,14 +5,14 @@ import Page from "../page.js"; export class ForwardProxyForm extends Page { async setAuthorizationFlow(selector: string) { await this.searchSelect( - 'ak-flow-search[name="authorizationFlow"]', + '>>>ak-flow-search[name="authorizationFlow"]', "authorizationFlow", selector, ); } get externalHost() { - return $('input[name="externalHost"]'); + return $('>>>input[name="externalHost"]'); } } diff --git a/web/tests/pageobjects/forms/ldap.form.ts b/web/tests/pageobjects/forms/ldap.form.ts index 37d0dc893b..70a0375bb8 100644 --- a/web/tests/pageobjects/forms/ldap.form.ts +++ b/web/tests/pageobjects/forms/ldap.form.ts @@ -3,7 +3,7 @@ import Page from "../page.js"; export class LdapForm extends Page { async setBindFlow() { await this.searchSelect( - 'ak-search-select-view[name="authorizationFlow"]', + '>>>ak-search-select-view[name="authorizationFlow"]', "authorizationFlow", "default-authentication-flow", ); diff --git a/web/tests/pageobjects/forms/oauth.form.ts b/web/tests/pageobjects/forms/oauth.form.ts index 025437cf46..4b00f0485d 100644 --- a/web/tests/pageobjects/forms/oauth.form.ts +++ b/web/tests/pageobjects/forms/oauth.form.ts @@ -5,14 +5,14 @@ import Page from "../page.js"; export class OauthForm extends Page { async setAuthorizationFlow(selector: string) { await this.searchSelect( - 'ak-flow-search[name="authorizationFlow"]', + '>>>ak-flow-search[name="authorizationFlow"]', "authorizationFlow", `${selector}`, ); } async providerName() { - return await $('ak-form-element-horizontal[name="name"]').$("input"); + return await $('>>>ak-form-element-horizontal[name="name"]').$("input"); } } diff --git a/web/tests/pageobjects/forms/radius.form.ts b/web/tests/pageobjects/forms/radius.form.ts index ddb8cd684b..f9f7e15724 100644 --- a/web/tests/pageobjects/forms/radius.form.ts +++ b/web/tests/pageobjects/forms/radius.form.ts @@ -3,7 +3,7 @@ import Page from "../page.js"; export class RadiusForm extends Page { async setAuthenticationFlow(selector: string) { await this.searchSelect( - 'ak-branded-flow-search[name="authorizationFlow"]', + '>>>ak-branded-flow-search[name="authorizationFlow"]', "authorizationFlow", selector, ); diff --git a/web/tests/pageobjects/forms/saml.form.ts b/web/tests/pageobjects/forms/saml.form.ts index 81339ef108..2525a0de3e 100644 --- a/web/tests/pageobjects/forms/saml.form.ts +++ b/web/tests/pageobjects/forms/saml.form.ts @@ -5,14 +5,14 @@ import Page from "../page.js"; export class SamlForm extends Page { async setAuthorizationFlow(selector: string) { await this.searchSelect( - 'ak-flow-search[name="authorizationFlow"]', + '>>>ak-flow-search[name="authorizationFlow"]', "authorizationFlow", selector, ); } get acsUrl() { - return $('input[name="acsUrl"]'); + return $('>>>input[name="acsUrl"]'); } } diff --git a/web/tests/pageobjects/forms/scim.form.ts b/web/tests/pageobjects/forms/scim.form.ts index 59ccbc0dfa..41a11356cd 100644 --- a/web/tests/pageobjects/forms/scim.form.ts +++ b/web/tests/pageobjects/forms/scim.form.ts @@ -2,11 +2,11 @@ import Page from "../page.js"; export class ScimForm extends Page { get url() { - return $('input[name="url"]'); + return $('>>>input[name="url"]'); } get token() { - return $('input[name="token"]'); + return $('>>>input[name="token"]'); } } diff --git a/web/tests/pageobjects/forms/transparent-proxy.form.ts b/web/tests/pageobjects/forms/transparent-proxy.form.ts index 0dfa139a25..f7576d1c7f 100644 --- a/web/tests/pageobjects/forms/transparent-proxy.form.ts +++ b/web/tests/pageobjects/forms/transparent-proxy.form.ts @@ -5,18 +5,18 @@ import Page from "../page.js"; export class TransparentProxyForm extends Page { async setAuthorizationFlow(selector: string) { await this.searchSelect( - 'ak-flow-search[name="authorizationFlow"]', + '>>>ak-flow-search[name="authorizationFlow"]', "authorizationFlow", selector, ); } get externalHost() { - return $('input[name="externalHost"]'); + return $('>>>input[name="externalHost"]'); } get internalHost() { - return $('input[name="internalHost"]'); + return $('>>>input[name="internalHost"]'); } } diff --git a/web/tests/pageobjects/login.page.ts b/web/tests/pageobjects/login.page.ts index e96e69c591..62f3a177fe 100644 --- a/web/tests/pageobjects/login.page.ts +++ b/web/tests/pageobjects/login.page.ts @@ -11,23 +11,23 @@ class LoginPage extends Page { * Selectors */ async inputUsername() { - return await $('input[name="uidField"]'); + return await $('>>>input[name="uidField"]'); } async usernameBtnSubmit() { - return await $('button[type="submit"]'); + return await $('>>>button[type="submit"]'); } async inputPassword() { - return await $("input#ak-stage-password-input"); + return await $(">>>input#ak-stage-password-input"); } async passwordBtnSubmit() { - return await $("ak-stage-password").$('button[type="submit"]'); + return await $(">>>ak-stage-password").$('>>>button[type="submit"]'); } async authFailure() { - return await $(".pf-m-error"); + return await $(">>>.pf-m-error"); } /** @@ -53,7 +53,7 @@ class LoginPage extends Page { await this.pause(); await this.password(password); await this.pause(); - await this.pause("div.header h1"); + await this.pause(">>>div.header h1"); return UserLibraryPage; } diff --git a/web/tests/pageobjects/page.ts b/web/tests/pageobjects/page.ts index a5b5f15a02..533cefbb54 100644 --- a/web/tests/pageobjects/page.ts +++ b/web/tests/pageobjects/page.ts @@ -34,11 +34,11 @@ export default class Page { async searchSelect(searchSelector: string, managedSelector: string, buttonSelector: string) { const inputBind = await $(searchSelector); - const inputMain = await inputBind.$('input[type="text"]'); + const inputMain = await inputBind.$('>>>input[type="text"]'); await inputMain.click(); const searchBlock = await ( - await $(`div[data-managed-for="${managedSelector}"]`).$("ak-list-select") - ).shadow$$("button"); + await $(`>>>div[data-managed-for="${managedSelector}"]`).$(">>>ak-list-select") + ).$$(">>>button"); let target: WebdriverIO.Element; // @ts-expect-error "Types break on shadow$$" for (const button of searchBlock) { diff --git a/web/tests/pageobjects/user-library.page.ts b/web/tests/pageobjects/user-library.page.ts index dbd8f6f81d..3797f52fdb 100644 --- a/web/tests/pageobjects/user-library.page.ts +++ b/web/tests/pageobjects/user-library.page.ts @@ -11,11 +11,11 @@ class UserLibraryPage extends Page { */ public async pageHeader() { - return await $('h1[aria-level="1"]'); + return await $('>>>h1[aria-level="1"]'); } public async goToAdmin() { - await $('a[href="/if/admin"]').click(); + await $('>>>a[href="/if/admin"]').click(); return await $("ak-admin-overview").waitForDisplayed(); } } diff --git a/web/tests/specs/new-application-by-wizard.ts b/web/tests/specs/new-application-by-wizard.ts index a43d84b9d7..c56b856c7a 100644 --- a/web/tests/specs/new-application-by-wizard.ts +++ b/web/tests/specs/new-application-by-wizard.ts @@ -16,7 +16,7 @@ async function reachTheProvider(title: string) { await ApplicationsListPage.logout(); await login(); await ApplicationsListPage.open(); - await ApplicationsListPage.pause("ak-page-header"); + await ApplicationsListPage.pause(); await expect(await ApplicationsListPage.pageHeader()).toBeDisplayed(); await expect(await ApplicationsListPage.pageHeader()).toHaveText("Applications"); diff --git a/web/tests/specs/oauth-provider.ts b/web/tests/specs/oauth-provider.ts index 69557e9891..1f02d759e6 100644 --- a/web/tests/specs/oauth-provider.ts +++ b/web/tests/specs/oauth-provider.ts @@ -22,13 +22,13 @@ describe("Configure Oauth2 Providers", () => { await reachTheProvider(); - await $("ak-wizard-page-type-create").waitForDisplayed(); - await $('div[data-ouid-component-name="oauth2provider"]').scrollIntoView(); - await $('div[data-ouid-component-name="oauth2provider"]').click(); + await $(">>>ak-wizard-page-type-create").waitForDisplayed(); + await $('>>>div[data-ouid-component-name="oauth2provider"]').scrollIntoView(); + await $('>>>div[data-ouid-component-name="oauth2provider"]').click(); await ProviderWizardView.nextButton.click(); await ProviderWizardView.pause(); - return await $('ak-form-element-horizontal[name="name"]').$("input"); + return await $('>>>ak-form-element-horizontal[name="name"]').$(">>>input"); await ProviderWizardView.oauth.setAuthorizationFlow( "default-provider-authorization-explicit-consent", );